@opengeni/artifact-tool 0.2.2

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 (203) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +104 -0
  3. package/dist/bounded-zip.d.ts +28 -0
  4. package/dist/chunk-4BSENMAE.js +39 -0
  5. package/dist/chunk-4BSENMAE.js.map +1 -0
  6. package/dist/chunk-4FRFNNBM.js +276 -0
  7. package/dist/chunk-4FRFNNBM.js.map +1 -0
  8. package/dist/chunk-5XIA6KHZ.js +49 -0
  9. package/dist/chunk-5XIA6KHZ.js.map +1 -0
  10. package/dist/chunk-66CT2ZRP.js +2229 -0
  11. package/dist/chunk-66CT2ZRP.js.map +1 -0
  12. package/dist/chunk-72SYDU4O.js +319 -0
  13. package/dist/chunk-72SYDU4O.js.map +1 -0
  14. package/dist/chunk-B2ZJRZDA.js +279 -0
  15. package/dist/chunk-B2ZJRZDA.js.map +1 -0
  16. package/dist/chunk-DCTLTNPW.js +2458 -0
  17. package/dist/chunk-DCTLTNPW.js.map +1 -0
  18. package/dist/chunk-FGZM4FQ6.js +473 -0
  19. package/dist/chunk-FGZM4FQ6.js.map +1 -0
  20. package/dist/chunk-HTJBLBDE.js +591 -0
  21. package/dist/chunk-HTJBLBDE.js.map +1 -0
  22. package/dist/chunk-IYFXZQ34.js +5306 -0
  23. package/dist/chunk-IYFXZQ34.js.map +1 -0
  24. package/dist/chunk-J6N7IR44.js +172 -0
  25. package/dist/chunk-J6N7IR44.js.map +1 -0
  26. package/dist/chunk-N2IHNWBS.js +1002 -0
  27. package/dist/chunk-N2IHNWBS.js.map +1 -0
  28. package/dist/chunk-NAG44K67.js +549 -0
  29. package/dist/chunk-NAG44K67.js.map +1 -0
  30. package/dist/chunk-O24KTEGP.js +33 -0
  31. package/dist/chunk-O24KTEGP.js.map +1 -0
  32. package/dist/chunk-PI2AZVGK.js +2074 -0
  33. package/dist/chunk-PI2AZVGK.js.map +1 -0
  34. package/dist/chunk-Q2FFHEY7.js +40 -0
  35. package/dist/chunk-Q2FFHEY7.js.map +1 -0
  36. package/dist/chunk-RBGQADXA.js +57 -0
  37. package/dist/chunk-RBGQADXA.js.map +1 -0
  38. package/dist/chunk-U5G2BKM2.js +613 -0
  39. package/dist/chunk-U5G2BKM2.js.map +1 -0
  40. package/dist/chunk-VOJHMSM3.js +39 -0
  41. package/dist/chunk-VOJHMSM3.js.map +1 -0
  42. package/dist/chunk-VUNV25KB.js +16 -0
  43. package/dist/chunk-VUNV25KB.js.map +1 -0
  44. package/dist/chunk-XIFUNVBO.js +101 -0
  45. package/dist/chunk-XIFUNVBO.js.map +1 -0
  46. package/dist/chunk-ZCJYRJGB.js +489 -0
  47. package/dist/chunk-ZCJYRJGB.js.map +1 -0
  48. package/dist/chunk-ZZXPEE4S.js +2260 -0
  49. package/dist/chunk-ZZXPEE4S.js.map +1 -0
  50. package/dist/document-docx-api.d.ts +49 -0
  51. package/dist/document-docx-codec.d.ts +9 -0
  52. package/dist/document-docx-codec.js +4765 -0
  53. package/dist/document-docx-codec.js.map +1 -0
  54. package/dist/document-docx-import.d.ts +261 -0
  55. package/dist/document-docx-packer.d.ts +18 -0
  56. package/dist/document-docx-state.d.ts +12 -0
  57. package/dist/document-render.d.ts +3 -0
  58. package/dist/document-render.js +492 -0
  59. package/dist/document-render.js.map +1 -0
  60. package/dist/document.d.ts +432 -0
  61. package/dist/document.js +37 -0
  62. package/dist/document.js.map +1 -0
  63. package/dist/errors.d.ts +23 -0
  64. package/dist/file-blob.d.ts +11 -0
  65. package/dist/index.d.ts +4 -0
  66. package/dist/index.js +186 -0
  67. package/dist/index.js.map +1 -0
  68. package/dist/inspection.d.ts +12 -0
  69. package/dist/kernel.d.ts +101 -0
  70. package/dist/lib-GQLPACFK.js +9746 -0
  71. package/dist/lib-GQLPACFK.js.map +1 -0
  72. package/dist/materializer-cli-entry.d.ts +2 -0
  73. package/dist/materializer-cli-entry.js +29 -0
  74. package/dist/materializer-cli-entry.js.map +1 -0
  75. package/dist/materializer-cli.d.ts +24 -0
  76. package/dist/materializer-cli.js +24 -0
  77. package/dist/materializer-cli.js.map +1 -0
  78. package/dist/native-raster.d.ts +10 -0
  79. package/dist/native.d.ts +129 -0
  80. package/dist/native.js +31 -0
  81. package/dist/native.js.map +1 -0
  82. package/dist/office-import.d.ts +48 -0
  83. package/dist/office-import.js +154 -0
  84. package/dist/office-import.js.map +1 -0
  85. package/dist/presentation-pptx-api.d.ts +60 -0
  86. package/dist/presentation-pptx-import.d.ts +5 -0
  87. package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
  88. package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
  89. package/dist/presentation-pptx-state-digest.d.ts +3 -0
  90. package/dist/presentation-pptx-state.d.ts +4 -0
  91. package/dist/presentation-pptx-xml.d.ts +29 -0
  92. package/dist/presentation-pptx.d.ts +5 -0
  93. package/dist/presentation-pptx.js +2365 -0
  94. package/dist/presentation-pptx.js.map +1 -0
  95. package/dist/presentation-render.d.ts +29 -0
  96. package/dist/presentation-render.js +375 -0
  97. package/dist/presentation-render.js.map +1 -0
  98. package/dist/presentation.d.ts +565 -0
  99. package/dist/presentation.js +71 -0
  100. package/dist/presentation.js.map +1 -0
  101. package/dist/production-composite.d.ts +68 -0
  102. package/dist/production-document.d.ts +2 -0
  103. package/dist/production-document.js +176 -0
  104. package/dist/production-document.js.map +1 -0
  105. package/dist/production-native-codecs.d.ts +94 -0
  106. package/dist/production-presentation.d.ts +2 -0
  107. package/dist/production-presentation.js +176 -0
  108. package/dist/production-presentation.js.map +1 -0
  109. package/dist/production-sha256.d.ts +2 -0
  110. package/dist/production-spreadsheet-native.d.ts +12 -0
  111. package/dist/production-spreadsheet.d.ts +2 -0
  112. package/dist/production-spreadsheet.js +176 -0
  113. package/dist/production-spreadsheet.js.map +1 -0
  114. package/dist/production.d.ts +96 -0
  115. package/dist/raster-image.d.ts +34 -0
  116. package/dist/reference.d.ts +17 -0
  117. package/dist/reference.js +574 -0
  118. package/dist/reference.js.map +1 -0
  119. package/dist/runtime-cli-entry.d.ts +2 -0
  120. package/dist/runtime-cli-entry.js +21 -0
  121. package/dist/runtime-cli-entry.js.map +1 -0
  122. package/dist/runtime-cli.d.ts +76 -0
  123. package/dist/runtime-cli.js +26 -0
  124. package/dist/runtime-cli.js.map +1 -0
  125. package/dist/runtime-development.d.ts +73 -0
  126. package/dist/runtime-development.js +30 -0
  127. package/dist/runtime-development.js.map +1 -0
  128. package/dist/runtime-receipt.d.ts +19 -0
  129. package/dist/runtime.d.ts +355 -0
  130. package/dist/runtime.js +42 -0
  131. package/dist/runtime.js.map +1 -0
  132. package/dist/snapshot.d.ts +26 -0
  133. package/dist/snapshot.js +61 -0
  134. package/dist/snapshot.js.map +1 -0
  135. package/dist/spreadsheet-address.d.ts +18 -0
  136. package/dist/spreadsheet-file.d.ts +17 -0
  137. package/dist/spreadsheet-formula.d.ts +67 -0
  138. package/dist/spreadsheet-image.d.ts +17 -0
  139. package/dist/spreadsheet-render.d.ts +5 -0
  140. package/dist/spreadsheet-render.js +752 -0
  141. package/dist/spreadsheet-render.js.map +1 -0
  142. package/dist/spreadsheet-types.d.ts +155 -0
  143. package/dist/spreadsheet-xlsx-api.d.ts +88 -0
  144. package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
  145. package/dist/spreadsheet-xlsx-codec.js +16 -0
  146. package/dist/spreadsheet-xlsx-codec.js.map +1 -0
  147. package/dist/spreadsheet-xlsx-state.d.ts +16 -0
  148. package/dist/spreadsheet.d.ts +724 -0
  149. package/dist/spreadsheet.js +67 -0
  150. package/dist/spreadsheet.js.map +1 -0
  151. package/package.json +129 -0
  152. package/src/bounded-zip.ts +450 -0
  153. package/src/document-docx-api.ts +64 -0
  154. package/src/document-docx-codec.ts +2253 -0
  155. package/src/document-docx-import.ts +3814 -0
  156. package/src/document-docx-packer.ts +72 -0
  157. package/src/document-docx-state.ts +59 -0
  158. package/src/document-render.ts +615 -0
  159. package/src/document.ts +2800 -0
  160. package/src/errors.ts +45 -0
  161. package/src/file-blob.ts +43 -0
  162. package/src/index.ts +4 -0
  163. package/src/inspection.ts +50 -0
  164. package/src/kernel.ts +525 -0
  165. package/src/materializer-cli-entry.ts +11 -0
  166. package/src/materializer-cli.ts +743 -0
  167. package/src/native-raster.ts +141 -0
  168. package/src/native.ts +390 -0
  169. package/src/office-import.ts +243 -0
  170. package/src/presentation-pptx-api.ts +107 -0
  171. package/src/presentation-pptx-import.ts +2065 -0
  172. package/src/presentation-pptx-state-digest.ts +46 -0
  173. package/src/presentation-pptx-state.ts +18 -0
  174. package/src/presentation-pptx-xml.ts +473 -0
  175. package/src/presentation-pptx.ts +447 -0
  176. package/src/presentation-render.ts +532 -0
  177. package/src/presentation.ts +2599 -0
  178. package/src/production-composite.ts +805 -0
  179. package/src/production-document.ts +2 -0
  180. package/src/production-native-codecs.ts +1141 -0
  181. package/src/production-presentation.ts +2 -0
  182. package/src/production-sha256.ts +150 -0
  183. package/src/production-spreadsheet-native.ts +686 -0
  184. package/src/production-spreadsheet.ts +2 -0
  185. package/src/production.ts +1022 -0
  186. package/src/raster-image.ts +586 -0
  187. package/src/reference.ts +17 -0
  188. package/src/runtime-cli-entry.ts +13 -0
  189. package/src/runtime-cli.ts +670 -0
  190. package/src/runtime-development.ts +689 -0
  191. package/src/runtime-receipt.ts +169 -0
  192. package/src/runtime.ts +1463 -0
  193. package/src/snapshot.ts +88 -0
  194. package/src/spreadsheet-address.ts +90 -0
  195. package/src/spreadsheet-file.ts +71 -0
  196. package/src/spreadsheet-formula.ts +1212 -0
  197. package/src/spreadsheet-image.ts +242 -0
  198. package/src/spreadsheet-render.ts +985 -0
  199. package/src/spreadsheet-types.ts +171 -0
  200. package/src/spreadsheet-xlsx-api.ts +123 -0
  201. package/src/spreadsheet-xlsx-codec.ts +3063 -0
  202. package/src/spreadsheet-xlsx-state.ts +191 -0
  203. package/src/spreadsheet.ts +5195 -0
