@superdoc-dev/sdk 1.19.1 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/action-primitives/doc-index.cjs +215 -0
  2. package/dist/action-primitives/doc-index.d.ts +83 -0
  3. package/dist/action-primitives/doc-index.d.ts.map +1 -0
  4. package/dist/action-primitives/doc-index.js +211 -0
  5. package/dist/action-primitives/engine.cjs +204 -0
  6. package/dist/action-primitives/engine.d.ts +71 -0
  7. package/dist/action-primitives/engine.d.ts.map +1 -0
  8. package/dist/action-primitives/engine.js +196 -0
  9. package/dist/action-primitives/receipt.cjs +39 -0
  10. package/dist/action-primitives/receipt.d.ts +49 -0
  11. package/dist/action-primitives/receipt.d.ts.map +1 -0
  12. package/dist/action-primitives/receipt.js +32 -0
  13. package/dist/action-primitives/resolve.cjs +252 -0
  14. package/dist/action-primitives/resolve.d.ts +56 -0
  15. package/dist/action-primitives/resolve.d.ts.map +1 -0
  16. package/dist/action-primitives/resolve.js +246 -0
  17. package/dist/action-primitives/session-cache.cjs +43 -0
  18. package/dist/action-primitives/session-cache.d.ts +19 -0
  19. package/dist/action-primitives/session-cache.d.ts.map +1 -0
  20. package/dist/action-primitives/session-cache.js +37 -0
  21. package/dist/action-primitives/tools/list-transform.cjs +661 -0
  22. package/dist/action-primitives/tools/list-transform.d.ts +98 -0
  23. package/dist/action-primitives/tools/list-transform.d.ts.map +1 -0
  24. package/dist/action-primitives/tools/list-transform.js +656 -0
  25. package/dist/action-primitives/tools/structure-insert.cjs +1343 -0
  26. package/dist/action-primitives/tools/structure-insert.d.ts +183 -0
  27. package/dist/action-primitives/tools/structure-insert.d.ts.map +1 -0
  28. package/dist/action-primitives/tools/structure-insert.js +1338 -0
  29. package/dist/action-primitives/tools/text-transform.cjs +669 -0
  30. package/dist/action-primitives/tools/text-transform.d.ts +64 -0
  31. package/dist/action-primitives/tools/text-transform.d.ts.map +1 -0
  32. package/dist/action-primitives/tools/text-transform.js +664 -0
  33. package/dist/action-primitives/types.d.ts +36 -0
  34. package/dist/action-primitives/types.d.ts.map +1 -0
  35. package/dist/action-primitives/types.js +15 -0
  36. package/dist/agent/actions.cjs +5381 -0
  37. package/dist/agent/actions.d.ts +404 -0
  38. package/dist/agent/actions.d.ts.map +1 -0
  39. package/dist/agent/actions.js +5373 -0
  40. package/dist/agent/catalog.cjs +483 -0
  41. package/dist/agent/catalog.d.ts +103 -0
  42. package/dist/agent/catalog.d.ts.map +1 -0
  43. package/dist/agent/catalog.js +471 -0
  44. package/dist/agent/doc-snapshot.cjs +663 -0
  45. package/dist/agent/doc-snapshot.d.ts +247 -0
  46. package/dist/agent/doc-snapshot.d.ts.map +1 -0
  47. package/dist/agent/doc-snapshot.js +657 -0
  48. package/dist/agent/index.d.ts +16 -0
  49. package/dist/agent/index.d.ts.map +1 -0
  50. package/dist/agent/index.js +15 -0
  51. package/dist/agent/ir.cjs +170 -0
  52. package/dist/agent/ir.d.ts +216 -0
  53. package/dist/agent/ir.d.ts.map +1 -0
  54. package/dist/agent/ir.js +181 -0
  55. package/dist/agent/operation-catalog.cjs +418 -0
  56. package/dist/agent/operation-catalog.d.ts +36 -0
  57. package/dist/agent/operation-catalog.d.ts.map +1 -0
  58. package/dist/agent/operation-catalog.js +446 -0
  59. package/dist/agent/runtime.cjs +501 -0
  60. package/dist/agent/runtime.d.ts +120 -0
  61. package/dist/agent/runtime.d.ts.map +1 -0
  62. package/dist/agent/runtime.js +493 -0
  63. package/dist/embedded-prompts.generated.cjs +13 -0
  64. package/dist/embedded-prompts.generated.d.ts +4 -0
  65. package/dist/embedded-prompts.generated.d.ts.map +1 -0
  66. package/dist/embedded-prompts.generated.js +9 -0
  67. package/dist/generated/client.cjs +18 -20
  68. package/dist/generated/client.d.ts +723 -3223
  69. package/dist/generated/client.d.ts.map +1 -1
  70. package/dist/generated/client.js +18 -20
  71. package/dist/generated/contract.cjs +12898 -18467
  72. package/dist/generated/contract.d.ts.map +1 -1
  73. package/dist/generated/contract.js +12898 -18467
  74. package/dist/generated/intent-dispatch.generated.cjs +0 -1
  75. package/dist/generated/intent-dispatch.generated.d.ts.map +1 -1
  76. package/dist/generated/intent-dispatch.generated.js +0 -1
  77. package/dist/index.cjs +3 -7
  78. package/dist/index.d.ts +8 -6
  79. package/dist/index.d.ts.map +1 -1
  80. package/dist/index.js +2 -8
  81. package/dist/presets/core.cjs +454 -0
  82. package/dist/presets/core.d.ts +20 -0
  83. package/dist/presets/core.d.ts.map +1 -0
  84. package/dist/presets/core.js +447 -0
  85. package/dist/presets.cjs +55 -8
  86. package/dist/presets.d.ts +39 -8
  87. package/dist/presets.d.ts.map +1 -1
  88. package/dist/presets.js +53 -8
  89. package/dist/prompts/mcp-prompt.md +23 -0
  90. package/dist/prompts/system-prompt.md +108 -0
  91. package/dist/runtime/transport-common.cjs +8 -0
  92. package/dist/runtime/transport-common.d.ts.map +1 -1
  93. package/dist/runtime/transport-common.js +8 -0
  94. package/dist/tools.cjs +46 -11
  95. package/dist/tools.d.ts +55 -8
  96. package/dist/tools.d.ts.map +1 -1
  97. package/dist/tools.js +45 -13
  98. package/package.json +9 -8
  99. package/tools/__pycache__/__init__.cpython-312.pyc +0 -0
  100. package/tools/__pycache__/intent_dispatch_generated.cpython-312.pyc +0 -0
  101. package/tools/catalog.json +22 -178
  102. package/tools/intent_dispatch_generated.py +0 -2
  103. package/tools/tools-policy.json +1 -1
  104. package/tools/tools.anthropic.json +22 -158
  105. package/tools/tools.generic.json +23 -160
  106. package/tools/tools.openai.json +22 -158
  107. package/tools/tools.vercel.json +22 -158
