Skip to content

Commit e198d13

Browse files
authored
Merge branch 'main' into fix/drawingml-image-hyperlinks
2 parents 7bea148 + 69480c6 commit e198d13

226 files changed

Lines changed: 16167 additions & 978 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-demos.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ on:
1010
- 'packages/layout-engine/**'
1111
- 'shared/**'
1212
- 'package.json'
13-
- 'pnpm-lock.yaml'
14-
- 'pnpm-workspace.yaml'
1513
workflow_dispatch:
1614

1715
jobs:

.github/workflows/ci-docs.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ on:
1010
- 'packages/document-api/scripts/**'
1111
- 'scripts/generate-all.mjs'
1212
- 'package.json'
13-
- 'pnpm-lock.yaml'
1413
workflow_dispatch:
1514

1615
jobs:

.github/workflows/ci-sdk.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ on:
1212
- 'packages/sdk/**'
1313
- 'scripts/generate-all.mjs'
1414
- 'package.json'
15-
- 'pnpm-lock.yaml'
1615
- '.github/workflows/ci-sdk.yml'
1716
workflow_dispatch:
1817

.github/workflows/ci-superdoc.yml

Lines changed: 75 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,16 @@ on:
77
pull_request:
88
branches: [main, 'release/**']
99
paths-ignore:
10-
- '.github/workflows/**'
1110
- 'apps/docs/**'
11+
- 'apps/mcp/**'
12+
- 'apps/vscode-ext/**'
1213
- 'demos/**'
1314
- 'examples/**'
15+
- 'packages/react/**'
16+
- 'packages/sdk/**'
1417
- 'packages/template-builder/**'
1518
- 'packages/esign/**'
19+
- 'evals/**'
1620
- '**/*.md'
1721
workflow_dispatch:
1822

@@ -21,7 +25,7 @@ concurrency:
2125
cancel-in-progress: true
2226

2327
jobs:
24-
validate:
28+
build:
2529
runs-on: ubuntu-latest
2630
steps:
2731
- uses: actions/checkout@v6
@@ -67,24 +71,85 @@ jobs:
6771
- name: Typecheck
6872
run: pnpm run type-check
6973

70-
- name: Run unit tests
71-
run: pnpm test
74+
unit-tests:
75+
needs: build
76+
runs-on: ubuntu-latest
77+
strategy:
78+
fail-fast: false
79+
matrix:
80+
shard: [1, 2, 3, 4, 5]
81+
steps:
82+
- uses: actions/checkout@v6
83+
84+
- uses: pnpm/action-setup@v4
85+
86+
- uses: actions/setup-node@v6
87+
with:
88+
node-version-file: .nvmrc
89+
cache: pnpm
90+
91+
- uses: oven-sh/setup-bun@v2
92+
with:
93+
bun-version: 1.3.10
94+
95+
- name: Install canvas system dependencies
96+
run: |
97+
sudo apt-get update
98+
sudo apt-get install -y \
99+
build-essential \
100+
libcairo2-dev \
101+
libpango1.0-dev \
102+
libjpeg-dev \
103+
libgif-dev \
104+
librsvg2-dev \
105+
libpixman-1-dev
106+
107+
- name: Install dependencies
108+
run: pnpm install
109+
110+
- name: Build
111+
run: pnpm run build
112+
113+
- name: Run vitest (shard ${{ matrix.shard }}/5)
114+
env:
115+
NODE_OPTIONS: '--max-old-space-size=4096'
116+
run: pnpm exec vitest run --shard=${{ matrix.shard }}/5 --exclude='**/decrypt-docx.integration*'
117+
118+
- name: Run bun tests
119+
if: matrix.shard == 1
120+
run: |
121+
pnpm -r --parallel \
122+
--filter @superdoc/document-api \
123+
--filter @superdoc/layout-engine --filter @superdoc/style-engine \
124+
--filter @superdoc/geometry-utils --filter @superdoc/word-layout \
125+
--filter @superdoc/common --filter @font-utils \
126+
--filter @locale-utils --filter @url-validation \
127+
test
128+
129+
- name: Run SDK scripts tests
130+
if: matrix.shard == 1
131+
run: pnpm --prefix packages/sdk run test:scripts
72132