@@ -0,0 +1,686 @@
1
+ import {
2
+ SPREADSHEET_ARTIFACT_PROJECTION_MAX_BYTES,
3
+ SPREADSHEET_ARTIFACT_VIEWPORT_MAX_CELLS,
4
+ decodeSpreadsheetMetadataKernelProjection,
5
+ decodeSpreadsheetViewportKernelProjection,
6
+ editableArtifactStableId,
7
+ encodeSpreadsheetMetadataKernelQuery,
8
+ encodeSpreadsheetViewportKernelQuery,
9
+ spreadsheetSheetId,
10
+ type SpreadsheetArtifactCommand,
11
+ type SpreadsheetArtifactProjectedCellValue,
12
+ type SpreadsheetArtifactSheetMetadata,
13
+ type SpreadsheetCellInput,
14
+ type SpreadsheetSheetGeneration,
15
+ } from "@opengeni/contracts/editable-artifacts";
16
+ import { NativeSpreadsheetSession } from "./native";
17
+ import type {
18
+ CompositeMutationPreparer,
19
+ CompositeArtifactState,
20
+ CompositePropertyRead,
21
+ CompositeReconciliation,
22
+ PreparedCompositeMutation,
23
+ } from "./production-composite";
24
+ import type { ArtifactKernelRuntime } from "./runtime";
25
+ import {
26
+ Workbook as ReferenceWorkbook,
27
+ type SerializedWorkbook,
28
+ type Workbook,
29
+ } from "./spreadsheet";
30
+ import type { FormulaResult } from "./spreadsheet-types";
31
+
32
+ type SerializedWorksheet = SerializedWorkbook["worksheets"][number];
33
+ type SerializedCell = SerializedWorksheet["cells"][number];
34
+
35
+ type SpreadsheetNativeData = {
36
+ readonly sheetIds: Map<string, ReturnType<typeof spreadsheetSheetId>>;
37
+ readonly generations: Map<string, SpreadsheetSheetGeneration>;
38
+ readonly author: SpreadsheetProjectionAuthor;
39
+ metadata: ReadonlyMap<string, SpreadsheetArtifactSheetMetadata> | null;
40
+ };
41
+
42
+ type RangeLike = Readonly<{
43
+ worksheet: Readonly<{ id: string }>;
44
+ address: Readonly<{ row: number; col: number; rowCount: number; colCount: number }>;
45
+ }>;
46
+
47
+ const textEncoder = new TextEncoder();
48
+
49
+ export function reconcileSpreadsheetProjection(
50
+ workbook: Workbook,
51
+ runtime: ArtifactKernelRuntime,
52
+ namespace: bigint,
53
+ ): CompositeReconciliation {
54
+ const snapshot = workbook.toJSON();
55
+ const session = NativeSpreadsheetSession.create(runtime, namespace);
56
+ const sheetIds = new Map<string, ReturnType<typeof spreadsheetSheetId>>();
57
+ const generations = new Map<string, SpreadsheetSheetGeneration>();
58
+ const author = new SpreadsheetProjectionAuthor(session, namespace);
59
+
60
+ try {
61
+ let previous: SpreadsheetSheetGeneration | null = null;
62
+ let offset = 0;
63
+ const maximumCommands = Math.max(1, Math.min(session.capabilities.maxCommands, 1_024));
64
+ while (offset < snapshot.worksheets.length) {
65
+ const chunk = snapshot.worksheets.slice(offset, offset + maximumCommands);
66
+ const commands: SpreadsheetArtifactCommand[] = [];
67
+ for (let relativeIndex = 0; relativeIndex < chunk.length; relativeIndex += 1) {
68
+ const worksheet = chunk[relativeIndex]!;
69
+ const sheetId = nativeSheetId(namespace, worksheet.id);
70
+ sheetIds.set(worksheet.id, sheetId);
71
+ const after =
72
+ relativeIndex === 0
73
+ ? previous
74
+ : {
75
+ kind: "created-in-batch" as const,
76
+ sheetId: sheetIds.get(chunk[relativeIndex - 1]!.id)!,
77
+ createCommandIndex: relativeIndex - 1,
78
+ };
79
+ commands.push({ kind: "sheet.create", sheetId, name: worksheet.name, after });
80
+ }
81
+ author.author(commands);
82
+ const metadata = querySpreadsheetMetadata(session, sheetIds.size);
83
+ const metadataById = new Map(metadata.map((sheet) => [sheet.sheetId, sheet]));
84
+ for (let index = 0; index < chunk.length; index += 1) {
85
+ const worksheet = chunk[index]!;
86
+ const sheetId = sheetIds.get(worksheet.id)!;
87
+ const operationId = metadataById.get(sheetId)?.generationId;
88
+ if (!operationId) throw new Error("Native kernel omitted a sheet generation id");
89
+ const generation: SpreadsheetSheetGeneration = Object.freeze({
90
+ kind: "generation",
91
+ sheetId,
92
+ creationOperationId: editableArtifactStableId(operationId),
93
+ });
94
+ generations.set(worksheet.id, generation);
95
+ previous = generation;
96
+ }
97
+ offset += chunk.length;
98
+ }
99
+
100
+ const batch = new SpreadsheetCommandBatcher(author, session);
101
+ for (const worksheet of snapshot.worksheets) {
102
+ const generation = generations.get(worksheet.id);
103
+ if (!generation) throw new Error(`Missing native generation for worksheet ${worksheet.id}`);
104
+ for (const command of cellCommands(worksheet, generation)) batch.add(command);
105
+ }
106
+ batch.finish();
107
+ return {
108
+ session,
109
+ data: {
110
+ sheetIds,
111
+ generations,
112
+ author,
113
+ metadata: null,
114
+ } satisfies SpreadsheetNativeData,
115
+ };
116
+ } catch (cause) {
117
+ session.dispose();
118
+ throw cause;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Converts the public hot spreadsheet mutations directly into the existing
124
+ * OGASC/OGATX protocol. The retained native session stays authoritative; rare
125
+ * non-cell features continue through the complete reconciliation path.
126
+ */
127
+ export const prepareSpreadsheetMutation: CompositeMutationPreparer<Workbook> = (
128
+ mutation,
129
+ state,
130
+ ): PreparedCompositeMutation | null => {
131
+ const data = spreadsheetData(state);
132
+ const root = state.rawRoot();
133
+ if (mutation.owner === root.worksheets && mutation.member === "add") {
134
+ const previousHostId = root.worksheets.items.at(-1)?.id;
135
+ const after = previousHostId ? (data.generations.get(previousHostId) ?? null) : null;
136
+ return {
137
+ commit(result) {
138
+ if (!isWorksheet(result)) throw new Error("Worksheet add did not return a worksheet");
139
+ const sheetId = nativeSheetId(state.namespace, result.id);
140
+ data.author.author([{ kind: "sheet.create", sheetId, name: result.name, after }]);
141
+ const operationId = querySpreadsheetMetadata(
142
+ state.native as NativeSpreadsheetSession,
143
+ data.sheetIds.size + 1,
144
+ ).find((sheet) => sheet.sheetId === sheetId)?.generationId;
145
+ if (!operationId) throw new Error("Native kernel omitted a sheet generation id");
146
+ data.sheetIds.set(result.id, sheetId);
147
+ data.generations.set(
148
+ result.id,
149
+ Object.freeze({
150
+ kind: "generation",
151
+ sheetId,
152
+ creationOperationId: editableArtifactStableId(operationId),
153
+ }),
154
+ );
155
+ data.metadata = null;
156
+ return true;
157
+ },
158
+ };
159
+ }
160
+
161
+ if (isWorksheet(mutation.owner) && mutation.member === "name") {
162
+ const generation = requiredGeneration(data, mutation.owner.id);
163
+ const name = normalizedSheetName(mutation.arguments?.[0]);
164
+ return commitSpreadsheetCommands(data, [{ kind: "sheet.rename", sheet: generation, name }]);
165
+ }
166
+
167
+ if (!isRange(mutation.owner)) return null;
168
+ const range = mutation.owner;
169
+ const generation = requiredGeneration(data, range.worksheet.id);
170
+ if (mutation.member === "values" || mutation.member === "formulas") {
171
+ return commitSpreadsheetCommands(data, [
172
+ rectangularWriteCommand(generation, range.address, mutation.arguments?.[0], mutation.member),
173
+ ]);
174
+ }
175
+ if (mutation.member === "writeValues") {
176
+ const matrix = rectangularMatrix(mutation.arguments?.[0], "spreadsheet values");
177
+ if (matrix.rows === 0 || matrix.columns === 0) return { commit: () => false };
178
+ return commitSpreadsheetCommands(data, [
179
+ rectangularWriteCommand(
180
+ generation,
181
+ { ...range.address, rowCount: matrix.rows, colCount: matrix.columns },
182
+ matrix.values,
183
+ "values",
184
+ ),
185
+ ]);
186
+ }
187
+ if (mutation.member === "clear") {
188
+ const options = mutation.arguments?.[0] as { applyTo?: unknown } | undefined;
189
+ if (options?.applyTo === "formats") {
190
+ // The canonical Rust spreadsheet model intentionally has no formatting
191
+ // state. Preserve the facade revision without fabricating a kernel edit.
192
+ return { commit: () => true };
193
+ }
194
+ return commitSpreadsheetCommands(data, [
195
+ {
196
+ kind: "range.clear",
197
+ sheet: generation,
198
+ range: {
199
+ start: { row: range.address.row, column: range.address.col },
200
+ end: {
201
+ row: range.address.row + range.address.rowCount - 1,
202
+ column: range.address.col + range.address.colCount - 1,
203
+ },
204
+ },
205
+ },
206
+ ]);
207
+ }
208
+ return null;
209
+ };
210
+
211
+ function commitSpreadsheetCommands(
212
+ data: SpreadsheetNativeData,
213
+ commands: readonly SpreadsheetArtifactCommand[],
214
+ ): PreparedCompositeMutation {
215
+ return {
216
+ commit() {
217
+ data.author.author(commands);
218
+ data.metadata = null;
219
+ return true;
220
+ },
221
+ };
222
+ }
223
+
224
+ function requiredGeneration(
225
+ data: SpreadsheetNativeData,
226
+ hostSheetId: string,
227
+ ): SpreadsheetSheetGeneration {
228
+ const generation = data.generations.get(hostSheetId);
229
+ if (!generation) throw new Error(`Spreadsheet native generation is missing: ${hostSheetId}`);
230
+ return generation;
231
+ }
232
+
233
+ function rectangularWriteCommand(
234
+ sheet: SpreadsheetSheetGeneration,
235
+ address: RangeLike["address"],
236
+ input: unknown,
237
+ kind: "values" | "formulas",
238
+ ): SpreadsheetArtifactCommand {
239
+ const matrix = rectangularMatrix(input, `spreadsheet ${kind}`);
240
+ if (matrix.rows !== address.rowCount || matrix.columns !== address.colCount) {
241
+ throw new Error(
242
+ `Matrix shape ${matrix.rows}x${matrix.columns} does not match range ${address.rowCount}x${address.colCount}`,
243
+ );
244
+ }
245
+ const cells: SpreadsheetCellInput[] = [];
246
+ for (const row of matrix.values) {
247
+ for (const value of row) {
248
+ cells.push(kind === "values" ? nativeInputValue(value) : nativeFormula(value));
249
+ }
250
+ }
251
+ return {
252
+ kind: "cells.set",
253
+ sheet,
254
+ anchor: { row: address.row, column: address.col },
255
+ rows: address.rowCount,
256
+ columns: address.colCount,
257
+ cells,
258
+ };
259
+ }
260
+
261
+ function rectangularMatrix(
262
+ input: unknown,
263
+ label: string,
264
+ ): { values: readonly (readonly unknown[])[]; rows: number; columns: number } {
265
+ if (!Array.isArray(input)) throw new TypeError(`${label} must be a matrix`);
266
+ const columns = input[0]?.length ?? 0;
267
+ if (!input.every((row) => Array.isArray(row) && row.length === columns)) {
268
+ throw new Error(`${label} must be rectangular`);
269
+ }
270
+ return { values: input, rows: input.length, columns };
271
+ }
272
+
273
+ function nativeInputValue(value: unknown): SpreadsheetCellInput {
274
+ if (value instanceof Date) {
275
+ const milliseconds = Date.prototype.getTime.call(value);
276
+ if (!Number.isSafeInteger(milliseconds)) {
277
+ throw new TypeError("Spreadsheet cell dates must be valid ECMAScript instants");
278
+ }
279
+ return { date: new Date(milliseconds).toISOString() };
280
+ }
281
+ if (value === null || typeof value === "string" || typeof value === "boolean") return value;
282
+ if (typeof value === "number") {
283
+ if (!Number.isFinite(value)) throw new TypeError("Spreadsheet cell numbers must be finite");
284
+ return value;
285
+ }
286
+ throw new TypeError("Spreadsheet cell value is not supported by the native kernel");
287
+ }
288
+
289
+ function nativeFormula(value: unknown): SpreadsheetCellInput {
290
+ if (value === null) return null;
291
+ if (typeof value !== "string")
292
+ throw new TypeError("Spreadsheet formulas must be strings or null");
293
+ const trimmed = value.trim();
294
+ if (trimmed.length === 0) return null;
295
+ return { formula: trimmed.startsWith("=") ? trimmed : `=${trimmed}`, cached: null };
296
+ }
297
+
298
+ function normalizedSheetName(value: unknown): string {
299
+ if (typeof value !== "string") throw new TypeError("Worksheet name must be a string");
300
+ const name = value.trim();
301
+ if (!name || name.length > 31 || /[\\/?*[\]:]/u.test(name)) {
302
+ throw new Error(`Invalid worksheet name: ${value}`);
303
+ }
304
+ return name;
305
+ }
306
+
307
+ export function installSpreadsheetProjection(
308
+ workbook: Workbook,
309
+ state: CompositeArtifactState<Workbook>,
310
+ ): void {
311
+ Object.defineProperty(workbook, "valueAt", {
312
+ configurable: true,
313
+ writable: true,
314
+ value: (
315
+ sheet: Parameters<Workbook["valueAt"]>[0],
316
+ address: Parameters<Workbook["valueAt"]>[1],
317
+ ): FormulaResult => {
318
+ if (state.inMutation) {
319
+ return ReferenceWorkbook.prototype.valueAt.call(workbook, sheet, address);
320
+ }
321
+ return readSpreadsheetMatrix(state, sheet.id, {
322
+ row: address.row,
323
+ col: address.col,
324
+ rowCount: 1,
325
+ colCount: 1,
326
+ }).values[0]![0]!;
327
+ },
328
+ });
329
+ }
330
+
331
+ export function readSpreadsheetProperty(
332
+ owner: unknown,
333
+ member: PropertyKey,
334
+ state: CompositeArtifactState<Workbook>,
335
+ ): CompositePropertyRead {
336
+ if (state.inMutation) return { handled: false };
337
+ if (member === "values" && isRange(owner)) {
338
+ return {
339
+ handled: true,
340
+ value: readSpreadsheetMatrix(state, owner.worksheet.id, owner.address).values,
341
+ };
342
+ }
343
+ if (member === "formulas" && isRange(owner)) {
344
+ return {
345
+ handled: true,
346
+ value: readSpreadsheetMatrix(state, owner.worksheet.id, owner.address).formulas,
347
+ };
348
+ }
349
+ if (member === "name" && isWorksheet(owner)) {
350
+ const metadata = spreadsheetMetadata(state).get(owner.id);
351
+ if (!metadata) throw new Error(`Native worksheet is missing: ${owner.id}`);
352
+ return { handled: true, value: metadata.name };
353
+ }
354
+ return { handled: false };
355
+ }
356
+
357
+ function readSpreadsheetMatrix(
358
+ state: CompositeArtifactState<Workbook>,
359
+ hostSheetId: string,
360
+ address: RangeLike["address"],
361
+ ): { values: FormulaResult[][]; formulas: Array<Array<string | null>> } {
362
+ const data = spreadsheetData(state);
363
+ const nativeSheetIdValue = data.sheetIds.get(hostSheetId);
364
+ if (!nativeSheetIdValue) throw new Error(`Native worksheet mapping is missing: ${hostSheetId}`);
365
+ const cells = address.rowCount * address.colCount;
366
+ if (!Number.isSafeInteger(cells) || cells > SPREADSHEET_ARTIFACT_VIEWPORT_MAX_CELLS) {
367
+ throw new RangeError(
368
+ `Spreadsheet read exceeds the native viewport limit (${SPREADSHEET_ARTIFACT_VIEWPORT_MAX_CELLS} cells)`,
369
+ );
370
+ }
371
+ const maxBytes = Math.min(
372
+ state.native.capabilities.maxQueryResponseBytes,
373
+ SPREADSHEET_ARTIFACT_PROJECTION_MAX_BYTES,
374
+ );
375
+ const query = {
376
+ sheetId: nativeSheetIdValue,
377
+ startRow: address.row,
378
+ startColumn: address.col,
379
+ rowCount: address.rowCount,
380
+ columnCount: address.colCount,
381
+ maxCells: cells,
382
+ maxBytes,
383
+ } as const;
384
+ const projection = decodeSpreadsheetViewportKernelProjection(
385
+ (state.native as NativeSpreadsheetSession).query(encodeSpreadsheetViewportKernelQuery(query)),
386
+ query,
387
+ );
388
+ const values: FormulaResult[][] = Array.from({ length: address.rowCount }, () =>
389
+ Array<FormulaResult>(address.colCount).fill(null),
390
+ );
391
+ const formulas: Array<Array<string | null>> = Array.from({ length: address.rowCount }, () =>
392
+ Array<string | null>(address.colCount).fill(null),
393
+ );
394
+ for (const cell of projection.cells) {
395
+ const row = cell.row - address.row;
396
+ const column = cell.column - address.col;
397
+ values[row]![column] = projectedCellValue(cell.value);
398
+ formulas[row]![column] = cell.formula;
399
+ }
400
+ return { values, formulas };
401
+ }
402
+
403
+ function spreadsheetMetadata(
404
+ state: CompositeArtifactState<Workbook>,
405
+ ): ReadonlyMap<string, SpreadsheetArtifactSheetMetadata> {
406
+ const data = spreadsheetData(state);
407
+ if (data.metadata) return data.metadata;
408
+ const sheets = querySpreadsheetMetadata(
409
+ state.native as NativeSpreadsheetSession,
410
+ data.sheetIds.size,
411
+ );
412
+ const hostByNative = new Map([...data.sheetIds].map(([host, native]) => [native, host]));
413
+ const metadata = new Map<string, SpreadsheetArtifactSheetMetadata>();
414
+ for (const sheet of sheets) {
415
+ const host = hostByNative.get(sheet.sheetId);
416
+ if (host) metadata.set(host, sheet);
417
+ }
418
+ data.metadata = metadata;
419
+ return metadata;
420
+ }
421
+
422
+ function querySpreadsheetMetadata(
423
+ session: NativeSpreadsheetSession,
424
+ maxSheets: number,
425
+ ): readonly SpreadsheetArtifactSheetMetadata[] {
426
+ const query = {
427
+ maxSheets: Math.max(1, maxSheets),
428
+ maxBytes: Math.min(
429
+ session.capabilities.maxQueryResponseBytes,
430
+ SPREADSHEET_ARTIFACT_PROJECTION_MAX_BYTES,
431
+ ),
432
+ } as const;
433
+ return decodeSpreadsheetMetadataKernelProjection(
434
+ session.query(encodeSpreadsheetMetadataKernelQuery(query)),
435
+ query,
436
+ ).sheets;
437
+ }
438
+
439
+ function spreadsheetData(state: CompositeArtifactState<Workbook>): SpreadsheetNativeData {
440
+ if (state.native.modality !== "spreadsheet")
441
+ throw new TypeError("Expected native spreadsheet session");
442
+ const data = state.nativeData as Partial<SpreadsheetNativeData> | undefined;
443
+ if (
444
+ !(data?.sheetIds instanceof Map) ||
445
+ !(data.generations instanceof Map) ||
446
+ !(data.author instanceof SpreadsheetProjectionAuthor)
447
+ ) {
448
+ throw new Error("Spreadsheet native mapping is unavailable");
449
+ }
450
+ return data as SpreadsheetNativeData;
451
+ }
452
+
453
+ class SpreadsheetProjectionAuthor {
454
+ readonly #session: NativeSpreadsheetSession;
455
+ readonly #replicaId: string;
456
+ readonly #artifactId: string;
457
+ #transactionCounter = 0;
458
+ #previousTransactionId: string | null = null;
459
+
460
+ constructor(session: NativeSpreadsheetSession, namespace: bigint) {
461
+ this.#session = session;
462
+ this.#replicaId = u64Hex(namespace);
463
+ this.#artifactId = `${this.#replicaId}ffffffffffffffff`;
464
+ }
465
+
466
+ author(commands: readonly SpreadsheetArtifactCommand[]): void {
467
+ if (commands.length === 0) return;
468
+ const counter = this.#transactionCounter + 1;
469
+ const clientTransactionId = `projection.${this.#replicaId}.${counter}`;
470
+ const revision = this.#session.revision();
471
+ if (revision > BigInt(Number.MAX_SAFE_INTEGER)) {
472
+ throw new RangeError("Native spreadsheet revision exceeds the JavaScript safe-integer range");
473
+ }
474
+ this.#session.authorCommands({
475
+ intent: {
476
+ artifactId: this.#artifactId,
477
+ clientTransactionId,
478
+ replicaId: this.#replicaId,
479
+ replicaCounter: counter,
480
+ previousLocalTransactionId: this.#previousTransactionId,
481
+ observedHeadSequence: Number(revision),
482
+ causalBase:
483
+ this.#transactionCounter === 0
484
+ ? []
485
+ : [{ replicaId: this.#replicaId, counter: this.#transactionCounter }],
486
+ selectiveUndoOperationIds: [],
487
+ },
488
+ commands: { version: 1, commands },
489
+ resolvedBaseBytes: this.#session.frontier(),
490
+ });
491
+ this.#transactionCounter = counter;
492
+ this.#previousTransactionId = clientTransactionId;
493
+ }
494
+ }
495
+
496
+ class SpreadsheetCommandBatcher {
497
+ readonly #author: SpreadsheetProjectionAuthor;
498
+ readonly #maxCommands: number;
499
+ readonly #maxCells: number;
500
+ readonly #maxEstimatedBytes: number;
501
+ #commands: SpreadsheetArtifactCommand[] = [];
502
+ #cells = 0;
503
+ #estimatedBytes = 0;
504
+
505
+ constructor(author: SpreadsheetProjectionAuthor, session: NativeSpreadsheetSession) {
506
+ this.#author = author;
507
+ this.#maxCommands = Math.max(1, Math.min(session.capabilities.maxCommands, 1_000));
508
+ this.#maxCells = Math.max(1, Math.min(session.capabilities.maxCellsPerBatch, 100_000));
509
+ this.#maxEstimatedBytes = Math.max(
510
+ 16_384,
511
+ Math.min(session.capabilities.maxSpreadsheetCommandBytes - 4_096, 2 * 1024 * 1024),
512
+ );
513
+ }
514
+
515
+ add(command: SpreadsheetArtifactCommand): void {
516
+ const cells = command.kind === "cells.set" ? command.cells.length : 0;
517
+ const estimatedBytes = estimateCommandBytes(command);
518
+ if (
519
+ this.#commands.length > 0 &&
520
+ (this.#commands.length + 1 > this.#maxCommands ||
521
+ this.#cells + cells > this.#maxCells ||
522
+ this.#estimatedBytes + estimatedBytes > this.#maxEstimatedBytes)
523
+ ) {
524
+ this.flush();
525
+ }
526
+ this.#commands.push(command);
527
+ this.#cells += cells;
528
+ this.#estimatedBytes += estimatedBytes;
529
+ if (estimatedBytes > this.#maxEstimatedBytes) this.flush();
530
+ }
531
+
532
+ finish(): void {
533
+ this.flush();
534
+ }
535
+
536
+ private flush(): void {
537
+ if (this.#commands.length === 0) return;
538
+ this.#author.author(this.#commands);
539
+ this.#commands = [];
540
+ this.#cells = 0;
541
+ this.#estimatedBytes = 0;
542
+ }
543
+ }
544
+
545
+ function* cellCommands(
546
+ worksheet: SerializedWorksheet,
547
+ generation: SpreadsheetSheetGeneration,
548
+ ): Iterable<SpreadsheetArtifactCommand> {
549
+ const cells = [...worksheet.cells].sort(
550
+ (left, right) => left.row - right.row || left.col - right.col,
551
+ );
552
+ let index = 0;
553
+ while (index < cells.length) {
554
+ const first = cells[index]!;
555
+ const rowCells: SpreadsheetCellInput[] = [nativeCell(first)];
556
+ let nextColumn = first.col + 1;
557
+ index += 1;
558
+ while (
559
+ index < cells.length &&
560
+ cells[index]!.row === first.row &&
561
+ cells[index]!.col === nextColumn &&
562
+ rowCells.length < 4_096 &&
563
+ estimateCellsBytes(rowCells) < 512 * 1024
564
+ ) {
565
+ rowCells.push(nativeCell(cells[index]!));
566
+ nextColumn += 1;
567
+ index += 1;
568
+ }
569
+ yield {
570
+ kind: "cells.set",
571
+ sheet: generation,
572
+ anchor: { row: first.row, column: first.col },
573
+ rows: 1,
574
+ columns: rowCells.length,
575
+ cells: rowCells,
576
+ };
577
+ }
578
+ }
579
+
580
+ function nativeCell(cell: SerializedCell): SpreadsheetCellInput {
581
+ if (cell.formula !== null) return { formula: cell.formula, cached: null };
582
+ const value = cell.value;
583
+ if (typeof value === "object" && value !== null) {
584
+ if ("type" in value && value.type === "date") {
585
+ return { date: value.value };
586
+ }
587
+ throw new TypeError("Serialized spreadsheet cell value is not supported by the native kernel");
588
+ }
589
+ if (typeof value === "number" && !Number.isFinite(value)) {
590
+ throw new TypeError("Spreadsheet cell numbers must be finite");
591
+ }
592
+ return value;
593
+ }
594
+
595
+ function nativeSheetId(namespace: bigint, hostId: string): ReturnType<typeof spreadsheetSheetId> {
596
+ const match = /^ws\/(\d+)$/u.exec(hostId);
597
+ if (!match) throw new Error(`Worksheet id cannot be mapped to the native kernel: ${hostId}`);
598
+ // Counter 1 is the native workbook object itself. Reference worksheet ids
599
+ // begin at ws/1, so their exact collision-free native mapping is N + 1.
600
+ const counter = BigInt(match[1]!) + 1n;
601
+ if (counter <= 1n || counter > 0xffff_ffff_ffff_ffffn) {
602
+ throw new RangeError(`Worksheet id counter is outside the native range: ${hostId}`);
603
+ }
604
+ return spreadsheetSheetId(`${u64Hex(namespace)}${u64Hex(counter)}`);
605
+ }
606
+
607
+ function projectedCellValue(value: SpreadsheetArtifactProjectedCellValue): FormulaResult {
608
+ switch (value.kind) {
609
+ case "empty":
610
+ return null;
611
+ case "boolean":
612
+ case "number":
613
+ case "text":
614
+ return value.value;
615
+ case "date":
616
+ return new Date(value.value);
617
+ case "error": {
618
+ if (typeof value.value === "object") return value.value.custom;
619
+ const errors: Readonly<Record<string, string>> = {
620
+ null: "#NULL!",
621
+ divide_by_zero: "#DIV/0!",
622
+ value: "#VALUE!",
623
+ reference: "#REF!",
624
+ name: "#NAME?",
625
+ number: "#NUM!",
626
+ not_available: "#N/A",
627
+ spill: "#SPILL!",
628
+ calculation: "#CALC!",
629
+ };
630
+ const projected = errors[value.value];
631
+ if (!projected) throw new Error(`Unknown native formula error: ${value.value}`);
632
+ return projected;
633
+ }
634
+ }
635
+ throw new Error("Unknown native spreadsheet cell value");
636
+ }
637
+
638
+ function estimateCommandBytes(command: SpreadsheetArtifactCommand): number {
639
+ if (command.kind !== "cells.set") return 256;
640
+ return 256 + estimateCellsBytes(command.cells);
641
+ }
642
+
643
+ function estimateCellsBytes(cells: readonly SpreadsheetCellInput[]): number {
644
+ let bytes = cells.length * 16;
645
+ for (const cell of cells) {
646
+ if (typeof cell === "string") bytes += textEncoder.encode(cell).byteLength;
647
+ else if (typeof cell === "object" && cell !== null) {
648
+ if ("formula" in cell) bytes += textEncoder.encode(cell.formula).byteLength;
649
+ else if ("error" in cell) bytes += textEncoder.encode(cell.error).byteLength;
650
+ }
651
+ }
652
+ return bytes;
653
+ }
654
+
655
+ function isRange(value: unknown): value is RangeLike {
656
+ if (typeof value !== "object" || value === null) return false;
657
+ const worksheet = Reflect.get(value, "worksheet");
658
+ const address = Reflect.get(value, "address");
659
+ return (
660
+ typeof worksheet === "object" &&
661
+ worksheet !== null &&
662
+ typeof Reflect.get(worksheet, "id") === "string" &&
663
+ typeof address === "object" &&
664
+ address !== null &&
665
+ Number.isInteger(Reflect.get(address, "row")) &&
666
+ Number.isInteger(Reflect.get(address, "col"))
667
+ );
668
+ }
669
+
670
+ function isWorksheet(value: unknown): value is { id: string; name: string } {
671
+ return (
672
+ typeof value === "object" &&
673
+ value !== null &&
674
+ typeof Reflect.get(value, "id") === "string" &&
675
+ typeof Reflect.get(value, "name") === "string" &&
676
+ String(Reflect.get(value, "id")).startsWith("ws/") &&
677
+ typeof Reflect.get(value, "getRangeByIndexes") === "function"
678
+ );
679
+ }
680
+
681
+ function u64Hex(value: bigint): string {
682
+ if (value <= 0n || value > 0xffff_ffff_ffff_ffffn) {
683
+ throw new RangeError("Artifact namespace must be a nonzero u64");
684
+ }
685
+ return value.toString(16).padStart(16, "0");
686
+ }
@@ -0,0 +1,2 @@
1
+ /** Native-authoritative spreadsheet facade. TypeScript models live at /reference. */
2
+ export * from "./production";