@@ -0,0 +1,663 @@
1
+ 'use strict';
2
+
3
+ function asString(v, fallback = '') {
4
+ return typeof v === 'string' ? v : fallback;
5
+ }
6
+ function asNumber(v, fallback = 0) {
7
+ return typeof v === 'number' && Number.isFinite(v) ? v : fallback;
8
+ }
9
+ function asRecord(v) {
10
+ return typeof v === 'object' && v !== null && !Array.isArray(v) ? v : null;
11
+ }
12
+ async function safeCall(thunk, fallback, onError) {
13
+ try {
14
+ return await thunk();
15
+ }
16
+ catch (err) {
17
+ onError(err);
18
+ return fallback;
19
+ }
20
+ }
21
+ function maybeMethod(api, path) {
22
+ let cursor = api;
23
+ for (const token of path) {
24
+ // Function-shaped intermediates are legal: RPC/proxy document handles
25
+ // (e.g. the browser doc-bridge) expose namespaces as callables.
26
+ if (!cursor || (typeof cursor !== 'object' && typeof cursor !== 'function'))
27
+ return null;
28
+ cursor = cursor[token];
29
+ }
30
+ return typeof cursor === 'function' ? cursor : null;
31
+ }
32
+ function explicitCount(counts, ...keys) {
33
+ for (const key of keys) {
34
+ const value = counts[key];
35
+ if (typeof value === 'number' && Number.isFinite(value))
36
+ return value;
37
+ }
38
+ return null;
39
+ }
40
+ function includesDomain(requested, domain) {
41
+ return requested == null || requested.has(domain);
42
+ }
43
+ /**
44
+ * Build a deterministic snapshot of a document. The snapshot uses only
45
+ * read-mode operations from the generated contract — it never mutates state,
46
+ * and any individual section that fails is captured as a diagnostic so the
47
+ * rest of the snapshot remains usable.
48
+ */
49
+ async function buildDocumentSnapshot(doc, options = {}) {
50
+ const diagnostics = [];
51
+ const recordError = (section) => (err) => {
52
+ diagnostics.push({ section, message: err instanceof Error ? err.message : String(err) });
53
+ };
54
+ const blockOffset = Math.max(0, options.blockOffset ?? 0);
55
+ const blockLimit = options.blockLimit ?? options.blockTextLimit ?? 1000;
56
+ const listLimit = options.listLimit ?? 1000;
57
+ const tableLimit = options.tableLimit ?? 500;
58
+ const commentLimit = options.commentLimit ?? 500;
59
+ const trackedChangeLimit = options.trackedChangeLimit ?? 500;
60
+ const requestedDomains = Array.isArray(options.includeDomains) && options.includeDomains.length > 0 ? new Set(options.includeDomains) : null;
61
+ const blockNodeTypes = new Set(Array.isArray(options.blockNodeTypes)
62
+ ? options.blockNodeTypes.filter((value) => typeof value === 'string' && value.length > 0)
63
+ : []);
64
+ const infoFn = maybeMethod(doc, ['info']);
65
+ const info = infoFn ? await safeCall(() => infoFn({}), null, recordError('info')) : null;
66
+ const infoRec = asRecord(info);
67
+ const counts = asRecord(infoRec?.counts) ?? {};
68
+ const revision = asString(infoRec?.revision, 'unknown');
69
+ const countsFromInfo = {
70
+ blocks: explicitCount(counts, 'blocks') ?? 0,
71
+ paragraphs: explicitCount(counts, 'paragraphs') ?? 0,
72
+ headings: explicitCount(counts, 'headings') ?? 0,
73
+ tables: explicitCount(counts, 'tables') ?? 0,
74
+ lists: explicitCount(counts, 'lists') ?? 0,
75
+ images: explicitCount(counts, 'images') ?? 0,
76
+ comments: explicitCount(counts, 'comments') ?? 0,
77
+ trackedChanges: explicitCount(counts, 'trackedChanges') ?? 0,
78
+ sections: explicitCount(counts, 'sections') ?? 0,
79
+ fields: explicitCount(counts, 'fields') ?? 0,
80
+ hyperlinks: explicitCount(counts, 'hyperlinks') ?? 0,
81
+ bookmarks: explicitCount(counts, 'bookmarks') ?? 0,
82
+ contentControls: explicitCount(counts, 'contentControls', 'sdtFields') ?? 0,
83
+ permissionRanges: explicitCount(counts, 'permissionRanges') ?? 0,
84
+ styles: explicitCount(counts, 'styles') ?? 0,
85
+ headers: explicitCount(counts, 'headers') ?? 0,
86
+ footers: explicitCount(counts, 'footers') ?? 0,
87
+ };
88
+ if (options.countsOnly === true) {
89
+ return {
90
+ revision,
91
+ counts: countsFromInfo,
92
+ blocks: [],
93
+ lists: [],
94
+ tables: [],
95
+ comments: [],
96
+ trackedChanges: [],
97
+ sections: [],
98
+ headerFooters: [],
99
+ styles: [],
100
+ contentControls: [],
101
+ fields: [],
102
+ hyperlinks: [],
103
+ bookmarks: [],
104
+ permissionRanges: [],
105
+ images: [],
106
+ diagnostics,
107
+ };
108
+ }
109
+ // Blocks
110
+ const includeBlocksInSnapshot = includesDomain(requestedDomains, 'blocks');
111
+ const needBlocksRead = includeBlocksInSnapshot || includesDomain(requestedDomains, 'tables');
112
+ const blocksFn = maybeMethod(doc, ['blocks', 'list']);
113
+ const blocksRaw = needBlocksRead && blocksFn
114
+ ? await safeCall(() => blocksFn({ offset: blockOffset, limit: blockLimit, includeText: includeBlocksInSnapshot }), null, recordError('blocks.list'))
115
+ : null;
116
+ const blocksRec = asRecord(blocksRaw);
117
+ const totalBlocks = explicitCount(blocksRec ?? {}, 'total') ?? countsFromInfo.blocks;
118
+ const rawBlocks = Array.isArray(blocksRec?.blocks) ? blocksRec.blocks : [];
119
+ const normalizedBlocks = rawBlocks.map((b, index) => {
120
+ const rec = asRecord(b) ?? {};
121
+ return {
122
+ // doc-api block.ordinal is 0-based; the model-facing convention is
123
+ // 1-based (matches paragraphOrdinal/tableOrdinal and ordinal selectors).
124
+ ordinal: asNumber(rec.ordinal, blockOffset + index) + 1,
125
+ nodeId: asString(rec.nodeId),
126
+ nodeType: asString(rec.nodeType, 'paragraph'),
127
+ text: asString(rec.text),
128
+ textPreview: typeof rec.textPreview === 'string' ? rec.textPreview : null,
129
+ styleId: typeof rec.styleId === 'string' ? rec.styleId : null,
130
+ headingLevel: typeof rec.headingLevel === 'number' ? rec.headingLevel : undefined,
131
+ ...(asRecord(rec.numbering)
132
+ ? {
133
+ numbering: {
134
+ marker: asString(asRecord(rec.numbering).marker) || null,
135
+ path: Array.isArray(asRecord(rec.numbering).path) ? asRecord(rec.numbering).path : null,
136
+ kind: asString(asRecord(rec.numbering).kind) || null,
137
+ },
138
+ }
139
+ : {}),
140
+ };
141
+ });
142
+ let blocks = includeBlocksInSnapshot && blockNodeTypes.size > 0
143
+ ? normalizedBlocks.filter((block) => blockNodeTypes.has(block.nodeType))
144
+ : includeBlocksInSnapshot
145
+ ? normalizedBlocks
146
+ : [];
147
+ if (includeBlocksInSnapshot && options.omitEmptyBlocks === true) {
148
+ // Drop empty paragraphs/list-items only; structural blocks (tables) keep
149
+ // their slot even with empty text.
150
+ blocks = blocks.filter((block) => block.text.trim().length > 0 || (block.nodeType !== 'paragraph' && block.nodeType !== 'listItem'));
151
+ }
152
+ if (includeBlocksInSnapshot && options.dropTextPreview === true) {
153
+ blocks = blocks.map((block) => ({ ...block, textPreview: null }));
154
+ }
155
+ // Lists
156
+ const listsFn = maybeMethod(doc, ['lists', 'list']);
157
+ const listsRaw = includesDomain(requestedDomains, 'lists') && listsFn
158
+ ? await safeCall(() => listsFn({ offset: 0, limit: listLimit }), null, recordError('lists.list'))
159
+ : null;
160
+ const listsRec = asRecord(listsRaw);
161
+ const listItemsArr = Array.isArray(listsRec?.items) ? listsRec.items : [];
162
+ const listsById = new Map();
163
+ for (const item of listItemsArr) {
164
+ const rec = asRecord(item);
165
+ if (!rec)
166
+ continue;
167
+ const listId = asString(rec.listId, 'list:unknown');
168
+ if (!listsById.has(listId)) {
169
+ listsById.set(listId, {
170
+ listId,
171
+ kind: asString(rec.kind, 'ordered'),
172
+ items: [],
173
+ });
174
+ }
175
+ const addr = asRecord(rec.address);
176
+ listsById.get(listId).items.push({
177
+ nodeId: asString(addr?.nodeId),
178
+ ordinal: asNumber(rec.ordinal),
179
+ level: asNumber(rec.level),
180
+ text: asString(rec.text),
181
+ });
182
+ }
183
+ const lists = [...listsById.values()];
184
+ // Tables: derive table block list from `blocks`, then query shape per table.
185
+ const tableBlocks = normalizedBlocks.filter((b) => b.nodeType === 'table');
186
+ const tablesFn = maybeMethod(doc, ['tables', 'get']);
187
+ const tables = [];
188
+ if (includesDomain(requestedDomains, 'tables')) {
189
+ for (let i = 0; i < Math.min(tableBlocks.length, tableLimit); i += 1) {
190
+ const block = tableBlocks[i];
191
+ if (!tablesFn) {
192
+ tables.push({ nodeId: block.nodeId, ordinal: i + 1, rows: 0, columns: 0, cells: [] });
193
+ continue;
194
+ }
195
+ const tableRaw = await safeCall(() => tablesFn({ nodeId: block.nodeId }), null, recordError(`tables.get:${block.nodeId}`));
196
+ const rec = asRecord(tableRaw);
197
+ tables.push({
198
+ nodeId: block.nodeId,
199
+ ordinal: i + 1,
200
+ rows: asNumber(rec?.rows),
201
+ columns: asNumber(rec?.columns),
202
+ cells: [],
203
+ });
204
+ }
205
+ }
206
+ const extractFn = maybeMethod(doc, ['extract']);
207
+ if (tables.length > 0 && extractFn) {
208
+ const extractRaw = await safeCall(() => extractFn({}), null, recordError('extract'));
209
+ const extractRec = asRecord(extractRaw);
210
+ const extractBlocks = Array.isArray(extractRec?.blocks) ? extractRec.blocks : [];
211
+ const cellsByTableNodeId = new Map();
212
+ for (const block of extractBlocks) {
213
+ const rec = asRecord(block);
214
+ const tableContext = asRecord(rec?.tableContext);
215
+ if (!rec || !tableContext)
216
+ continue;
217
+ const tableOrdinal = asNumber(tableContext.tableOrdinal, -1);
218
+ const rowIndex = asNumber(tableContext.rowIndex, -1);
219
+ const columnIndex = asNumber(tableContext.columnIndex, -1);
220
+ if (tableOrdinal < 0 || rowIndex < 0 || columnIndex < 0)
221
+ continue;
222
+ const table = tables[tableOrdinal];
223
+ if (!table)
224
+ continue;
225
+ const key = `${rowIndex}:${columnIndex}`;
226
+ const text = asString(rec.text);
227
+ const nodeId = asString(rec.nodeId) || undefined;
228
+ const cellMap = cellsByTableNodeId.get(table.nodeId) ??
229
+ new Map();
230
+ const existing = cellMap.get(key);
231
+ cellMap.set(key, {
232
+ rowIndex,
233
+ columnIndex,
234
+ text: existing == null || text.length === 0
235
+ ? (existing?.text ?? text)
236
+ : existing.text.length === 0
237
+ ? text
238
+ : `${existing.text}\n${text}`,
239
+ nodeId: existing?.nodeId ?? nodeId,
240
+ });
241
+ cellsByTableNodeId.set(table.nodeId, cellMap);
242
+ }
243
+ for (const table of tables) {
244
+ const cellMap = cellsByTableNodeId.get(table.nodeId);
245
+ if (!cellMap)
246
+ continue;
247
+ table.cells = [...cellMap.values()].sort((left, right) => left.rowIndex - right.rowIndex || left.columnIndex - right.columnIndex);
248
+ }
249
+ }
250
+ // Comments
251
+ const commentsFn = maybeMethod(doc, ['comments', 'list']);
252
+ const commentsRaw = includesDomain(requestedDomains, 'comments') && commentsFn
253
+ ? await safeCall(() => commentsFn({ includeResolved: true, offset: 0, limit: commentLimit }), null, recordError('comments.list'))
254
+ : null;
255
+ const commentsRec = asRecord(commentsRaw);
256
+ const commentItems = Array.isArray(commentsRec?.items) ? commentsRec.items : [];
257
+ const comments = commentItems.map((c) => {
258
+ const rec = asRecord(c) ?? {};
259
+ const target = asRecord(rec.target);
260
+ const segments = Array.isArray(target?.segments) ? target.segments : [];
261
+ return {
262
+ id: asString(rec.id),
263
+ text: asString(rec.text),
264
+ status: asString(rec.status, 'open'),
265
+ anchoredText: typeof rec.anchoredText === 'string' ? rec.anchoredText : undefined,
266
+ segments: segments.flatMap((seg) => {
267
+ const segRec = asRecord(seg);
268
+ const range = asRecord(segRec?.range);
269
+ if (!segRec)
270
+ return [];
271
+ return [
272
+ {
273
+ blockId: asString(segRec.blockId),
274
+ start: asNumber(range?.start),
275
+ end: asNumber(range?.end),
276
+ },
277
+ ];
278
+ }),
279
+ };
280
+ });
281
+ // Tracked changes
282
+ const trackedFn = maybeMethod(doc, ['trackChanges', 'list']);
283
+ const trackedRaw = includesDomain(requestedDomains, 'trackedChanges') && trackedFn
284
+ ? await safeCall(() => trackedFn({ offset: 0, limit: trackedChangeLimit }), null, recordError('trackChanges.list'))
285
+ : null;
286
+ const trackedRec = asRecord(trackedRaw);
287
+ const trackedItems = Array.isArray(trackedRec?.items) ? trackedRec.items : [];
288
+ const trackedChanges = trackedItems.map((t) => {
289
+ const rec = asRecord(t) ?? {};
290
+ const addr = asRecord(rec.address);
291
+ const story = asRecord(addr?.story);
292
+ return {
293
+ id: asString(rec.id),
294
+ type: asString(rec.type, 'insert'),
295
+ excerpt: typeof rec.excerpt === 'string' ? rec.excerpt : undefined,
296
+ author: typeof rec.author === 'string' ? rec.author : undefined,
297
+ date: typeof rec.date === 'string' ? rec.date : undefined,
298
+ story: typeof story?.storyType === 'string' ? story.storyType : undefined,
299
+ };
300
+ });
301
+ // Sections (best effort; not all SDK builds expose a sections.list)
302
+ const sectionsFn = maybeMethod(doc, ['sections', 'list']);
303
+ let sections = [];
304
+ if (includesDomain(requestedDomains, 'sections') && sectionsFn) {
305
+ const sectionsRaw = await safeCall(() => sectionsFn({}), null, recordError('sections.list'));
306
+ const rec = asRecord(sectionsRaw);
307
+ const items = Array.isArray(rec?.items) ? rec.items : [];
308
+ sections = items.map((s, index) => {
309
+ const srec = asRecord(s) ?? {};
310
+ const pageSize = asRecord(srec.pageSize);
311
+ return {
312
+ ordinal: asNumber(srec.ordinal, index + 1),
313
+ sectionId: typeof srec.sectionId === 'string' ? srec.sectionId : undefined,
314
+ startNodeId: typeof srec.startNodeId === 'string' ? srec.startNodeId : undefined,
315
+ endNodeId: typeof srec.endNodeId === 'string' ? srec.endNodeId : undefined,
316
+ pageSize: pageSize ? { width: asNumber(pageSize.width), height: asNumber(pageSize.height) } : undefined,
317
+ };
318
+ });
319
+ }
320
+ // Header / footer (best effort)
321
+ const headerFooterFn = maybeMethod(doc, ['headerFooters', 'list']);
322
+ let headerFooters = [];
323
+ if (includesDomain(requestedDomains, 'headerFooters') && headerFooterFn) {
324
+ const raw = await safeCall(() => headerFooterFn({}), null, recordError('headerFooters.list'));
325
+ const rec = asRecord(raw);
326
+ const items = Array.isArray(rec?.items) ? rec.items : [];
327
+ headerFooters = items.flatMap((hf) => {
328
+ const r = asRecord(hf);
329
+ if (!r)
330
+ return [];
331
+ const kind = asString(r.kind, 'header');
332
+ return [
333
+ {
334
+ kind: kind === 'footer' ? 'footer' : 'header',
335
+ sectionId: typeof r.sectionId === 'string' ? r.sectionId : undefined,
336
+ sectionIndex: typeof r.sectionIndex === 'number' ? r.sectionIndex : undefined,
337
+ variant: typeof r.variant === 'string' ? r.variant : undefined,
338
+ blocks: [],
339
+ },
340
+ ];
341
+ });
342
+ }
343
+ // Styles
344
+ const stylesFn = maybeMethod(doc, ['styles', 'list']);
345
+ let styles = [];
346
+ if (includesDomain(requestedDomains, 'styles') && stylesFn) {
347
+ const raw = await safeCall(() => stylesFn({}), null, recordError('styles.list'));
348
+ const rec = asRecord(raw);
349
+ const items = Array.isArray(rec?.items) ? rec.items : [];
350
+ styles = items.flatMap((it) => {
351
+ const r = asRecord(it);
352
+ if (!r)
353
+ return [];
354
+ return [
355
+ {
356
+ styleId: asString(r.styleId),
357
+ name: asString(r.name, asString(r.styleId)),
358
+ type: typeof r.type === 'string' ? r.type : undefined,
359
+ basedOn: typeof r.basedOn === 'string' ? r.basedOn : undefined,
360
+ },
361
+ ];
362
+ });
363
+ }
364
+ // Content controls
365
+ const contentControlsFn = maybeMethod(doc, ['contentControls', 'list']);
366
+ let contentControls = [];
367
+ if (includesDomain(requestedDomains, 'contentControls') && contentControlsFn) {
368
+ const raw = await safeCall(() => contentControlsFn({}), null, recordError('contentControls.list'));
369
+ const rec = asRecord(raw);
370
+ const items = Array.isArray(rec?.items) ? rec.items : [];
371
+ contentControls = items.flatMap((it) => {
372
+ const r = asRecord(it);
373
+ if (!r)
374
+ return [];
375
+ return [
376
+ {
377
+ id: asString(r.id),
378
+ type: typeof r.type === 'string' ? r.type : undefined,
379
+ alias: typeof r.alias === 'string' ? r.alias : undefined,
380
+ tag: typeof r.tag === 'string' ? r.tag : undefined,
381
+ text: typeof r.text === 'string' ? r.text : undefined,
382
+ },
383
+ ];
384
+ });
385
+ }
386
+ // Fields
387
+ const fieldsFn = maybeMethod(doc, ['fields', 'list']);
388
+ let fields = [];
389
+ if (includesDomain(requestedDomains, 'fields') && fieldsFn) {
390
+ const raw = await safeCall(() => fieldsFn({}), null, recordError('fields.list'));
391
+ const rec = asRecord(raw);
392
+ const items = Array.isArray(rec?.items) ? rec.items : [];
393
+ fields = items.flatMap((it) => {
394
+ const r = asRecord(it);
395
+ if (!r)
396
+ return [];
397
+ return [
398
+ {
399
+ id: asString(r.id),
400
+ type: typeof r.type === 'string' ? r.type : undefined,
401
+ result: typeof r.result === 'string' ? r.result : undefined,
402
+ },
403
+ ];
404
+ });
405
+ }
406
+ // Hyperlinks
407
+ const hyperlinksFn = maybeMethod(doc, ['hyperlinks', 'list']);
408
+ let hyperlinks = [];
409
+ if (includesDomain(requestedDomains, 'hyperlinks') && hyperlinksFn) {
410
+ const raw = await safeCall(() => hyperlinksFn({}), null, recordError('hyperlinks.list'));
411
+ const rec = asRecord(raw);
412
+ const items = Array.isArray(rec?.items) ? rec.items : [];
413
+ hyperlinks = items.flatMap((it) => {
414
+ const r = asRecord(it);
415
+ if (!r)
416
+ return [];
417
+ return [
418
+ {
419
+ id: asString(r.id),
420
+ url: asString(r.url),
421
+ display: typeof r.display === 'string' ? r.display : undefined,
422
+ nodeId: typeof r.nodeId === 'string' ? r.nodeId : undefined,
423
+ },
424
+ ];
425
+ });
426
+ }
427
+ // Bookmarks
428
+ const bookmarksFn = maybeMethod(doc, ['bookmarks', 'list']);
429
+ let bookmarks = [];
430
+ if (includesDomain(requestedDomains, 'bookmarks') && bookmarksFn) {
431
+ const raw = await safeCall(() => bookmarksFn({}), null, recordError('bookmarks.list'));
432
+ const rec = asRecord(raw);
433
+ const items = Array.isArray(rec?.items) ? rec.items : [];
434
+ bookmarks = items.flatMap((it) => {
435
+ const r = asRecord(it);
436
+ if (!r)
437
+ return [];
438
+ return [
439
+ {
440
+ id: asString(r.id),
441
+ name: asString(r.name, asString(r.id)),
442
+ startNodeId: typeof r.startNodeId === 'string' ? r.startNodeId : undefined,
443
+ endNodeId: typeof r.endNodeId === 'string' ? r.endNodeId : undefined,
444
+ },
445
+ ];
446
+ });
447
+ }
448
+ // Permission ranges
449
+ const prFn = maybeMethod(doc, ['permissionRanges', 'list']);
450
+ let permissionRanges = [];
451
+ if (includesDomain(requestedDomains, 'permissionRanges') && prFn) {
452
+ const raw = await safeCall(() => prFn({}), null, recordError('permissionRanges.list'));
453
+ const rec = asRecord(raw);
454
+ const items = Array.isArray(rec?.items) ? rec.items : [];
455
+ permissionRanges = items.flatMap((it) => {
456
+ const r = asRecord(it);
457
+ if (!r)
458
+ return [];
459
+ return [
460
+ {
461
+ id: asString(r.id),
462
+ startNodeId: typeof r.startNodeId === 'string' ? r.startNodeId : undefined,
463
+ endNodeId: typeof r.endNodeId === 'string' ? r.endNodeId : undefined,
464
+ editorOrEditorGroup: typeof r.editorOrEditorGroup === 'string' ? r.editorOrEditorGroup : undefined,
465
+ },
466
+ ];
467
+ });
468
+ }
469
+ // Images
470
+ const imagesFn = maybeMethod(doc, ['images', 'list']);
471
+ let images = [];
472
+ if (includesDomain(requestedDomains, 'images') && imagesFn) {
473
+ const raw = await safeCall(() => imagesFn({}), null, recordError('images.list'));
474
+ const rec = asRecord(raw);
475
+ const items = Array.isArray(rec?.items) ? rec.items : [];
476
+ images = items.flatMap((it) => {
477
+ const r = asRecord(it);
478
+ if (!r)
479
+ return [];
480
+ return [
481
+ {
482
+ imageId: asString(r.imageId, asString(r.id)),
483
+ nodeId: typeof r.nodeId === 'string' ? r.nodeId : undefined,
484
+ alt: typeof r.alt === 'string' ? r.alt : undefined,
485
+ caption: typeof r.caption === 'string' ? r.caption : undefined,
486
+ },
487
+ ];
488
+ });
489
+ }
490
+ return {
491
+ revision,
492
+ counts: {
493
+ blocks: countsFromInfo.blocks || totalBlocks || normalizedBlocks.length,
494
+ paragraphs: countsFromInfo.paragraphs || normalizedBlocks.filter((b) => b.nodeType === 'paragraph').length,
495
+ headings: countsFromInfo.headings || normalizedBlocks.filter((b) => b.nodeType === 'heading').length,
496
+ tables: countsFromInfo.tables || tableBlocks.length,
497
+ lists: countsFromInfo.lists || lists.length,
498
+ images: countsFromInfo.images || images.length,
499
+ comments: countsFromInfo.comments || comments.length,
500
+ trackedChanges: countsFromInfo.trackedChanges || trackedChanges.length,
501
+ sections: countsFromInfo.sections || sections.length,
502
+ fields: countsFromInfo.fields || fields.length,
503
+ hyperlinks: countsFromInfo.hyperlinks || hyperlinks.length,
504
+ bookmarks: countsFromInfo.bookmarks || bookmarks.length,
505
+ contentControls: countsFromInfo.contentControls || contentControls.length,
506
+ permissionRanges: countsFromInfo.permissionRanges || permissionRanges.length,
507
+ styles: countsFromInfo.styles || styles.length,
508
+ headers: countsFromInfo.headers || headerFooters.filter((h) => h.kind === 'header').length,
509
+ footers: countsFromInfo.footers || headerFooters.filter((h) => h.kind === 'footer').length,
510
+ },
511
+ blocks,
512
+ lists,
513
+ tables,
514
+ comments,
515
+ trackedChanges,
516
+ sections,
517
+ headerFooters,
518
+ styles,
519
+ contentControls,
520
+ fields,
521
+ hyperlinks,
522
+ bookmarks,
523
+ permissionRanges,
524
+ images,
525
+ diagnostics,
526
+ };
527
+ }
528
+ /**
529
+ * Structured ambiguity error returned when multiple candidates match a
530
+ * selector and the plan required uniqueness.
531
+ */
532
+ class AmbiguousSelectorError extends Error {
533
+ code = 'AMBIGUOUS_SELECTOR';
534
+ candidates;
535
+ constructor(message, candidates) {
536
+ super(message);
537
+ this.name = 'AmbiguousSelectorError';
538
+ this.candidates = candidates;
539
+ }
540
+ }
541
+ /**
542
+ * Resolve a deterministic selector against the snapshot. Returns the matching
543
+ * block ids in document order. Selectors that target entities return
544
+ * entity-domain matches by id.
545
+ */
546
+ function resolveSnapshotSelector(snapshot, selector) {
547
+ const nonEmptyParagraphs = snapshot.blocks.filter((block) => block.nodeType === 'paragraph' && block.text.trim().length > 0);
548
+ const firstHeading = snapshot.blocks.find((block) => block.nodeType === 'heading');
549
+ const bodyParagraphs = firstHeading == null
550
+ ? nonEmptyParagraphs
551
+ : snapshot.blocks.filter((block) => block.nodeType === 'paragraph' && block.text.trim().length > 0 && block.ordinal > firstHeading.ordinal);
552
+ switch (selector.kind) {
553
+ case 'ref':
554
+ return snapshot.blocks.filter((b) => b.nodeId === selector.ref).map((b) => b.nodeId);
555
+ case 'nodeId':
556
+ return snapshot.blocks.filter((b) => b.nodeId === selector.nodeId).map((b) => b.nodeId);
557
+ case 'tableCell': {
558
+ const table = snapshot.tables[selector.tableOrdinal - 1];
559
+ if (!table)
560
+ return [];
561
+ const cell = table.cells.find((entry) => entry.rowIndex === selector.rowIndex && entry.columnIndex === selector.columnIndex);
562
+ return cell?.nodeId ? [cell.nodeId] : [];
563
+ }
564
+ case 'textSearch': {
565
+ const matchMode = selector.match ?? 'all';
566
+ const occurrence = selector.occurrence ?? 1;
567
+ const caseSensitive = selector.caseSensitive === true;
568
+ const allowedNodeTypes = new Set(selector.nodeTypes ?? ['paragraph', 'heading', 'listItem']);
569
+ const terms = selector.terms.filter((term) => term.trim().length > 0);
570
+ if (terms.length === 0)
571
+ return [];
572
+ const normalizedTerms = caseSensitive ? terms : terms.map((term) => term.toLocaleLowerCase());
573
+ const matches = snapshot.blocks.filter((block) => {
574
+ if (!allowedNodeTypes.has(block.nodeType))
575
+ return false;
576
+ if (block.text.trim().length === 0)
577
+ return false;
578
+ const haystack = caseSensitive ? block.text : block.text.toLocaleLowerCase();
579
+ const hits = normalizedTerms.filter((term) => haystack.includes(term)).length;
580
+ return matchMode === 'any' ? hits > 0 : hits === normalizedTerms.length;
581
+ });
582
+ const target = matches[occurrence - 1];
583
+ return target ? [target.nodeId] : [];
584
+ }
585
+ case 'ordinal': {
586
+ switch (selector.ordinalKind) {
587
+ case 'blockOrdinal': {
588
+ const target = snapshot.blocks[selector.value - 1];
589
+ return target ? [target.nodeId] : [];
590
+ }
591
+ case 'paragraphOrdinal': {
592
+ const target = nonEmptyParagraphs[selector.value - 1];
593
+ return target ? [target.nodeId] : [];
594
+ }
595
+ case 'bodyParagraphOrdinal': {
596
+ const target = (bodyParagraphs.length > 0 ? bodyParagraphs : nonEmptyParagraphs)[selector.value - 1];
597
+ return target ? [target.nodeId] : [];
598
+ }
599
+ case 'headingOrdinal': {
600
+ const headings = snapshot.blocks.filter((b) => b.nodeType === 'heading');
601
+ const target = headings[selector.value - 1];
602
+ return target ? [target.nodeId] : [];
603
+ }
604
+ case 'tableOrdinal': {
605
+ const target = snapshot.tables[selector.value - 1];
606
+ return target ? [target.nodeId] : [];
607
+ }
608
+ case 'listOrdinal': {
609
+ const target = snapshot.lists[selector.value - 1];
610
+ return target ? target.items.map((item) => item.nodeId) : [];
611
+ }
612
+ case 'sectionOrdinal': {
613
+ const target = snapshot.sections[selector.value - 1];
614
+ return target?.startNodeId ? [target.startNodeId] : [];
615
+ }
616
+ }
617
+ return [];
618
+ }
619
+ case 'entity':
620
+ switch (selector.entityType) {
621
+ case 'comment':
622
+ return snapshot.comments.filter((c) => c.id === selector.entityId).map((c) => c.id);
623
+ case 'trackedChange':
624
+ return snapshot.trackedChanges.filter((c) => c.id === selector.entityId).map((c) => c.id);
625
+ case 'bookmark':
626
+ return snapshot.bookmarks.filter((b) => b.id === selector.entityId).map((b) => b.id);
627
+ case 'image':
628
+ return snapshot.images.filter((i) => i.imageId === selector.entityId).map((i) => i.imageId);
629
+ case 'hyperlink':
630
+ return snapshot.hyperlinks.filter((h) => h.id === selector.entityId).map((h) => h.id);
631
+ case 'field':
632
+ return snapshot.fields.filter((f) => f.id === selector.entityId).map((f) => f.id);
633
+ }
634
+ return [];
635
+ case 'placement':
636
+ if (selector.at === 'document_start') {
637
+ const first = snapshot.blocks[0];
638
+ return first ? [first.nodeId] : [];
639
+ }
640
+ const last = snapshot.blocks[snapshot.blocks.length - 1];
641
+ return last ? [last.nodeId] : [];
642
+ case 'relative': {
643
+ const targetIds = resolveSnapshotSelector(snapshot, selector.target);
644
+ const offset = selector.position === 'before' ? -1 : 1;
645
+ const matches = [];
646
+ for (const targetId of targetIds) {
647
+ const index = snapshot.blocks.findIndex((block) => block.nodeId === targetId);
648
+ if (index === -1)
649
+ continue;
650
+ const sibling = snapshot.blocks[index + offset];
651
+ if (sibling != null)
652
+ matches.push(sibling.nodeId);
653
+ }
654
+ return [...new Set(matches)];
655
+ }
656
+ case 'document':
657
+ return snapshot.blocks.map((b) => b.nodeId);
658
+ }
659
+ }
660
+
661
+ exports.AmbiguousSelectorError = AmbiguousSelectorError;
662
+ exports.buildDocumentSnapshot = buildDocumentSnapshot;
663
+ exports.resolveSnapshotSelector = resolveSnapshotSelector;