@opengeni/contracts 0.39.0 → 0.44.1

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 (130) hide show
  1. package/dist/atlassian.d.ts +278 -0
  2. package/dist/atlassian.js +55 -0
  3. package/dist/atlassian.js.map +1 -0
  4. package/dist/chunk-52SGB2QO.js +688 -0
  5. package/dist/chunk-52SGB2QO.js.map +1 -0
  6. package/dist/{chunk-7JZMI22I.js → chunk-5UDNP7AO.js} +34 -2
  7. package/dist/chunk-5UDNP7AO.js.map +1 -0
  8. package/dist/chunk-BVGIYW7D.js +11 -0
  9. package/dist/chunk-BVGIYW7D.js.map +1 -0
  10. package/dist/chunk-CADSX6UN.js +1145 -0
  11. package/dist/chunk-CADSX6UN.js.map +1 -0
  12. package/dist/chunk-D2HKF2EF.js +1460 -0
  13. package/dist/chunk-D2HKF2EF.js.map +1 -0
  14. package/dist/chunk-FJBG5E4O.js +194 -0
  15. package/dist/chunk-FJBG5E4O.js.map +1 -0
  16. package/dist/chunk-H5BVCZKF.js +382 -0
  17. package/dist/chunk-H5BVCZKF.js.map +1 -0
  18. package/dist/chunk-LSB7GRLG.js +779 -0
  19. package/dist/chunk-LSB7GRLG.js.map +1 -0
  20. package/dist/chunk-MYBZRDTY.js +340 -0
  21. package/dist/chunk-MYBZRDTY.js.map +1 -0
  22. package/dist/chunk-RIY7GYQS.js +1204 -0
  23. package/dist/chunk-RIY7GYQS.js.map +1 -0
  24. package/dist/chunk-TZ6YEYN4.js +386 -0
  25. package/dist/chunk-TZ6YEYN4.js.map +1 -0
  26. package/dist/chunk-VA22KRHS.js +15177 -0
  27. package/dist/chunk-VA22KRHS.js.map +1 -0
  28. package/dist/chunk-Y3MLSLB4.js +392 -0
  29. package/dist/chunk-Y3MLSLB4.js.map +1 -0
  30. package/dist/chunk-YRKRZ6OF.js +134 -0
  31. package/dist/chunk-YRKRZ6OF.js.map +1 -0
  32. package/dist/company-profile.d.ts +629 -0
  33. package/dist/document-artifact-commands.d.ts +282 -0
  34. package/dist/document-artifact-commands.js +54 -0
  35. package/dist/document-artifact-commands.js.map +1 -0
  36. package/dist/document-artifact-query.d.ts +2 -0
  37. package/dist/document-artifact-query.js +54 -0
  38. package/dist/document-artifact-query.js.map +1 -0
  39. package/dist/editable-artifact-binary.d.ts +56 -0
  40. package/dist/editable-artifact-causal-frontier.d.ts +12 -0
  41. package/dist/editable-artifact-causal-frontier.js +18 -0
  42. package/dist/editable-artifact-causal-frontier.js.map +1 -0
  43. package/dist/editable-artifact-codec-registry.d.ts +48 -0
  44. package/dist/editable-artifact-codec-registry.js +20 -0
  45. package/dist/editable-artifact-codec-registry.js.map +1 -0
  46. package/dist/editable-artifact-committed-transaction.d.ts +35 -0
  47. package/dist/editable-artifact-committed-transaction.js +19 -0
  48. package/dist/editable-artifact-committed-transaction.js.map +1 -0
  49. package/dist/editable-artifact-live.d.ts +276 -0
  50. package/dist/editable-artifact-live.js +907 -0
  51. package/dist/editable-artifact-live.js.map +1 -0
  52. package/dist/editable-artifact-serialized-commit.d.ts +53 -0
  53. package/dist/editable-artifact-serialized-commit.js +26 -0
  54. package/dist/editable-artifact-serialized-commit.js.map +1 -0
  55. package/dist/editable-artifact-versions.d.ts +6 -0
  56. package/dist/editable-artifact-versions.js +11 -0
  57. package/dist/editable-artifact-versions.js.map +1 -0
  58. package/dist/editable-artifacts.d.ts +46 -0
  59. package/dist/editable-artifacts.js +236 -0
  60. package/dist/editable-artifacts.js.map +1 -0
  61. package/dist/event-preview.d.ts +7 -7
  62. package/dist/google-drive.d.ts +114 -2
  63. package/dist/google-drive.js +70 -14
  64. package/dist/google-drive.js.map +1 -1
  65. package/dist/image-generation.d.ts +70 -0
  66. package/dist/index.d.ts +5180 -329
  67. package/dist/index.js +1173 -40
  68. package/dist/interaction.d.ts +6895 -0
  69. package/dist/mcp-receipts.d.ts +96 -0
  70. package/dist/memory-slack-delivery.d.ts +397 -0
  71. package/dist/presentation-artifact-commands.d.ts +349 -0
  72. package/dist/presentation-artifact-commands.js +66 -0
  73. package/dist/presentation-artifact-commands.js.map +1 -0
  74. package/dist/presentation-artifact-query.d.ts +2 -0
  75. package/dist/presentation-artifact-query.js +66 -0
  76. package/dist/presentation-artifact-query.js.map +1 -0
  77. package/dist/retained-output.d.ts +83 -6
  78. package/dist/sandbox-snapshots.d.ts +9 -0
  79. package/dist/scoped-knowledge.d.ts +14 -13
  80. package/dist/slack-bot-scopes.d.ts +4 -0
  81. package/dist/slack-bot-scopes.js +3 -1
  82. package/dist/spreadsheet-artifact-commands.d.ts +101 -0
  83. package/dist/spreadsheet-artifact-commands.js +27 -0
  84. package/dist/spreadsheet-artifact-commands.js.map +1 -0
  85. package/dist/spreadsheet-artifact-date.d.ts +4 -0
  86. package/dist/spreadsheet-artifact-query.d.ts +120 -0
  87. package/dist/spreadsheet-artifact-query.js +48 -0
  88. package/dist/spreadsheet-artifact-query.js.map +1 -0
  89. package/dist/tool-catalog.d.ts +576 -0
  90. package/dist/video-generation.d.ts +532 -0
  91. package/dist/video-generation.js +51 -0
  92. package/dist/video-generation.js.map +1 -0
  93. package/dist/workspace-learning-policy.d.ts +208 -0
  94. package/package.json +61 -1
  95. package/src/atlassian.ts +160 -0
  96. package/src/company-profile.ts +306 -0
  97. package/src/document-artifact-commands.ts +1709 -0
  98. package/src/document-artifact-query.ts +2 -0
  99. package/src/editable-artifact-binary.ts +396 -0
  100. package/src/editable-artifact-causal-frontier.ts +154 -0
  101. package/src/editable-artifact-codec-registry.ts +261 -0
  102. package/src/editable-artifact-committed-transaction.ts +478 -0
  103. package/src/editable-artifact-live.ts +1379 -0
  104. package/src/editable-artifact-serialized-commit.ts +494 -0
  105. package/src/editable-artifact-versions.ts +8 -0
  106. package/src/editable-artifacts.ts +473 -0
  107. package/src/event-preview.ts +6 -6
  108. package/src/google-drive.ts +68 -15
  109. package/src/image-generation.ts +106 -0
  110. package/src/index.ts +2535 -282
  111. package/src/interaction.ts +2292 -0
  112. package/src/mcp-receipts.ts +281 -0
  113. package/src/memory-slack-delivery.ts +183 -0
  114. package/src/presentation-artifact-commands.ts +2035 -0
  115. package/src/presentation-artifact-query.ts +2 -0
  116. package/src/retained-output.ts +248 -8
  117. package/src/sandbox-snapshots.ts +28 -1
  118. package/src/scoped-knowledge.ts +18 -4
  119. package/src/slack-bot-scopes.ts +34 -1
  120. package/src/spreadsheet-artifact-commands.ts +999 -0
  121. package/src/spreadsheet-artifact-date.ts +21 -0
  122. package/src/spreadsheet-artifact-query.ts +1411 -0
  123. package/src/tool-catalog.ts +334 -0
  124. package/src/video-generation.ts +394 -0
  125. package/src/workspace-learning-policy.ts +233 -0
  126. package/dist/chunk-7JZMI22I.js.map +0 -1
  127. package/dist/chunk-PCTCJEAF.js +0 -10799
  128. package/dist/chunk-PCTCJEAF.js.map +0 -1
  129. package/dist/secret-redaction.d.ts +0 -40
  130. package/src/secret-redaction.ts +0 -364