73133
- name: Run memory profiling tests (non-blocking)
134+
if: matrix.shard == 1
74135
continue-on-error: true
75136
run: pnpm --filter @superdoc/layout-tests run test:memory
76137

77138
- name: Run slow tests
139+
if: matrix.shard == 1
78140
run: pnpm test:slow
79141

80142
- name: Install Playwright for UMD smoke test
143+
if: matrix.shard == 1
81144
run: pnpm --filter @superdoc/umd-smoke-test exec playwright install --with-deps chromium
82145

83146
- name: Run UMD smoke test
147+
if: matrix.shard == 1
84148
working-directory: packages/superdoc/tests/umd-smoke
85149
run: pnpm test
86150

87151
cli-tests:
152+
needs: build
88153
runs-on: ubuntu-latest
89154
steps:
90155
- uses: actions/checkout@v6
@@ -108,3 +173,9 @@ jobs:
108173

109174
- name: Run CLI tests
110175
run: pnpm run test:cli
176+
177+
validate:
178+
needs: [build, unit-tests, cli-tests]
179+
runs-on: ubuntu-latest
180+
steps:
181+
- run: echo "All checks passed"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: CI VS Code Extension
2+
3+
permissions:
4+
contents: read
5+
6+
on:
7+
pull_request:
8+
paths:
9+
- 'apps/vscode-ext/**'
10+
- 'packages/superdoc/src/**'
11+
workflow_dispatch:
12+
13+
concurrency:
14+
group: ci-vscode-ext-${{ github.event.pull_request.number }}
15+
cancel-in-progress: true
16+
17+
jobs:
18+
validate:
19+
runs-on: ubuntu-latest
20+
steps:
21+
- uses: actions/checkout@v4
22+
23+
- uses: pnpm/action-setup@v4
24+
25+
- uses: actions/setup-node@v4
26+
with:
27+
node-version-file: .nvmrc
28+
cache: pnpm
29+
30+
- name: Install dependencies
31+
run: pnpm install
32+
33+
- name: Lint
34+
run: pnpm --filter superdoc-vscode-ext lint
35+
36+
- name: Type check
37+
run: pnpm --filter superdoc-vscode-ext typecheck
38+
39+
- name: Test
40+
run: pnpm --filter superdoc-vscode-ext test
41+
42+
- name: Build extension host
43+
run: pnpm --filter superdoc-vscode-ext compile:ext

.github/workflows/release-vscode-ext.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,14 @@ on:
77
branches: main
88
paths:
99
- 'apps/vscode-ext/**'
10+
- 'packages/superdoc/**'
11+
- 'packages/layout-engine/**'
12+
- 'packages/super-editor/**'
13+
- 'packages/ai/**'
14+
- 'packages/word-layout/**'
15+
- 'packages/preset-geometry/**'
1016
- 'pnpm-workspace.yaml'
11-
- '!apps/vscode-ext/*.md'
17+
- '!**/*.md'
1218
workflow_dispatch:
1319

1420
permissions:
@@ -45,6 +51,10 @@ jobs:
4551
- name: Build superdoc
4652
run: pnpm run build
4753

54+
- name: Test vscode-ext
55+
run: pnpm run test
56+
working-directory: apps/vscode-ext
57+
4858
- name: Build vscode-ext
4959
run: pnpm run build
5060
working-directory: apps/vscode-ext

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ Special thanks to these community members who have contributed code to SuperDoc:
156156
<a href="https://github.com/gpardhivvarma"><img src="https://github.com/gpardhivvarma.png" width="50" height="50" alt="gpardhivvarma" title="G Pardhiv Varma" /></a>
157157
<a href="https://github.com/lucbic"><img src="https://github.com/lucbic.png" width="50" height="50" alt="lucbic" title="Lucas Bicudo" /></a>
158158
<a href="https://github.com/claudiu-ior"><img src="https://github.com/claudiu-ior.png" width="50" height="50" alt="claudiu-ior" title="Claudiu Iorgulescu" /></a>
159+
<a href="https://github.com/Branc0"><img src="https://github.com/Branc0.png" width="50" height="50" alt="Branc0" title="Rafael Rocha de Azevedo" /></a>
159160

160161
Want to see your avatar here? Check the [Contributing Guide](CONTRIBUTING.md) to get started.
161162

apps/cli/scripts/export-sdk-contract.ts

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
* Check: bun run apps/cli/scripts/export-sdk-contract.ts --check
1212
*/
1313

14-
import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';
14+
import { writeFileSync, mkdirSync, readFileSync } from 'node:fs';
1515
import { resolve, dirname } from 'node:path';
16-
import { createHash } from 'node:crypto';
1716
import { tmpdir } from 'node:os';
1817

1918
import { COMMAND_CATALOG, INTENT_GROUP_META } from '@superdoc/document-api';
19+
import { buildContractSnapshot } from '@superdoc/document-api/scripts/lib/contract-snapshot.ts';
2020

2121
import { CLI_OPERATION_METADATA } from '../src/cli/operation-params';
2222
import {
@@ -27,7 +27,7 @@ import {
2727
cliRequiresDocumentContext,
2828
toDocApiId,
2929
} from '../src/cli/operation-set';
30-
import type { CliOnlyOperation } from '../src/cli/types';
30+
import type { CliOnlyOperation, CliOperationParamSpec, CliTypeSpec } from '../src/cli/types';
3131
import { CLI_ONLY_OPERATION_DEFINITIONS } from '../src/cli/cli-only-operation-definitions';
3232
import { RESPONSE_ENVELOPE_KEY } from '../src/cli/operation-hints';
3333
import { HOST_PROTOCOL_VERSION, HOST_PROTOCOL_FEATURES, HOST_PROTOCOL_NOTIFICATIONS } from '../src/host/protocol';
@@ -48,29 +48,60 @@ function classifySdkSurface(operationId: string): SdkSurface {
4848
return 'document';
4949
}
5050

51+
function buildParamSchema(param: CliOperationParamSpec): Record<string, unknown> {
52+
let schema: Record<string, unknown>;
53+
54+
if (param.type === 'string' && param.schema) schema = { type: 'string', ...(param.schema as CliTypeSpec) };
55+
else if (param.type === 'string') schema = { type: 'string' };
56+
else if (param.type === 'number') schema = { type: 'number' };
57+
else if (param.type === 'boolean') schema = { type: 'boolean' };
58+
else if (param.type === 'string[]') schema = { type: 'array', items: { type: 'string' } };
59+
else if (param.type === 'json' && param.schema && (param.schema as CliTypeSpec).type !== 'json') {
60+
schema = { ...(param.schema as CliTypeSpec) };
61+
} else {
62+
schema = { type: 'object' };
63+
}
64+
65+
if (param.description) schema.description = param.description;
66+
return schema;
67+
}
68+
69+
function buildCliOnlyInputSchema(
70+
params: readonly CliOperationParamSpec[],
71+
sdkSurface: SdkSurface,
72+
): Record<string, unknown> {
73+
const properties: Record<string, Record<string, unknown>> = {};
74+
const required: string[] = [];
75+
76+
for (const param of params) {
77+
if (param.agentVisible === false) continue;
78+
if (sdkSurface === 'document' && (param.name === 'doc' || param.name === 'sessionId')) continue;
79+
80+
properties[param.name] = buildParamSchema(param);
81+
if (param.required) required.push(param.name);
82+
}
83+
84+
return {
85+
type: 'object',
86+
properties,
87+
...(required.length > 0 ? { required } : {}),
88+
additionalProperties: false,
89+
};
90+
}
91+
5192
// ---------------------------------------------------------------------------
5293
// Paths
5394
// ---------------------------------------------------------------------------
5495

5596
const ROOT = resolve(import.meta.dir, '../../..');
5697
const CLI_DIR = resolve(ROOT, 'apps/cli');
57-
const CONTRACT_JSON_PATH = resolve(ROOT, 'packages/document-api/generated/schemas/document-api-contract.json');
5898
const OUTPUT_PATH = resolve(CLI_DIR, 'generated/sdk-contract.json');
5999
const CLI_PKG_PATH = resolve(CLI_DIR, 'package.json');
60100

61101
// ---------------------------------------------------------------------------
62102
// Load inputs
63103
// ---------------------------------------------------------------------------
64104

65-
function loadDocApiContract(): {
66-
contractVersion: string;
67-
$defs?: Record<string, unknown>;
68-
operations: Record<string, Record<string, unknown>>;
69-
} {
70-
const raw = readFileSync(CONTRACT_JSON_PATH, 'utf-8');
71-
return JSON.parse(raw);
72-
}
73-
74105
function loadCliPackage(): { name: string; version: string } {
75106
const raw = readFileSync(CLI_PKG_PATH, 'utf-8');
76107
return JSON.parse(raw);
@@ -81,10 +112,14 @@ function loadCliPackage(): { name: string; version: string } {
81112
// ---------------------------------------------------------------------------
82113

83114
function buildSdkContract() {
84-
const docApiContract = loadDocApiContract();
115+
// Read the live document-api source snapshot instead of the generated JSON
116+
// artifact. This keeps SDK export resilient when developers add operations
117+
// before refreshing packages/document-api/generated/.
118+
const docApiContract = buildContractSnapshot();
85119
const cliPkg = loadCliPackage();
86-
87-
const sourceHash = createHash('sha256').update(JSON.stringify(docApiContract)).digest('hex').slice(0, 16);
120+
const docApiOperations = Object.fromEntries(
121+
docApiContract.operations.map((operation) => [operation.operationId, operation]),
122+
);
88123

89124
const operations: Record<string, unknown> = {};
90125

@@ -94,11 +129,12 @@ function buildSdkContract() {
94129
const stripped = cliOpId.slice(4) as CliOnlyOperation;
95130

96131
const cliOnlyDef = docApiId ? null : CLI_ONLY_OPERATION_DEFINITIONS[stripped];
132+
const sdkSurface = classifySdkSurface(cliOpId);
97133

98134
// Base fields shared by all operations
99135
const entry: Record<string, unknown> = {
100136
operationId: cliOpId,
101-
sdkSurface: classifySdkSurface(cliOpId),
137+
sdkSurface,
102138
command: metadata.command,
103139
commandTokens: [...cliCommandTokens(cliOpId)],
104140
category: cliCategory(cliOpId),
@@ -135,21 +171,22 @@ function buildSdkContract() {
135171
entry.supportsTrackedMode = catalog.supportsTrackedMode;
136172
entry.supportsDryRun = catalog.supportsDryRun;
137173

138-
// Schema plane from document-api-contract.json
139-
const docOp = docApiContract.operations[docApiId];
174+
// Schema plane from the source snapshot.
175+
const docOp = docApiOperations[docApiId];
140176
if (!docOp) {
141-
throw new Error(`Missing document-api contract entry for ${docApiId}`);
177+
throw new Error(`CLI operation ${cliOpId} maps to missing document-api source entry ${docApiId}.`);
142178
}
143-
entry.inputSchema = docOp.inputSchema;
144-
entry.outputSchema = docOp.outputSchema;
145-
if (docOp.successSchema) entry.successSchema = docOp.successSchema;
146-
if (docOp.failureSchema) entry.failureSchema = docOp.failureSchema;
179+
entry.inputSchema = docOp.schemas.input;
180+
entry.outputSchema = docOp.schemas.output;
181+
if (docOp.schemas.success) entry.successSchema = docOp.schemas.success;
182+
if (docOp.schemas.failure) entry.failureSchema = docOp.schemas.failure;
147183
if (docOp.skipAsATool) entry.skipAsATool = true;
148184
if (docOp.intentGroup) entry.intentGroup = docOp.intentGroup;
149185
if (docOp.intentAction) entry.intentAction = docOp.intentAction;
150186
} else {
151187
// CLI-only operation — metadata from canonical definitions
152188
const def = cliOnlyDef!;
189+
entry.inputSchema = buildCliOnlyInputSchema(metadata.params, sdkSurface);
153190
entry.mutates = def.sdkMetadata.mutates;
154191
entry.idempotency = def.sdkMetadata.idempotency;
155192
entry.supportsTrackedMode = def.sdkMetadata.supportsTrackedMode;
@@ -168,7 +205,7 @@ function buildSdkContract() {
168205

169206
return {
170207
contractVersion: docApiContract.contractVersion,
171-
sourceHash,
208+
sourceHash: docApiContract.sourceHash,
172209
...(docApiContract.$defs ? { $defs: docApiContract.$defs } : {}),
173210
cli: {
174211
package: cliPkg.name,

0 commit comments

Comments
 (0)