@@ -0,0 +1,1709 @@
1
+ /** Canonical identity-free OGADC001 structured-document commands. */
2
+
3
+ import {
4
+ ArtifactBinaryReader,
5
+ ArtifactBinaryWriter,
6
+ decodeCountedEnvelope,
7
+ encodeCountedEnvelope,
8
+ equalBytes,
9
+ strictUtf8,
10
+ } from "./editable-artifact-binary";
11
+
12
+ export const DOCUMENT_ARTIFACT_COMMAND_VERSION = 1 as const;
13
+ // The modality payload is nested inside OGATX001, whose public command budget
14
+ // is 4 MiB. Keep the leaf codec at that same ceiling so a locally accepted
15
+ // document edit can never become unsendable at the durable/live boundary.
16
+ export const DOCUMENT_ARTIFACT_COMMAND_MAX_BYTES = 4 * 1024 * 1024;
17
+ export const DOCUMENT_ARTIFACT_COMMAND_MAX_COMMANDS = 4_096;
18
+ export const DOCUMENT_ARTIFACT_COMMAND_MAX_STRING_BYTES = 4 * 1024 * 1024;
19
+ export const DOCUMENT_ARTIFACT_MAX_TEXT_UTF16 = 10_000_000;
20
+ export const DOCUMENT_ARTIFACT_MAX_TEXT_RUNS = 250_000;
21
+ export const DOCUMENT_ARTIFACT_MAX_TABLE_CELLS = 1_000_000;
22
+ export const DOCUMENT_ARTIFACT_MAX_STRUCTURAL_COUNTER = 2 ** 53 - 2;
23
+ export const DOCUMENT_ARTIFACT_QUERY_VERSION = 1 as const;
24
+ export const DOCUMENT_ARTIFACT_QUERY_RESPONSE_VERSION = 1 as const;
25
+ export const DOCUMENT_ARTIFACT_QUERY_MAX_BYTES = 256;
26
+ export const DOCUMENT_ARTIFACT_QUERY_RESPONSE_MAX_BYTES = 8 * 1024 * 1024;
27
+ export const DOCUMENT_ARTIFACT_QUERY_MAX_ITEMS = 4_096;
28
+ export const DOCUMENT_ARTIFACT_QUERY_MAX_TEXT_UTF16 = 1_000_000;
29
+ export const DOCUMENT_ARTIFACT_QUERY_MAX_TABLE_CELLS = 100_000;
30
+
31
+ const MAGIC = "OGADC001";
32
+ const QUERY_MAGIC = "OGADQ001";
33
+ const QUERY_RESPONSE_MAGIC = "OGADP001";
34
+ const QUERY_RESPONSE_EXTENSION_MARKER = 0xff;
35
+ const PAGE_EXTRAS_EXTENSION_VERSION = 1;
36
+
37
+ export type DocumentArtifactIdKind =
38
+ | "paragraph"
39
+ | "table"
40
+ | "page-break"
41
+ | "section"
42
+ | "header"
43
+ | "footer"
44
+ | "comment"
45
+ | "tracked-change";
46
+
47
+ export type DocumentArtifactId = string;
48
+
49
+ export type DocumentArtifactPageGeometry = Readonly<{
50
+ widthPt: number;
51
+ heightPt: number;
52
+ marginTopPt: number;
53
+ marginRightPt: number;
54
+ marginBottomPt: number;
55
+ marginLeftPt: number;
56
+ headerPt?: number;
57
+ footerPt?: number;
58
+ gutterPt?: number;
59
+ }>;
60
+
61
+ export type DocumentArtifactTextStyle = Readonly<{
62
+ fontFamily?: string;
63
+ fontSizePt?: number;
64
+ color?: string;
65
+ bold?: boolean;
66
+ italic?: boolean;
67
+ underline?: boolean;
68
+ strike?: boolean;
69
+ }>;
70
+
71
+ export type DocumentArtifactTextStylePatch = Readonly<{
72
+ fontFamily?: string | null;
73
+ fontSizePt?: number | null;
74
+ color?: string | null;
75
+ bold?: boolean | null;
76
+ italic?: boolean | null;
77
+ underline?: boolean | null;
78
+ strike?: boolean | null;
79
+ }>;
80
+
81
+ export type DocumentArtifactTextRun = Readonly<{
82
+ text: string;
83
+ style: DocumentArtifactTextStyle;
84
+ }>;
85
+
86
+ export type DocumentArtifactParagraphStyle = Readonly<{
87
+ headingLevel?: number;
88
+ alignment?: "left" | "center" | "right" | "justify";
89
+ spaceBeforePt?: number;
90
+ spaceAfterPt?: number;
91
+ lineHeight?: number;
92
+ keepNext?: boolean;
93
+ pageBreakBefore?: boolean;
94
+ list?: Readonly<{
95
+ kind: "bullet" | "number";
96
+ level: number | null;
97
+ instanceId: string | null;
98
+ }>;
99
+ }>;
100
+
101
+ export type DocumentArtifactTableStyle = Readonly<{
102
+ widthPt?: number;
103
+ columnWidthsPt?: readonly number[];
104
+ headerRows?: number;
105
+ cellPaddingPt?: number;
106
+ borderColor?: string;
107
+ headerFill?: string;
108
+ allowRowSplit?: boolean;
109
+ }>;
110
+
111
+ export type DocumentArtifactStoryTarget =
112
+ | Readonly<{ kind: "body" }>
113
+ | Readonly<{
114
+ kind: "section";
115
+ sectionId: DocumentArtifactId;
116
+ storyKind: "header" | "footer";
117
+ variant: "default" | "first" | "even";
118
+ }>;
119
+
120
+ export type DocumentArtifactTextRange = Readonly<{ start: number; end: number }>;
121
+ export type DocumentArtifactCommentReply = Readonly<{
122
+ author: string;
123
+ text: string;
124
+ createdAt: string;
125
+ }>;
126
+
127
+ export type DocumentArtifactCommand =
128
+ | Readonly<{
129
+ kind: "document.flags.set";
130
+ evenAndOddHeaders?: boolean | null;
131
+ trackRevisions?: boolean | null;
132
+ }>
133
+ | Readonly<{
134
+ kind: "paragraph.add";
135
+ target: DocumentArtifactStoryTarget;
136
+ id: DocumentArtifactId;
137
+ runs: readonly DocumentArtifactTextRun[];
138
+ style: DocumentArtifactParagraphStyle;
139
+ }>
140
+ | Readonly<{
141
+ kind: "paragraph.edit";
142
+ id: DocumentArtifactId;
143
+ range: DocumentArtifactTextRange;
144
+ replacement: string;
145
+ style: DocumentArtifactTextStyle | null;
146
+ }>
147
+ | Readonly<{
148
+ kind: "paragraph.format";
149
+ id: DocumentArtifactId;
150
+ range: DocumentArtifactTextRange;
151
+ style: DocumentArtifactTextStylePatch;
152
+ }>
153
+ | Readonly<{
154
+ kind: "paragraph.style.set";
155
+ id: DocumentArtifactId;
156
+ style: DocumentArtifactParagraphStyle;
157
+ }>
158
+ | Readonly<{
159
+ kind: "table.add";
160
+ target: DocumentArtifactStoryTarget;
161
+ id: DocumentArtifactId;
162
+ rows: readonly (readonly (readonly DocumentArtifactTextRun[])[])[];
163
+ style: DocumentArtifactTableStyle;
164
+ }>
165
+ | Readonly<{ kind: "table.style.set"; id: DocumentArtifactId; style: DocumentArtifactTableStyle }>
166
+ | Readonly<{ kind: "page-break.add"; id: DocumentArtifactId }>
167
+ | Readonly<{
168
+ kind: "section.add";
169
+ ids: Readonly<{
170
+ section: DocumentArtifactId;
171
+ headerDefault: DocumentArtifactId;
172
+ headerFirst: DocumentArtifactId;
173
+ headerEven: DocumentArtifactId;
174
+ footerDefault: DocumentArtifactId;
175
+ footerFirst: DocumentArtifactId;
176
+ footerEven: DocumentArtifactId;
177
+ }>;
178
+ page: DocumentArtifactPageGeometry;
179
+ titlePage: boolean | null;
180
+ }>
181
+ | Readonly<{ kind: "section.title-page.set"; id: DocumentArtifactId; titlePage: boolean | null }>
182
+ | Readonly<{
183
+ kind: "section.page.set";
184
+ id: DocumentArtifactId;
185
+ page: DocumentArtifactPageGeometry;
186
+ }>
187
+ | Readonly<{
188
+ kind: "comment.add";
189
+ id: DocumentArtifactId;
190
+ paragraphId: DocumentArtifactId;
191
+ range: DocumentArtifactTextRange;
192
+ resolved: boolean;
193
+ root: DocumentArtifactCommentReply;
194
+ }>
195
+ | Readonly<{
196
+ kind: "comment.reply.add";
197
+ id: DocumentArtifactId;
198
+ reply: DocumentArtifactCommentReply;
199
+ }>
200
+ | Readonly<{ kind: "comment.resolved.set"; id: DocumentArtifactId; resolved: boolean }>
201
+ | Readonly<{
202
+ kind: "tracked-change.add";
203
+ id: DocumentArtifactId;
204
+ paragraphId: DocumentArtifactId;
205
+ range: DocumentArtifactTextRange;
206
+ changeKind: "insert" | "delete";
207
+ author: string;
208
+ createdAt: string;
209
+ }>;
210
+
211
+ export type DocumentArtifactCommandBatch = Readonly<{
212
+ version: typeof DOCUMENT_ARTIFACT_COMMAND_VERSION;
213
+ commands: readonly DocumentArtifactCommand[];
214
+ }>;
215
+
216
+ type CodecState = { runs: number; textUtf16: number; tableCells: number };
217
+
218
+ export function encodeDocumentArtifactCommandBatch(
219
+ input: DocumentArtifactCommandBatch,
220
+ ): Uint8Array {
221
+ if (input?.version !== DOCUMENT_ARTIFACT_COMMAND_VERSION || !Array.isArray(input.commands)) {
222
+ throw new TypeError("document command batch is invalid");
223
+ }
224
+ if (input.commands.length > DOCUMENT_ARTIFACT_COMMAND_MAX_COMMANDS) {
225
+ throw new RangeError("document command count exceeds its limit");
226
+ }
227
+ const payload = new ArtifactBinaryWriter(DOCUMENT_ARTIFACT_COMMAND_MAX_BYTES - 32);
228
+ const state: CodecState = { runs: 0, textUtf16: 0, tableCells: 0 };
229
+ for (const command of input.commands) encodeCommand(payload, command, state);
230
+ return encodeCountedEnvelope(
231
+ MAGIC,
232
+ DOCUMENT_ARTIFACT_COMMAND_VERSION,
233
+ input.commands.length,
234
+ payload.finish(),
235
+ DOCUMENT_ARTIFACT_COMMAND_MAX_BYTES,
236
+ );
237
+ }
238
+
239
+ export function decodeDocumentArtifactCommandBatch(
240
+ bytes: Uint8Array,
241
+ ): DocumentArtifactCommandBatch {
242
+ const { count, payload } = decodeCountedEnvelope(
243
+ bytes,
244
+ MAGIC,
245
+ DOCUMENT_ARTIFACT_COMMAND_VERSION,
246
+ DOCUMENT_ARTIFACT_COMMAND_MAX_BYTES,
247
+ DOCUMENT_ARTIFACT_COMMAND_MAX_COMMANDS,
248
+ );
249
+ const reader = new ArtifactBinaryReader(payload);
250
+ const state: CodecState = { runs: 0, textUtf16: 0, tableCells: 0 };
251
+ const commands: DocumentArtifactCommand[] = [];
252
+ for (let index = 0; index < count; index += 1) commands.push(decodeCommand(reader, state));
253
+ reader.done("document command payload contains trailing bytes");
254
+ return Object.freeze({
255
+ version: DOCUMENT_ARTIFACT_COMMAND_VERSION,
256
+ commands: Object.freeze(commands),
257
+ });
258
+ }
259
+
260
+ export function assertCanonicalDocumentArtifactCommandBytes(bytes: Uint8Array): void {
261
+ if (
262
+ !equalBytes(
263
+ bytes,
264
+ encodeDocumentArtifactCommandBatch(decodeDocumentArtifactCommandBatch(bytes)),
265
+ )
266
+ ) {
267
+ throw new TypeError("document command batch is not canonically encoded");
268
+ }
269
+ }
270
+
271
+ function encodeCommand(
272
+ writer: ArtifactBinaryWriter,
273
+ command: DocumentArtifactCommand,
274
+ state: CodecState,
275
+ ): void {
276
+ switch (command.kind) {
277
+ case "document.flags.set":
278
+ writer.u8(0);
279
+ writePatchBool(writer, command.evenAndOddHeaders);
280
+ writePatchBool(writer, command.trackRevisions);
281
+ return;
282
+ case "paragraph.add":
283
+ writer.u8(1);
284
+ writeTarget(writer, command.target);
285
+ writeDocumentId(writer, command.id, "paragraph");
286
+ writeRuns(writer, command.runs, state);
287
+ writeParagraphStyle(writer, command.style);
288
+ return;
289
+ case "paragraph.edit":
290
+ writer.u8(2);
291
+ writeDocumentId(writer, command.id, "paragraph");
292
+ writeRange(writer, command.range);
293
+ writeDocumentString(writer, command.replacement, state, true);
294
+ writer.bool(command.style !== null);
295
+ if (command.style !== null) writeTextStyle(writer, command.style);
296
+ return;
297
+ case "paragraph.format":
298
+ writer.u8(3);
299
+ writeDocumentId(writer, command.id, "paragraph");
300
+ writeRange(writer, command.range);
301
+ writeTextStylePatch(writer, command.style);
302
+ return;
303
+ case "paragraph.style.set":
304
+ writer.u8(4);
305
+ writeDocumentId(writer, command.id, "paragraph");
306
+ writeParagraphStyle(writer, command.style);
307
+ return;
308
+ case "table.add":
309
+ writer.u8(5);
310
+ writeTarget(writer, command.target);
311
+ writeDocumentId(writer, command.id, "table");
312
+ writeTableRows(writer, command.rows, state);
313
+ writeTableStyle(writer, command.style);
314
+ return;
315
+ case "table.style.set":
316
+ writer.u8(6);
317
+ writeDocumentId(writer, command.id, "table");
318
+ writeTableStyle(writer, command.style);
319
+ return;
320
+ case "page-break.add":
321
+ writer.u8(7);
322
+ writeDocumentId(writer, command.id, "page-break");
323
+ return;
324
+ case "section.add": {
325
+ assertDefaultPageExtras(command.page);
326
+ writer.u8(8);
327
+ const ordered: readonly [DocumentArtifactId, DocumentArtifactIdKind][] = [
328
+ [command.ids.section, "section"],
329
+ [command.ids.headerDefault, "header"],
330
+ [command.ids.headerFirst, "header"],
331
+ [command.ids.headerEven, "header"],
332
+ [command.ids.footerDefault, "footer"],
333
+ [command.ids.footerFirst, "footer"],
334
+ [command.ids.footerEven, "footer"],
335
+ ];
336
+ for (const [id, kind] of ordered) writeDocumentId(writer, id, kind);
337
+ writePage(writer, command.page);
338
+ writeOptionalBool(writer, command.titlePage);
339
+ return;
340
+ }
341
+ case "section.title-page.set":
342
+ writer.u8(9);
343
+ writeDocumentId(writer, command.id, "section");
344
+ writeOptionalBool(writer, command.titlePage);
345
+ return;
346
+ case "comment.add":
347
+ writer.u8(10);
348
+ writeDocumentId(writer, command.id, "comment");
349
+ writeDocumentId(writer, command.paragraphId, "paragraph");
350
+ writeRange(writer, command.range);
351
+ writer.bool(command.resolved);
352
+ writeCommentReply(writer, command.root, state);
353
+ return;
354
+ case "comment.reply.add":
355
+ writer.u8(11);
356
+ writeDocumentId(writer, command.id, "comment");
357
+ writeCommentReply(writer, command.reply, state);
358
+ return;
359
+ case "comment.resolved.set":
360
+ writer.u8(12);
361
+ writeDocumentId(writer, command.id, "comment");
362
+ writer.bool(command.resolved);
363
+ return;
364
+ case "tracked-change.add":
365
+ writer.u8(13);
366
+ writeDocumentId(writer, command.id, "tracked-change");
367
+ writeDocumentId(writer, command.paragraphId, "paragraph");
368
+ writeRange(writer, command.range);
369
+ writer.u8(
370
+ command.changeKind === "insert"
371
+ ? 1
372
+ : command.changeKind === "delete"
373
+ ? 2
374
+ : invalidTag("tracked change kind"),
375
+ );
376
+ writeDocumentString(writer, command.author, state, false);
377
+ writeDocumentString(writer, command.createdAt, state, false);
378
+ return;
379
+ case "section.page.set":
380
+ writer.u8(14);
381
+ writeDocumentId(writer, command.id, "section");
382
+ writePageWithExtras(writer, command.page);
383
+ return;
384
+ default:
385
+ throw new TypeError("invalid document command kind");
386
+ }
387
+ }
388
+
389
+ function decodeCommand(reader: ArtifactBinaryReader, state: CodecState): DocumentArtifactCommand {
390
+ switch (reader.u8()) {
391
+ case 0: {
392
+ const evenAndOddHeaders = readPatchBool(reader);
393
+ const trackRevisions = readPatchBool(reader);
394
+ return freezeOptional(
395
+ { kind: "document.flags.set" as const },
396
+ { evenAndOddHeaders, trackRevisions },
397
+ ) as DocumentArtifactCommand;
398
+ }
399
+ case 1:
400
+ return Object.freeze({
401
+ kind: "paragraph.add",
402
+ target: readTarget(reader),
403
+ id: readDocumentId(reader, "paragraph"),
404
+ runs: readRuns(reader, state),
405
+ style: readParagraphStyle(reader),
406
+ });
407
+ case 2:
408
+ return Object.freeze({
409
+ kind: "paragraph.edit",
410
+ id: readDocumentId(reader, "paragraph"),
411
+ range: readRange(reader),
412
+ replacement: readDocumentString(reader, state, true),
413
+ style: reader.bool("optional document text style") ? readTextStyle(reader) : null,
414
+ });
415
+ case 3:
416
+ return Object.freeze({
417
+ kind: "paragraph.format",
418
+ id: readDocumentId(reader, "paragraph"),
419
+ range: readRange(reader),
420
+ style: readTextStylePatch(reader),
421
+ });
422
+ case 4:
423
+ return Object.freeze({
424
+ kind: "paragraph.style.set",
425
+ id: readDocumentId(reader, "paragraph"),
426
+ style: readParagraphStyle(reader),
427
+ });
428
+ case 5:
429
+ return Object.freeze({
430
+ kind: "table.add",
431
+ target: readTarget(reader),
432
+ id: readDocumentId(reader, "table"),
433
+ rows: readTableRows(reader, state),
434
+ style: readTableStyle(reader),
435
+ });
436
+ case 6:
437
+ return Object.freeze({
438
+ kind: "table.style.set",
439
+ id: readDocumentId(reader, "table"),
440
+ style: readTableStyle(reader),
441
+ });
442
+ case 7:
443
+ return Object.freeze({ kind: "page-break.add", id: readDocumentId(reader, "page-break") });
444
+ case 8:
445
+ return Object.freeze({
446
+ kind: "section.add",
447
+ ids: Object.freeze({
448
+ section: readDocumentId(reader, "section"),
449
+ headerDefault: readDocumentId(reader, "header"),
450
+ headerFirst: readDocumentId(reader, "header"),
451
+ headerEven: readDocumentId(reader, "header"),
452
+ footerDefault: readDocumentId(reader, "footer"),
453
+ footerFirst: readDocumentId(reader, "footer"),
454
+ footerEven: readDocumentId(reader, "footer"),
455
+ }),
456
+ page: readPage(reader),
457
+ titlePage: readOptionalBool(reader),
458
+ });
459
+ case 9:
460
+ return Object.freeze({
461
+ kind: "section.title-page.set",
462
+ id: readDocumentId(reader, "section"),
463
+ titlePage: readOptionalBool(reader),
464
+ });
465
+ case 10:
466
+ return Object.freeze({
467
+ kind: "comment.add",
468
+ id: readDocumentId(reader, "comment"),
469
+ paragraphId: readDocumentId(reader, "paragraph"),
470
+ range: readRange(reader),
471
+ resolved: reader.bool("document boolean"),
472
+ root: readCommentReply(reader, state),
473
+ });
474
+ case 11:
475
+ return Object.freeze({
476
+ kind: "comment.reply.add",
477
+ id: readDocumentId(reader, "comment"),
478
+ reply: readCommentReply(reader, state),
479
+ });
480
+ case 12:
481
+ return Object.freeze({
482
+ kind: "comment.resolved.set",
483
+ id: readDocumentId(reader, "comment"),
484
+ resolved: reader.bool("document boolean"),
485
+ });
486
+ case 13: {
487
+ const id = readDocumentId(reader, "tracked-change");
488
+ const paragraphId = readDocumentId(reader, "paragraph");
489
+ const range = readRange(reader);
490
+ const tag = reader.u8();
491
+ if (tag !== 1 && tag !== 2) throw new TypeError("invalid tracked change kind");
492
+ return Object.freeze({
493
+ kind: "tracked-change.add",
494
+ id,
495
+ paragraphId,
496
+ range,
497
+ changeKind: tag === 1 ? "insert" : "delete",
498
+ author: readDocumentString(reader, state, false),
499
+ createdAt: readDocumentString(reader, state, false),
500
+ });
501
+ }
502
+ case 14:
503
+ return Object.freeze({
504
+ kind: "section.page.set",
505
+ id: readDocumentId(reader, "section"),
506
+ page: readPageWithExtras(reader),
507
+ });
508
+ default:
509
+ throw new TypeError("invalid document command tag");
510
+ }
511
+ }
512
+
513
+ const ID_PREFIX: Record<DocumentArtifactIdKind, { prefix: string; tag: number }> = {
514
+ paragraph: { prefix: "p", tag: 1 },
515
+ table: { prefix: "dt", tag: 2 },
516
+ "page-break": { prefix: "pb", tag: 3 },
517
+ section: { prefix: "sec", tag: 4 },
518
+ header: { prefix: "hdr", tag: 5 },
519
+ footer: { prefix: "ftr", tag: 6 },
520
+ comment: { prefix: "dc", tag: 7 },
521
+ "tracked-change": { prefix: "chg", tag: 8 },
522
+ };
523
+
524
+ function writeDocumentId(
525
+ writer: ArtifactBinaryWriter,
526
+ id: DocumentArtifactId,
527
+ kind: DocumentArtifactIdKind,
528
+ ): void {
529
+ const expected = ID_PREFIX[kind];
530
+ const match = new RegExp(`^${expected.prefix}/([0-9a-f]{16})([0-9a-f]{16})$`, "u").exec(id);
531
+ if (!match) throw new TypeError(`document ${kind} id is invalid`);
532
+ const counter = BigInt(`0x${match[2]}`);
533
+ if (counter === 0n || counter > BigInt(DOCUMENT_ARTIFACT_MAX_STRUCTURAL_COUNTER)) {
534
+ throw new TypeError(`document ${kind} id counter is invalid`);
535
+ }
536
+ writer.u8(expected.tag);
537
+ writer.u64(BigInt(`0x${match[1]}`));
538
+ writer.u64(counter);
539
+ }
540
+
541
+ function readDocumentId(
542
+ reader: ArtifactBinaryReader,
543
+ kind: DocumentArtifactIdKind,
544
+ ): DocumentArtifactId {
545
+ const expected = ID_PREFIX[kind];
546
+ if (reader.u8() !== expected.tag) throw new TypeError("document id kind mismatch");
547
+ const namespace = reader.u64BigInt().toString(16).padStart(16, "0");
548
+ const counterValue = reader.u64BigInt();
549
+ if (counterValue === 0n || counterValue > BigInt(DOCUMENT_ARTIFACT_MAX_STRUCTURAL_COUNTER)) {
550
+ throw new TypeError("document id counter is invalid");
551
+ }
552
+ return `${expected.prefix}/${namespace}${counterValue.toString(16).padStart(16, "0")}`;
553
+ }
554
+
555
+ function writeDocumentString(
556
+ writer: ArtifactBinaryWriter,
557
+ value: string,
558
+ state: CodecState,
559
+ content: boolean,
560
+ ): void {
561
+ const bytes = strictUtf8(value, "document string");
562
+ const utf16 = value.length;
563
+ if (
564
+ bytes.byteLength > DOCUMENT_ARTIFACT_COMMAND_MAX_STRING_BYTES ||
565
+ utf16 > DOCUMENT_ARTIFACT_MAX_TEXT_UTF16
566
+ ) {
567
+ throw new RangeError("document string exceeds its limit");
568
+ }
569
+ if (content) {
570
+ state.textUtf16 += utf16;
571
+ if (state.textUtf16 > DOCUMENT_ARTIFACT_MAX_TEXT_UTF16)
572
+ throw new RangeError("document text exceeds its UTF-16 limit");
573
+ }
574
+ writer.u32(bytes.byteLength);
575
+ writer.bytes(bytes);
576
+ }
577
+
578
+ function readDocumentString(
579
+ reader: ArtifactBinaryReader,
580
+ state: CodecState,
581
+ content: boolean,
582
+ ): string {
583
+ const value = reader.string(DOCUMENT_ARTIFACT_COMMAND_MAX_STRING_BYTES, "document string");
584
+ if (value.length > DOCUMENT_ARTIFACT_MAX_TEXT_UTF16)
585
+ throw new RangeError("document string exceeds its UTF-16 limit");
586
+ if (content) {
587
+ state.textUtf16 += value.length;
588
+ if (state.textUtf16 > DOCUMENT_ARTIFACT_MAX_TEXT_UTF16)
589
+ throw new RangeError("document text exceeds its UTF-16 limit");
590
+ }
591
+ return value;
592
+ }
593
+
594
+ function writeRuns(
595
+ writer: ArtifactBinaryWriter,
596
+ runs: readonly DocumentArtifactTextRun[],
597
+ state: CodecState,
598
+ ): void {
599
+ if (!Array.isArray(runs)) throw new TypeError("document runs must be an array");
600
+ state.runs += runs.length;
601
+ if (state.runs > DOCUMENT_ARTIFACT_MAX_TEXT_RUNS)
602
+ throw new RangeError("document text runs exceed their limit");
603
+ writer.count(runs.length, DOCUMENT_ARTIFACT_MAX_TEXT_RUNS, "document runs");
604
+ for (const run of runs) {
605
+ writeDocumentString(writer, run.text, state, true);
606
+ writeTextStyle(writer, run.style);
607
+ }
608
+ }
609
+
610
+ function readRuns(
611
+ reader: ArtifactBinaryReader,
612
+ state: CodecState,
613
+ ): readonly DocumentArtifactTextRun[] {
614
+ const count = reader.count(DOCUMENT_ARTIFACT_MAX_TEXT_RUNS, "document runs");
615
+ state.runs += count;
616
+ if (state.runs > DOCUMENT_ARTIFACT_MAX_TEXT_RUNS)
617
+ throw new RangeError("document text runs exceed their limit");
618
+ const runs: DocumentArtifactTextRun[] = [];
619
+ for (let index = 0; index < count; index += 1)
620
+ runs.push(
621
+ Object.freeze({
622
+ text: readDocumentString(reader, state, true),
623
+ style: readTextStyle(reader),
624
+ }),
625
+ );
626
+ return Object.freeze(runs);
627
+ }
628
+
629
+ function writeTextStyle(writer: ArtifactBinaryWriter, style: DocumentArtifactTextStyle): void {
630
+ const mask =
631
+ Number(style.fontFamily !== undefined) |
632
+ (Number(style.fontSizePt !== undefined) << 1) |
633
+ (Number(style.color !== undefined) << 2) |
634
+ (Number(style.bold !== undefined) << 3) |
635
+ (Number(style.italic !== undefined) << 4) |
636
+ (Number(style.underline !== undefined) << 5) |
637
+ (Number(style.strike !== undefined) << 6);
638
+ writer.u8(mask);
639
+ const dummy: CodecState = { runs: 0, textUtf16: 0, tableCells: 0 };
640
+ if (style.fontFamily !== undefined) writeDocumentString(writer, style.fontFamily, dummy, false);
641
+ if (style.fontSizePt !== undefined) writer.f64(style.fontSizePt, "document font size");
642
+ if (style.color !== undefined) writeDocumentString(writer, style.color, dummy, false);
643
+ for (const value of [style.bold, style.italic, style.underline, style.strike])
644
+ if (value !== undefined) writer.bool(value);
645
+ }
646
+
647
+ function readTextStyle(reader: ArtifactBinaryReader): DocumentArtifactTextStyle {
648
+ const mask = reader.u8();
649
+ if ((mask & ~0x7f) !== 0) throw new TypeError("unknown document text-style bits");
650
+ const state: CodecState = { runs: 0, textUtf16: 0, tableCells: 0 };
651
+ return Object.freeze({
652
+ ...(mask & 1 ? { fontFamily: readDocumentString(reader, state, false) } : {}),
653
+ ...(mask & 2 ? { fontSizePt: reader.f64("document font size") } : {}),
654
+ ...(mask & 4 ? { color: readDocumentString(reader, state, false) } : {}),
655
+ ...(mask & 8 ? { bold: reader.bool("document boolean") } : {}),
656
+ ...(mask & 16 ? { italic: reader.bool("document boolean") } : {}),
657
+ ...(mask & 32 ? { underline: reader.bool("document boolean") } : {}),
658
+ ...(mask & 64 ? { strike: reader.bool("document boolean") } : {}),
659
+ });
660
+ }
661
+
662
+ function writeTextStylePatch(
663
+ writer: ArtifactBinaryWriter,
664
+ patch: DocumentArtifactTextStylePatch,
665
+ ): void {
666
+ writePatchString(writer, patch.fontFamily);
667
+ writePatchNumber(writer, patch.fontSizePt);
668
+ writePatchString(writer, patch.color);
669
+ writePatchBool(writer, patch.bold);
670
+ writePatchBool(writer, patch.italic);
671
+ writePatchBool(writer, patch.underline);
672
+ writePatchBool(writer, patch.strike);
673
+ }
674
+
675
+ function readTextStylePatch(reader: ArtifactBinaryReader): DocumentArtifactTextStylePatch {
676
+ const fontFamily = readPatchString(reader);
677
+ const fontSizePt = readPatchNumber(reader);
678
+ const color = readPatchString(reader);
679
+ const bold = readPatchBool(reader);
680
+ const italic = readPatchBool(reader);
681
+ const underline = readPatchBool(reader);
682
+ const strike = readPatchBool(reader);
683
+ return freezeOptional(
684
+ {},
685
+ { fontFamily, fontSizePt, color, bold, italic, underline, strike },
686
+ ) as DocumentArtifactTextStylePatch;
687
+ }
688
+
689
+ function writePatchString(writer: ArtifactBinaryWriter, value: string | null | undefined): void {
690
+ if (value === undefined) writer.u8(0);
691
+ else if (value === null) writer.u8(1);
692
+ else {
693
+ writer.u8(2);
694
+ writeDocumentString(writer, value, { runs: 0, textUtf16: 0, tableCells: 0 }, false);
695
+ }
696
+ }
697
+
698
+ function readPatchString(reader: ArtifactBinaryReader): string | null | undefined {
699
+ const tag = reader.u8();
700
+ if (tag === 0) return undefined;
701
+ if (tag === 1) return null;
702
+ if (tag === 2) return readDocumentString(reader, { runs: 0, textUtf16: 0, tableCells: 0 }, false);
703
+ throw new TypeError("invalid document string patch");
704
+ }
705
+
706
+ function writePatchNumber(writer: ArtifactBinaryWriter, value: number | null | undefined): void {
707
+ if (value === undefined) writer.u8(0);
708
+ else if (value === null) writer.u8(1);
709
+ else {
710
+ writer.u8(2);
711
+ writer.f64(value, "document number patch");
712
+ }
713
+ }
714
+
715
+ function readPatchNumber(reader: ArtifactBinaryReader): number | null | undefined {
716
+ const tag = reader.u8();
717
+ if (tag === 0) return undefined;
718
+ if (tag === 1) return null;
719
+ if (tag === 2) return reader.f64("document number patch");
720
+ throw new TypeError("invalid document number patch");
721
+ }
722
+
723
+ function writePatchBool(writer: ArtifactBinaryWriter, value: boolean | null | undefined): void {
724
+ writer.u8(value === undefined ? 0 : value === null ? 1 : value ? 3 : 2);
725
+ }
726
+
727
+ function readPatchBool(reader: ArtifactBinaryReader): boolean | null | undefined {
728
+ const tag = reader.u8();
729
+ if (tag === 0) return undefined;
730
+ if (tag === 1) return null;
731
+ if (tag === 2) return false;
732
+ if (tag === 3) return true;
733
+ throw new TypeError("invalid document boolean patch");
734
+ }
735
+
736
+ function writeOptionalBool(writer: ArtifactBinaryWriter, value: boolean | null): void {
737
+ writer.u8(value === null ? 0 : value ? 2 : 1);
738
+ }
739
+
740
+ function readOptionalBool(reader: ArtifactBinaryReader): boolean | null {
741
+ const tag = reader.u8();
742
+ if (tag === 0) return null;
743
+ if (tag === 1) return false;
744
+ if (tag === 2) return true;
745
+ throw new TypeError("invalid optional document boolean");
746
+ }
747
+
748
+ function writeParagraphStyle(
749
+ writer: ArtifactBinaryWriter,
750
+ style: DocumentArtifactParagraphStyle,
751
+ ): void {
752
+ const mask =
753
+ Number(style.headingLevel !== undefined) |
754
+ (Number(style.alignment !== undefined) << 1) |
755
+ (Number(style.spaceBeforePt !== undefined) << 2) |
756
+ (Number(style.spaceAfterPt !== undefined) << 3) |
757
+ (Number(style.lineHeight !== undefined) << 4) |
758
+ (Number(style.keepNext !== undefined) << 5) |
759
+ (Number(style.pageBreakBefore !== undefined) << 6) |
760
+ (Number(style.list !== undefined) << 7);
761
+ writer.u16(mask);
762
+ if (style.headingLevel !== undefined) writer.u8(style.headingLevel);
763
+ if (style.alignment !== undefined)
764
+ writer.u8(
765
+ ({ left: 1, center: 2, right: 3, justify: 4 } as const)[style.alignment] ??
766
+ invalidTag("paragraph alignment"),
767
+ );
768
+ if (style.spaceBeforePt !== undefined)
769
+ writer.f64(style.spaceBeforePt, "document paragraph spacing");
770
+ if (style.spaceAfterPt !== undefined)
771
+ writer.f64(style.spaceAfterPt, "document paragraph spacing");
772
+ if (style.lineHeight !== undefined) writer.f64(style.lineHeight, "document line height");
773
+ if (style.keepNext !== undefined) writer.bool(style.keepNext);
774
+ if (style.pageBreakBefore !== undefined) writer.bool(style.pageBreakBefore);
775
+ if (style.list !== undefined) {
776
+ writer.u8(
777
+ style.list.kind === "bullet" ? 1 : style.list.kind === "number" ? 2 : invalidTag("list kind"),
778
+ );
779
+ writer.bool(style.list.level !== null);
780
+ if (style.list.level !== null) writer.u8(style.list.level);
781
+ writer.bool(style.list.instanceId !== null);
782
+ if (style.list.instanceId !== null)
783
+ writeDocumentString(
784
+ writer,
785
+ style.list.instanceId,
786
+ { runs: 0, textUtf16: 0, tableCells: 0 },
787
+ false,
788
+ );
789
+ }
790
+ }
791
+
792
+ function readParagraphStyle(reader: ArtifactBinaryReader): DocumentArtifactParagraphStyle {
793
+ const mask = reader.u16();
794
+ if ((mask & ~0xff) !== 0) throw new TypeError("unknown paragraph-style bits");
795
+ const headingLevel = mask & 1 ? reader.u8() : undefined;
796
+ let alignment: DocumentArtifactParagraphStyle["alignment"];
797
+ if (mask & 2) {
798
+ const tag = reader.u8();
799
+ alignment =
800
+ tag === 1
801
+ ? "left"
802
+ : tag === 2
803
+ ? "center"
804
+ : tag === 3
805
+ ? "right"
806
+ : tag === 4
807
+ ? "justify"
808
+ : invalidTag("paragraph alignment");
809
+ }
810
+ const spaceBeforePt = mask & 4 ? reader.f64("document paragraph spacing") : undefined;
811
+ const spaceAfterPt = mask & 8 ? reader.f64("document paragraph spacing") : undefined;
812
+ const lineHeight = mask & 16 ? reader.f64("document line height") : undefined;
813
+ const keepNext = mask & 32 ? reader.bool("document boolean") : undefined;
814
+ const pageBreakBefore = mask & 64 ? reader.bool("document boolean") : undefined;
815
+ let list: DocumentArtifactParagraphStyle["list"];
816
+ if (mask & 128) {
817
+ const tag = reader.u8();
818
+ const kind = tag === 1 ? "bullet" : tag === 2 ? "number" : invalidTag("list kind");
819
+ list = Object.freeze({
820
+ kind,
821
+ level: reader.bool("document list level presence") ? reader.u8() : null,
822
+ instanceId: reader.bool("optional document string")
823
+ ? readDocumentString(reader, { runs: 0, textUtf16: 0, tableCells: 0 }, false)
824
+ : null,
825
+ });
826
+ }
827
+ return Object.freeze({
828
+ ...(headingLevel !== undefined ? { headingLevel } : {}),
829
+ ...(alignment !== undefined ? { alignment } : {}),
830
+ ...(spaceBeforePt !== undefined ? { spaceBeforePt } : {}),
831
+ ...(spaceAfterPt !== undefined ? { spaceAfterPt } : {}),
832
+ ...(lineHeight !== undefined ? { lineHeight } : {}),
833
+ ...(keepNext !== undefined ? { keepNext } : {}),
834
+ ...(pageBreakBefore !== undefined ? { pageBreakBefore } : {}),
835
+ ...(list !== undefined ? { list } : {}),
836
+ });
837
+ }
838
+
839
+ function writeTableStyle(writer: ArtifactBinaryWriter, style: DocumentArtifactTableStyle): void {
840
+ const mask =
841
+ Number(style.widthPt !== undefined) |
842
+ (Number(style.columnWidthsPt !== undefined) << 1) |
843
+ (Number(style.headerRows !== undefined) << 2) |
844
+ (Number(style.cellPaddingPt !== undefined) << 3) |
845
+ (Number(style.borderColor !== undefined) << 4) |
846
+ (Number(style.headerFill !== undefined) << 5) |
847
+ (Number(style.allowRowSplit !== undefined) << 6);
848
+ writer.u8(mask);
849
+ if (style.widthPt !== undefined) writer.f64(style.widthPt, "document table width");
850
+ if (style.columnWidthsPt !== undefined) {
851
+ writer.count(
852
+ style.columnWidthsPt.length,
853
+ DOCUMENT_ARTIFACT_MAX_TABLE_CELLS,
854
+ "document table widths",
855
+ );
856
+ for (const width of style.columnWidthsPt) writer.f64(width, "document column width");
857
+ }
858
+ if (style.headerRows !== undefined) writer.u32(style.headerRows);
859
+ if (style.cellPaddingPt !== undefined) writer.f64(style.cellPaddingPt, "document cell padding");
860
+ if (style.borderColor !== undefined)
861
+ writeDocumentString(writer, style.borderColor, { runs: 0, textUtf16: 0, tableCells: 0 }, false);
862
+ if (style.headerFill !== undefined)
863
+ writeDocumentString(writer, style.headerFill, { runs: 0, textUtf16: 0, tableCells: 0 }, false);
864
+ if (style.allowRowSplit !== undefined) writer.bool(style.allowRowSplit);
865
+ }
866
+
867
+ function readTableStyle(reader: ArtifactBinaryReader): DocumentArtifactTableStyle {
868
+ const mask = reader.u8();
869
+ if ((mask & ~0x7f) !== 0) throw new TypeError("unknown document table-style bits");
870
+ const widthPt = mask & 1 ? reader.f64("document table width") : undefined;
871
+ let columnWidthsPt: readonly number[] | undefined;
872
+ if (mask & 2) {
873
+ const count = reader.count(DOCUMENT_ARTIFACT_MAX_TABLE_CELLS, "document table widths");
874
+ const widths: number[] = [];
875
+ for (let index = 0; index < count; index += 1) widths.push(reader.f64("document column width"));
876
+ columnWidthsPt = Object.freeze(widths);
877
+ }
878
+ const headerRows = mask & 4 ? reader.u32() : undefined;
879
+ const cellPaddingPt = mask & 8 ? reader.f64("document cell padding") : undefined;
880
+ const state: CodecState = { runs: 0, textUtf16: 0, tableCells: 0 };
881
+ const borderColor = mask & 16 ? readDocumentString(reader, state, false) : undefined;
882
+ const headerFill = mask & 32 ? readDocumentString(reader, state, false) : undefined;
883
+ const allowRowSplit = mask & 64 ? reader.bool("document boolean") : undefined;
884
+ return Object.freeze({
885
+ ...(widthPt !== undefined ? { widthPt } : {}),
886
+ ...(columnWidthsPt ? { columnWidthsPt } : {}),
887
+ ...(headerRows !== undefined ? { headerRows } : {}),
888
+ ...(cellPaddingPt !== undefined ? { cellPaddingPt } : {}),
889
+ ...(borderColor !== undefined ? { borderColor } : {}),
890
+ ...(headerFill !== undefined ? { headerFill } : {}),
891
+ ...(allowRowSplit !== undefined ? { allowRowSplit } : {}),
892
+ });
893
+ }
894
+
895
+ function writeTableRows(
896
+ writer: ArtifactBinaryWriter,
897
+ rows: readonly (readonly (readonly DocumentArtifactTextRun[])[])[],
898
+ state: CodecState,
899
+ ): void {
900
+ writer.count(rows.length, DOCUMENT_ARTIFACT_MAX_TABLE_CELLS, "document table rows");
901
+ for (const row of rows) {
902
+ state.tableCells += row.length;
903
+ if (state.tableCells > DOCUMENT_ARTIFACT_MAX_TABLE_CELLS)
904
+ throw new RangeError("document table cells exceed their limit");
905
+ writer.count(row.length, DOCUMENT_ARTIFACT_MAX_TABLE_CELLS, "document table cells");
906
+ for (const cell of row) writeRuns(writer, cell, state);
907
+ }
908
+ }
909
+
910
+ function readTableRows(
911
+ reader: ArtifactBinaryReader,
912
+ state: CodecState,
913
+ ): readonly (readonly (readonly DocumentArtifactTextRun[])[])[] {
914
+ const rowCount = reader.count(DOCUMENT_ARTIFACT_MAX_TABLE_CELLS, "document table rows");
915
+ const rows: (readonly (readonly DocumentArtifactTextRun[])[])[] = [];
916
+ for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
917
+ const columnCount = reader.count(DOCUMENT_ARTIFACT_MAX_TABLE_CELLS, "document table cells");
918
+ state.tableCells += columnCount;
919
+ if (state.tableCells > DOCUMENT_ARTIFACT_MAX_TABLE_CELLS)
920
+ throw new RangeError("document table cells exceed their limit");
921
+ const row: (readonly DocumentArtifactTextRun[])[] = [];
922
+ for (let columnIndex = 0; columnIndex < columnCount; columnIndex += 1)
923
+ row.push(readRuns(reader, state));
924
+ rows.push(Object.freeze(row));
925
+ }
926
+ return Object.freeze(rows);
927
+ }
928
+
929
+ function writeTarget(writer: ArtifactBinaryWriter, target: DocumentArtifactStoryTarget): void {
930
+ if (target.kind === "body") {
931
+ writer.u8(0);
932
+ return;
933
+ }
934
+ if (target.kind !== "section") throw new TypeError("invalid document story target");
935
+ writer.u8(1);
936
+ writeDocumentId(writer, target.sectionId, "section");
937
+ writer.u8(
938
+ target.storyKind === "header"
939
+ ? 1
940
+ : target.storyKind === "footer"
941
+ ? 2
942
+ : invalidTag("story kind"),
943
+ );
944
+ writer.u8(
945
+ target.variant === "default"
946
+ ? 1
947
+ : target.variant === "first"
948
+ ? 2
949
+ : target.variant === "even"
950
+ ? 3
951
+ : invalidTag("story variant"),
952
+ );
953
+ }
954
+
955
+ function readTarget(reader: ArtifactBinaryReader): DocumentArtifactStoryTarget {
956
+ const tag = reader.u8();
957
+ if (tag === 0) return Object.freeze({ kind: "body" });
958
+ if (tag !== 1) throw new TypeError("invalid document story target");
959
+ const sectionId = readDocumentId(reader, "section");
960
+ const kindTag = reader.u8();
961
+ const storyKind = kindTag === 1 ? "header" : kindTag === 2 ? "footer" : invalidTag("story kind");
962
+ const variantTag = reader.u8();
963
+ const variant =
964
+ variantTag === 1
965
+ ? "default"
966
+ : variantTag === 2
967
+ ? "first"
968
+ : variantTag === 3
969
+ ? "even"
970
+ : invalidTag("story variant");
971
+ return Object.freeze({ kind: "section", sectionId, storyKind, variant });
972
+ }
973
+
974
+ function writeRange(writer: ArtifactBinaryWriter, range: DocumentArtifactTextRange): void {
975
+ if (range.end < range.start) throw new TypeError("document text range is invalid");
976
+ writer.u32(range.start);
977
+ writer.u32(range.end);
978
+ }
979
+
980
+ function readRange(reader: ArtifactBinaryReader): DocumentArtifactTextRange {
981
+ const start = reader.u32();
982
+ const end = reader.u32();
983
+ if (end < start) throw new TypeError("document text range is invalid");
984
+ return Object.freeze({ start, end });
985
+ }
986
+
987
+ function writePage(writer: ArtifactBinaryWriter, page: DocumentArtifactPageGeometry): void {
988
+ for (const [value, label] of [
989
+ [page.widthPt, "width"],
990
+ [page.heightPt, "height"],
991
+ [page.marginTopPt, "top margin"],
992
+ [page.marginRightPt, "right margin"],
993
+ [page.marginBottomPt, "bottom margin"],
994
+ [page.marginLeftPt, "left margin"],
995
+ ] as const)
996
+ writer.f64(value, `document ${label}`);
997
+ }
998
+
999
+ function writePageWithExtras(
1000
+ writer: ArtifactBinaryWriter,
1001
+ page: DocumentArtifactPageGeometry,
1002
+ ): void {
1003
+ writePage(writer, page);
1004
+ writer.f64(page.headerPt ?? 36, "document header distance");
1005
+ writer.f64(page.footerPt ?? 36, "document footer distance");
1006
+ writer.f64(page.gutterPt ?? 0, "document gutter");
1007
+ }
1008
+
1009
+ function assertDefaultPageExtras(page: DocumentArtifactPageGeometry): void {
1010
+ if ((page.headerPt ?? 36) !== 36 || (page.footerPt ?? 36) !== 36 || (page.gutterPt ?? 0) !== 0) {
1011
+ throw new TypeError("section.add page extras require section.page.set");
1012
+ }
1013
+ }
1014
+
1015
+ function readPage(reader: ArtifactBinaryReader): DocumentArtifactPageGeometry {
1016
+ return Object.freeze({
1017
+ widthPt: reader.f64("document width"),
1018
+ heightPt: reader.f64("document height"),
1019
+ marginTopPt: reader.f64("document top margin"),
1020
+ marginRightPt: reader.f64("document right margin"),
1021
+ marginBottomPt: reader.f64("document bottom margin"),
1022
+ marginLeftPt: reader.f64("document left margin"),
1023
+ });
1024
+ }
1025
+
1026
+ function readPageWithExtras(reader: ArtifactBinaryReader): DocumentArtifactPageGeometry {
1027
+ return Object.freeze({
1028
+ ...readPage(reader),
1029
+ headerPt: reader.f64("document header distance"),
1030
+ footerPt: reader.f64("document footer distance"),
1031
+ gutterPt: reader.f64("document gutter"),
1032
+ });
1033
+ }
1034
+
1035
+ function writeCommentReply(
1036
+ writer: ArtifactBinaryWriter,
1037
+ reply: DocumentArtifactCommentReply,
1038
+ state: CodecState,
1039
+ ): void {
1040
+ writeDocumentString(writer, reply.author, state, false);
1041
+ writeDocumentString(writer, reply.text, state, true);
1042
+ writeDocumentString(writer, reply.createdAt, state, false);
1043
+ }
1044
+
1045
+ function readCommentReply(
1046
+ reader: ArtifactBinaryReader,
1047
+ state: CodecState,
1048
+ ): DocumentArtifactCommentReply {
1049
+ return Object.freeze({
1050
+ author: readDocumentString(reader, state, false),
1051
+ text: readDocumentString(reader, state, true),
1052
+ createdAt: readDocumentString(reader, state, false),
1053
+ });
1054
+ }
1055
+
1056
+ export type DocumentArtifactQueryLimits = Readonly<{
1057
+ maxItems: number;
1058
+ maxTextUtf16: number;
1059
+ maxTableCells: number;
1060
+ }>;
1061
+
1062
+ export type DocumentArtifactQuery =
1063
+ | Readonly<{ kind: "summary" }>
1064
+ | Readonly<{ kind: "body"; startBlock: number; limits: DocumentArtifactQueryLimits }>
1065
+ | Readonly<{
1066
+ kind: "story";
1067
+ sectionId: DocumentArtifactId;
1068
+ storyKind: "header" | "footer";
1069
+ variant: "default" | "first" | "even";
1070
+ startBlock: number;
1071
+ limits: DocumentArtifactQueryLimits;
1072
+ }>
1073
+ | Readonly<{ kind: "sections"; startSection: number; limits: DocumentArtifactQueryLimits }>
1074
+ | Readonly<{ kind: "review"; startItem: number; limits: DocumentArtifactQueryLimits }>;
1075
+
1076
+ export type DocumentArtifactPageGeometryProjection = Readonly<{
1077
+ widthMillipoints: bigint;
1078
+ heightMillipoints: bigint;
1079
+ marginTopMillipoints: bigint;
1080
+ marginRightMillipoints: bigint;
1081
+ marginBottomMillipoints: bigint;
1082
+ marginLeftMillipoints: bigint;
1083
+ headerMillipoints: bigint;
1084
+ footerMillipoints: bigint;
1085
+ gutterMillipoints: bigint;
1086
+ }>;
1087
+
1088
+ export type DocumentArtifactProjectionItem =
1089
+ | Readonly<{
1090
+ kind: "summary";
1091
+ idNamespace: bigint;
1092
+ revision: bigint;
1093
+ nextIdCounter: bigint;
1094
+ blockCount: number;
1095
+ sectionCount: number;
1096
+ commentCount: number;
1097
+ trackedChangeCount: number;
1098
+ evenAndOddHeaders: boolean;
1099
+ trackRevisions: boolean;
1100
+ page: DocumentArtifactPageGeometryProjection;
1101
+ }>
1102
+ | Readonly<{
1103
+ kind: "section";
1104
+ id: DocumentArtifactId;
1105
+ startBlockIndex: number;
1106
+ titlePage: boolean;
1107
+ page: DocumentArtifactPageGeometryProjection;
1108
+ headerBlockCounts: readonly [number, number, number];
1109
+ footerBlockCounts: readonly [number, number, number];
1110
+ }>
1111
+ | Readonly<{
1112
+ kind: "paragraph";
1113
+ id: DocumentArtifactId;
1114
+ runs: readonly DocumentArtifactTextRun[];
1115
+ style: DocumentArtifactParagraphStyle;
1116
+ }>
1117
+ | Readonly<{
1118
+ kind: "table";
1119
+ id: DocumentArtifactId;
1120
+ rows: readonly (readonly (readonly DocumentArtifactTextRun[])[])[];
1121
+ style: DocumentArtifactTableStyle;
1122
+ }>
1123
+ | Readonly<{ kind: "page-break"; id: DocumentArtifactId }>
1124
+ | Readonly<{
1125
+ kind: "comment";
1126
+ id: DocumentArtifactId;
1127
+ paragraphId: DocumentArtifactId;
1128
+ range: DocumentArtifactTextRange;
1129
+ resolved: boolean;
1130
+ replies: readonly DocumentArtifactCommentReply[];
1131
+ }>
1132
+ | Readonly<{
1133
+ kind: "tracked-change";
1134
+ id: DocumentArtifactId;
1135
+ paragraphId: DocumentArtifactId;
1136
+ changeKind: "insert" | "delete";
1137
+ range: DocumentArtifactTextRange;
1138
+ author: string;
1139
+ createdAt: string;
1140
+ }>;
1141
+
1142
+ export type DocumentArtifactProjection = Readonly<{
1143
+ revision: bigint;
1144
+ items: readonly DocumentArtifactProjectionItem[];
1145
+ nextCursor: number | null;
1146
+ truncated: boolean;
1147
+ projectedTextUtf16: number;
1148
+ projectedTableCells: number;
1149
+ }>;
1150
+
1151
+ export function encodeDocumentArtifactQuery(query: DocumentArtifactQuery): Uint8Array {
1152
+ const payload = new ArtifactBinaryWriter(DOCUMENT_ARTIFACT_QUERY_MAX_BYTES - 32);
1153
+ switch (query.kind) {
1154
+ case "summary":
1155
+ payload.u8(0);
1156
+ break;
1157
+ case "body":
1158
+ validateDocumentQueryLimits(query.limits);
1159
+ payload.u8(1);
1160
+ payload.u64(query.startBlock);
1161
+ writeQueryLimits(payload, query.limits);
1162
+ break;
1163
+ case "story":
1164
+ validateDocumentQueryLimits(query.limits);
1165
+ payload.u8(2);
1166
+ writeDocumentId(payload, query.sectionId, "section");
1167
+ payload.u8(
1168
+ query.storyKind === "header"
1169
+ ? 1
1170
+ : query.storyKind === "footer"
1171
+ ? 2
1172
+ : invalidTag("story kind"),
1173
+ );
1174
+ payload.u8(
1175
+ query.variant === "default"
1176
+ ? 1
1177
+ : query.variant === "first"
1178
+ ? 2
1179
+ : query.variant === "even"
1180
+ ? 3
1181
+ : invalidTag("story variant"),
1182
+ );
1183
+ payload.u64(query.startBlock);
1184
+ writeQueryLimits(payload, query.limits);
1185
+ break;
1186
+ case "sections":
1187
+ validateDocumentQueryLimits(query.limits);
1188
+ payload.u8(3);
1189
+ payload.u64(query.startSection);
1190
+ writeQueryLimits(payload, query.limits);
1191
+ break;
1192
+ case "review":
1193
+ validateDocumentQueryLimits(query.limits);
1194
+ payload.u8(4);
1195
+ payload.u64(query.startItem);
1196
+ writeQueryLimits(payload, query.limits);
1197
+ break;
1198
+ default:
1199
+ throw new TypeError("invalid document query kind");
1200
+ }
1201
+ return encodeCountedEnvelope(
1202
+ QUERY_MAGIC,
1203
+ DOCUMENT_ARTIFACT_QUERY_VERSION,
1204
+ 1,
1205
+ payload.finish(),
1206
+ DOCUMENT_ARTIFACT_QUERY_MAX_BYTES,
1207
+ );
1208
+ }
1209
+
1210
+ export function decodeDocumentArtifactQuery(bytes: Uint8Array): DocumentArtifactQuery {
1211
+ const { count, payload } = decodeCountedEnvelope(
1212
+ bytes,
1213
+ QUERY_MAGIC,
1214
+ DOCUMENT_ARTIFACT_QUERY_VERSION,
1215
+ DOCUMENT_ARTIFACT_QUERY_MAX_BYTES,
1216
+ 1,
1217
+ );
1218
+ if (count !== 1) throw new TypeError("document query envelope must contain exactly one query");
1219
+ const reader = new ArtifactBinaryReader(payload);
1220
+ const tag = reader.u8();
1221
+ let query: DocumentArtifactQuery;
1222
+ if (tag === 0) query = Object.freeze({ kind: "summary" });
1223
+ else if (tag === 1)
1224
+ query = Object.freeze({
1225
+ kind: "body",
1226
+ startBlock: reader.u64Safe("document body cursor"),
1227
+ limits: readQueryLimits(reader),
1228
+ });
1229
+ else if (tag === 2) {
1230
+ const sectionId = readDocumentId(reader, "section");
1231
+ const storyKindTag = reader.u8();
1232
+ const variantTag = reader.u8();
1233
+ query = Object.freeze({
1234
+ kind: "story",
1235
+ sectionId,
1236
+ storyKind:
1237
+ storyKindTag === 1 ? "header" : storyKindTag === 2 ? "footer" : invalidTag("story kind"),
1238
+ variant:
1239
+ variantTag === 1
1240
+ ? "default"
1241
+ : variantTag === 2
1242
+ ? "first"
1243
+ : variantTag === 3
1244
+ ? "even"
1245
+ : invalidTag("story variant"),
1246
+ startBlock: reader.u64Safe("document story cursor"),
1247
+ limits: readQueryLimits(reader),
1248
+ });
1249
+ } else if (tag === 3)
1250
+ query = Object.freeze({
1251
+ kind: "sections",
1252
+ startSection: reader.u64Safe("document section cursor"),
1253
+ limits: readQueryLimits(reader),
1254
+ });
1255
+ else if (tag === 4)
1256
+ query = Object.freeze({
1257
+ kind: "review",
1258
+ startItem: reader.u64Safe("document review cursor"),
1259
+ limits: readQueryLimits(reader),
1260
+ });
1261
+ else throw new TypeError("invalid document query tag");
1262
+ reader.done("document query contains trailing bytes");
1263
+ return query;
1264
+ }
1265
+
1266
+ export function assertCanonicalDocumentArtifactQueryBytes(bytes: Uint8Array): void {
1267
+ if (!equalBytes(bytes, encodeDocumentArtifactQuery(decodeDocumentArtifactQuery(bytes)))) {
1268
+ throw new TypeError("document query is not canonically encoded");
1269
+ }
1270
+ }
1271
+
1272
+ export function encodeDocumentArtifactQueryResponse(
1273
+ projection: DocumentArtifactProjection,
1274
+ ): Uint8Array {
1275
+ if (projection.items.length > DOCUMENT_ARTIFACT_QUERY_MAX_ITEMS)
1276
+ throw new RangeError("document query response has too many items");
1277
+ if (projection.truncated !== (projection.nextCursor !== null))
1278
+ throw new TypeError("document query cursor and truncation flag disagree");
1279
+ const metrics = measureProjection(projection.items);
1280
+ if (
1281
+ metrics.text !== projection.projectedTextUtf16 ||
1282
+ metrics.cells !== projection.projectedTableCells
1283
+ ) {
1284
+ throw new TypeError("document query response metrics do not match its items");
1285
+ }
1286
+ const payload = new ArtifactBinaryWriter(DOCUMENT_ARTIFACT_QUERY_RESPONSE_MAX_BYTES - 32);
1287
+ payload.u64(projection.revision);
1288
+ payload.bool(projection.nextCursor !== null);
1289
+ if (projection.nextCursor !== null) payload.u64(projection.nextCursor);
1290
+ payload.bool(projection.truncated);
1291
+ payload.u64(projection.projectedTextUtf16);
1292
+ payload.u64(projection.projectedTableCells);
1293
+ const state: CodecState = { runs: 0, textUtf16: 0, tableCells: 0 };
1294
+ for (const item of projection.items) writeProjectionItem(payload, item, state);
1295
+ const pageExtras = projection.items
1296
+ .map((item, index) => ({ index, page: projectionPage(item) }))
1297
+ .filter(
1298
+ (entry): entry is { index: number; page: DocumentArtifactPageGeometryProjection } =>
1299
+ entry.page !== null && hasNonDefaultProjectionPageExtras(entry.page),
1300
+ );
1301
+ if (pageExtras.length > 0) {
1302
+ payload.u8(QUERY_RESPONSE_EXTENSION_MARKER);
1303
+ payload.u8(PAGE_EXTRAS_EXTENSION_VERSION);
1304
+ payload.count(pageExtras.length, DOCUMENT_ARTIFACT_QUERY_MAX_ITEMS, "document page extras");
1305
+ for (const { index, page } of pageExtras) {
1306
+ payload.count(index, DOCUMENT_ARTIFACT_QUERY_MAX_ITEMS - 1, "document page item index");
1307
+ payload.i64(page.headerMillipoints);
1308
+ payload.i64(page.footerMillipoints);
1309
+ payload.i64(page.gutterMillipoints);
1310
+ }
1311
+ }
1312
+ return encodeCountedEnvelope(
1313
+ QUERY_RESPONSE_MAGIC,
1314
+ DOCUMENT_ARTIFACT_QUERY_RESPONSE_VERSION,
1315
+ projection.items.length,
1316
+ payload.finish(),
1317
+ DOCUMENT_ARTIFACT_QUERY_RESPONSE_MAX_BYTES,
1318
+ );
1319
+ }
1320
+
1321
+ export function decodeDocumentArtifactQueryResponse(bytes: Uint8Array): DocumentArtifactProjection {
1322
+ const { count, payload } = decodeCountedEnvelope(
1323
+ bytes,
1324
+ QUERY_RESPONSE_MAGIC,
1325
+ DOCUMENT_ARTIFACT_QUERY_RESPONSE_VERSION,
1326
+ DOCUMENT_ARTIFACT_QUERY_RESPONSE_MAX_BYTES,
1327
+ DOCUMENT_ARTIFACT_QUERY_MAX_ITEMS,
1328
+ );
1329
+ const reader = new ArtifactBinaryReader(payload);
1330
+ const revision = reader.u64BigInt();
1331
+ const nextCursor = reader.bool("optional document cursor")
1332
+ ? reader.u64Safe("document query cursor")
1333
+ : null;
1334
+ const truncated = reader.bool("document truncation flag");
1335
+ if (truncated !== (nextCursor !== null))
1336
+ throw new TypeError("document query cursor and truncation flag disagree");
1337
+ const projectedTextUtf16 = reader.u64Safe("document projected text");
1338
+ const projectedTableCells = reader.u64Safe("document projected table cells");
1339
+ if (
1340
+ projectedTextUtf16 > DOCUMENT_ARTIFACT_QUERY_MAX_TEXT_UTF16 ||
1341
+ projectedTableCells > DOCUMENT_ARTIFACT_QUERY_MAX_TABLE_CELLS
1342
+ ) {
1343
+ throw new RangeError("document query response metrics exceed their limits");
1344
+ }
1345
+ const state: CodecState = { runs: 0, textUtf16: 0, tableCells: 0 };
1346
+ let items: DocumentArtifactProjectionItem[] = [];
1347
+ for (let index = 0; index < count; index += 1) items.push(readProjectionItem(reader, state));
1348
+ if (reader.remaining > 0) items = readProjectionPageExtras(reader, items);
1349
+ reader.done("document query response contains trailing bytes");
1350
+ const measured = measureProjection(items);
1351
+ if (measured.text !== projectedTextUtf16 || measured.cells !== projectedTableCells) {
1352
+ throw new TypeError("document query response metrics do not match its items");
1353
+ }
1354
+ const projection = Object.freeze({
1355
+ revision,
1356
+ items: Object.freeze(items),
1357
+ nextCursor,
1358
+ truncated,
1359
+ projectedTextUtf16,
1360
+ projectedTableCells,
1361
+ });
1362
+ if (!equalBytes(bytes, encodeDocumentArtifactQueryResponse(projection))) {
1363
+ throw new TypeError("document query response is not canonically encoded");
1364
+ }
1365
+ return projection;
1366
+ }
1367
+
1368
+ export function assertCanonicalDocumentArtifactQueryResponseBytes(bytes: Uint8Array): void {
1369
+ decodeDocumentArtifactQueryResponse(bytes);
1370
+ }
1371
+
1372
+ function writeQueryLimits(writer: ArtifactBinaryWriter, limits: DocumentArtifactQueryLimits): void {
1373
+ validateDocumentQueryLimits(limits);
1374
+ writer.u32(limits.maxItems);
1375
+ writer.u32(limits.maxTextUtf16);
1376
+ writer.u32(limits.maxTableCells);
1377
+ }
1378
+
1379
+ function readQueryLimits(reader: ArtifactBinaryReader): DocumentArtifactQueryLimits {
1380
+ const limits = Object.freeze({
1381
+ maxItems: reader.u32(),
1382
+ maxTextUtf16: reader.u32(),
1383
+ maxTableCells: reader.u32(),
1384
+ });
1385
+ validateDocumentQueryLimits(limits);
1386
+ return limits;
1387
+ }
1388
+
1389
+ function validateDocumentQueryLimits(limits: DocumentArtifactQueryLimits): void {
1390
+ if (
1391
+ !Number.isInteger(limits.maxItems) ||
1392
+ limits.maxItems < 1 ||
1393
+ limits.maxItems > DOCUMENT_ARTIFACT_QUERY_MAX_ITEMS ||
1394
+ !Number.isInteger(limits.maxTextUtf16) ||
1395
+ limits.maxTextUtf16 < 1 ||
1396
+ limits.maxTextUtf16 > DOCUMENT_ARTIFACT_QUERY_MAX_TEXT_UTF16 ||
1397
+ !Number.isInteger(limits.maxTableCells) ||
1398
+ limits.maxTableCells < 1 ||
1399
+ limits.maxTableCells > DOCUMENT_ARTIFACT_QUERY_MAX_TABLE_CELLS
1400
+ ) {
1401
+ throw new RangeError("invalid document query limits");
1402
+ }
1403
+ }
1404
+
1405
+ function writePageProjection(
1406
+ writer: ArtifactBinaryWriter,
1407
+ page: DocumentArtifactPageGeometryProjection,
1408
+ ): void {
1409
+ writer.i64(page.widthMillipoints);
1410
+ writer.i64(page.heightMillipoints);
1411
+ writer.i64(page.marginTopMillipoints);
1412
+ writer.i64(page.marginRightMillipoints);
1413
+ writer.i64(page.marginBottomMillipoints);
1414
+ writer.i64(page.marginLeftMillipoints);
1415
+ }
1416
+
1417
+ function projectionPage(
1418
+ item: DocumentArtifactProjectionItem,
1419
+ ): DocumentArtifactPageGeometryProjection | null {
1420
+ return item.kind === "summary" || item.kind === "section" ? item.page : null;
1421
+ }
1422
+
1423
+ function hasNonDefaultProjectionPageExtras(page: DocumentArtifactPageGeometryProjection): boolean {
1424
+ return (
1425
+ page.headerMillipoints !== 36_000n ||
1426
+ page.footerMillipoints !== 36_000n ||
1427
+ page.gutterMillipoints !== 0n
1428
+ );
1429
+ }
1430
+
1431
+ function readPageProjection(reader: ArtifactBinaryReader): DocumentArtifactPageGeometryProjection {
1432
+ return Object.freeze({
1433
+ widthMillipoints: reader.i64BigInt(),
1434
+ heightMillipoints: reader.i64BigInt(),
1435
+ marginTopMillipoints: reader.i64BigInt(),
1436
+ marginRightMillipoints: reader.i64BigInt(),
1437
+ marginBottomMillipoints: reader.i64BigInt(),
1438
+ marginLeftMillipoints: reader.i64BigInt(),
1439
+ headerMillipoints: 36_000n,
1440
+ footerMillipoints: 36_000n,
1441
+ gutterMillipoints: 0n,
1442
+ });
1443
+ }
1444
+
1445
+ function readProjectionPageExtras(
1446
+ reader: ArtifactBinaryReader,
1447
+ source: readonly DocumentArtifactProjectionItem[],
1448
+ ): DocumentArtifactProjectionItem[] {
1449
+ if (source.length === 0) throw new TypeError("document page extras require a page item");
1450
+ if (reader.u8() !== QUERY_RESPONSE_EXTENSION_MARKER) {
1451
+ throw new TypeError("unknown document query response extension");
1452
+ }
1453
+ if (reader.u8() !== PAGE_EXTRAS_EXTENSION_VERSION) {
1454
+ throw new TypeError("unsupported document page extras extension version");
1455
+ }
1456
+ const count = reader.count(source.length, "document page extras", 28);
1457
+ if (count === 0) throw new TypeError("empty document page extras extension");
1458
+ const items = [...source];
1459
+ let previousIndex = -1;
1460
+ for (let offset = 0; offset < count; offset += 1) {
1461
+ const index = reader.count(source.length - 1, "document page item index", 24);
1462
+ if (index <= previousIndex)
1463
+ throw new TypeError("document page extras are not strictly ordered");
1464
+ previousIndex = index;
1465
+ const item = items[index];
1466
+ if (!item || (item.kind !== "summary" && item.kind !== "section")) {
1467
+ throw new TypeError("document page extras reference a non-page item");
1468
+ }
1469
+ const page = Object.freeze({
1470
+ ...item.page,
1471
+ headerMillipoints: reader.i64BigInt(),
1472
+ footerMillipoints: reader.i64BigInt(),
1473
+ gutterMillipoints: reader.i64BigInt(),
1474
+ });
1475
+ if (!hasNonDefaultProjectionPageExtras(page)) {
1476
+ throw new TypeError("redundant document page extras");
1477
+ }
1478
+ items[index] = Object.freeze({ ...item, page });
1479
+ }
1480
+ return items;
1481
+ }
1482
+
1483
+ function writeProjectionItem(
1484
+ writer: ArtifactBinaryWriter,
1485
+ item: DocumentArtifactProjectionItem,
1486
+ state: CodecState,
1487
+ ): void {
1488
+ switch (item.kind) {
1489
+ case "summary":
1490
+ writer.u8(0);
1491
+ writer.u64(item.idNamespace);
1492
+ writer.u64(item.revision);
1493
+ writer.u64(item.nextIdCounter);
1494
+ writer.u64(item.blockCount);
1495
+ writer.u64(item.sectionCount);
1496
+ writer.u64(item.commentCount);
1497
+ writer.u64(item.trackedChangeCount);
1498
+ writer.bool(item.evenAndOddHeaders);
1499
+ writer.bool(item.trackRevisions);
1500
+ writePageProjection(writer, item.page);
1501
+ return;
1502
+ case "section":
1503
+ writer.u8(1);
1504
+ writeDocumentId(writer, item.id, "section");
1505
+ writer.u64(item.startBlockIndex);
1506
+ writer.bool(item.titlePage);
1507
+ writePageProjection(writer, item.page);
1508
+ for (const count of item.headerBlockCounts) writer.u64(count);
1509
+ for (const count of item.footerBlockCounts) writer.u64(count);
1510
+ return;
1511
+ case "paragraph":
1512
+ writer.u8(2);
1513
+ writeDocumentId(writer, item.id, "paragraph");
1514
+ writeRuns(writer, item.runs, state);
1515
+ writeParagraphStyle(writer, item.style);
1516
+ return;
1517
+ case "table":
1518
+ writer.u8(3);
1519
+ writeDocumentId(writer, item.id, "table");
1520
+ writeTableRows(writer, item.rows, state);
1521
+ writeTableStyle(writer, item.style);
1522
+ return;
1523
+ case "page-break":
1524
+ writer.u8(4);
1525
+ writeDocumentId(writer, item.id, "page-break");
1526
+ return;
1527
+ case "comment":
1528
+ writer.u8(5);
1529
+ writeDocumentId(writer, item.id, "comment");
1530
+ writeDocumentId(writer, item.paragraphId, "paragraph");
1531
+ writeRange(writer, item.range);
1532
+ writer.bool(item.resolved);
1533
+ writer.count(item.replies.length, 100_000, "document comment replies");
1534
+ for (const reply of item.replies) writeCommentReply(writer, reply, state);
1535
+ return;
1536
+ case "tracked-change":
1537
+ writer.u8(6);
1538
+ writeDocumentId(writer, item.id, "tracked-change");
1539
+ writeDocumentId(writer, item.paragraphId, "paragraph");
1540
+ writer.u8(
1541
+ item.changeKind === "insert"
1542
+ ? 1
1543
+ : item.changeKind === "delete"
1544
+ ? 2
1545
+ : invalidTag("tracked change kind"),
1546
+ );
1547
+ writeRange(writer, item.range);
1548
+ writeDocumentString(writer, item.author, state, false);
1549
+ writeDocumentString(writer, item.createdAt, state, false);
1550
+ return;
1551
+ default:
1552
+ throw new TypeError("invalid document projection item kind");
1553
+ }
1554
+ }
1555
+
1556
+ function readProjectionItem(
1557
+ reader: ArtifactBinaryReader,
1558
+ state: CodecState,
1559
+ ): DocumentArtifactProjectionItem {
1560
+ const tag = reader.u8();
1561
+ if (tag === 0) {
1562
+ const item = Object.freeze({
1563
+ kind: "summary" as const,
1564
+ idNamespace: reader.u64BigInt(),
1565
+ revision: reader.u64BigInt(),
1566
+ nextIdCounter: reader.u64BigInt(),
1567
+ blockCount: boundedProjectionCount(reader, 100_000, "document block count"),
1568
+ sectionCount: boundedProjectionCount(reader, 10_000, "document section count"),
1569
+ commentCount: boundedProjectionCount(reader, 100_000, "document comment count"),
1570
+ trackedChangeCount: boundedProjectionCount(reader, 100_000, "document tracked-change count"),
1571
+ evenAndOddHeaders: reader.bool("document boolean"),
1572
+ trackRevisions: reader.bool("document boolean"),
1573
+ page: readPageProjection(reader),
1574
+ });
1575
+ return item;
1576
+ }
1577
+ if (tag === 1) {
1578
+ const id = readDocumentId(reader, "section");
1579
+ const startBlockIndex = boundedProjectionCount(reader, 100_000, "document section block index");
1580
+ const titlePage = reader.bool("document boolean");
1581
+ const page = readPageProjection(reader);
1582
+ const headerBlockCounts = Object.freeze([
1583
+ boundedProjectionCount(reader, 100_000, "document header blocks"),
1584
+ boundedProjectionCount(reader, 100_000, "document header blocks"),
1585
+ boundedProjectionCount(reader, 100_000, "document header blocks"),
1586
+ ]) as readonly [number, number, number];
1587
+ const footerBlockCounts = Object.freeze([
1588
+ boundedProjectionCount(reader, 100_000, "document footer blocks"),
1589
+ boundedProjectionCount(reader, 100_000, "document footer blocks"),
1590
+ boundedProjectionCount(reader, 100_000, "document footer blocks"),
1591
+ ]) as readonly [number, number, number];
1592
+ return Object.freeze({
1593
+ kind: "section",
1594
+ id,
1595
+ startBlockIndex,
1596
+ titlePage,
1597
+ page,
1598
+ headerBlockCounts,
1599
+ footerBlockCounts,
1600
+ });
1601
+ }
1602
+ if (tag === 2)
1603
+ return Object.freeze({
1604
+ kind: "paragraph",
1605
+ id: readDocumentId(reader, "paragraph"),
1606
+ runs: readRuns(reader, state),
1607
+ style: readParagraphStyle(reader),
1608
+ });
1609
+ if (tag === 3)
1610
+ return Object.freeze({
1611
+ kind: "table",
1612
+ id: readDocumentId(reader, "table"),
1613
+ rows: readTableRows(reader, state),
1614
+ style: readTableStyle(reader),
1615
+ });
1616
+ if (tag === 4)
1617
+ return Object.freeze({ kind: "page-break", id: readDocumentId(reader, "page-break") });
1618
+ if (tag === 5) {
1619
+ const id = readDocumentId(reader, "comment");
1620
+ const paragraphId = readDocumentId(reader, "paragraph");
1621
+ const range = readRange(reader);
1622
+ const resolved = reader.bool("document boolean");
1623
+ const count = reader.count(100_000, "document comment replies");
1624
+ const replies: DocumentArtifactCommentReply[] = [];
1625
+ for (let index = 0; index < count; index += 1) replies.push(readCommentReply(reader, state));
1626
+ return Object.freeze({
1627
+ kind: "comment",
1628
+ id,
1629
+ paragraphId,
1630
+ range,
1631
+ resolved,
1632
+ replies: Object.freeze(replies),
1633
+ });
1634
+ }
1635
+ if (tag === 6) {
1636
+ const id = readDocumentId(reader, "tracked-change");
1637
+ const paragraphId = readDocumentId(reader, "paragraph");
1638
+ const kindTag = reader.u8();
1639
+ if (kindTag !== 1 && kindTag !== 2) throw new TypeError("invalid tracked change kind");
1640
+ return Object.freeze({
1641
+ kind: "tracked-change",
1642
+ id,
1643
+ paragraphId,
1644
+ changeKind: kindTag === 1 ? "insert" : "delete",
1645
+ range: readRange(reader),
1646
+ author: readDocumentString(reader, state, false),
1647
+ createdAt: readDocumentString(reader, state, false),
1648
+ });
1649
+ }
1650
+ throw new TypeError("invalid document projection item tag");
1651
+ }
1652
+
1653
+ function boundedProjectionCount(
1654
+ reader: ArtifactBinaryReader,
1655
+ maximum: number,
1656
+ label: string,
1657
+ ): number {
1658
+ const value = reader.u64Safe(label);
1659
+ if (value > maximum) throw new RangeError(`${label} exceeds its limit`);
1660
+ return value;
1661
+ }
1662
+
1663
+ function measureProjection(items: readonly DocumentArtifactProjectionItem[]): {
1664
+ text: number;
1665
+ cells: number;
1666
+ } {
1667
+ let text = 0;
1668
+ let cells = 0;
1669
+ const add = (value: string): void => {
1670
+ text += value.length;
1671
+ };
1672
+ for (const item of items) {
1673
+ if (item.kind === "paragraph") for (const run of item.runs) add(run.text);
1674
+ else if (item.kind === "table")
1675
+ for (const row of item.rows) {
1676
+ cells += row.length;
1677
+ for (const cell of row) for (const run of cell) add(run.text);
1678
+ }
1679
+ else if (item.kind === "comment")
1680
+ for (const reply of item.replies) {
1681
+ add(reply.author);
1682
+ add(reply.text);
1683
+ add(reply.createdAt);
1684
+ }
1685
+ else if (item.kind === "tracked-change") {
1686
+ add(item.author);
1687
+ add(item.createdAt);
1688
+ }
1689
+ if (
1690
+ text > DOCUMENT_ARTIFACT_QUERY_MAX_TEXT_UTF16 ||
1691
+ cells > DOCUMENT_ARTIFACT_QUERY_MAX_TABLE_CELLS
1692
+ )
1693
+ throw new RangeError("document query response metrics exceed their limits");
1694
+ }
1695
+ return { text, cells };
1696
+ }
1697
+
1698
+ function freezeOptional<T extends object, V extends Record<string, unknown>>(
1699
+ base: T,
1700
+ values: V,
1701
+ ): Readonly<Record<string, unknown>> {
1702
+ const output: Record<string, unknown> = { ...(base as Record<string, unknown>) };
1703
+ for (const [key, value] of Object.entries(values)) if (value !== undefined) output[key] = value;
1704
+ return Object.freeze(output);
1705
+ }
1706
+
1707
+ function invalidTag(label: string): never {
1708
+ throw new TypeError(`invalid ${label}`);
1709
+ }