@opengeni/artifact-tool 0.2.8 → 0.3.4-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/{chunk-ZCJYRJGB.js → chunk-4XJE7NRA.js} +2 -2
  2. package/dist/{chunk-N2IHNWBS.js → chunk-4YYYUZQH.js} +15 -6
  3. package/dist/chunk-4YYYUZQH.js.map +1 -0
  4. package/dist/{chunk-U5G2BKM2.js → chunk-LMLPFF3S.js} +12 -6
  5. package/dist/{chunk-U5G2BKM2.js.map → chunk-LMLPFF3S.js.map} +1 -1
  6. package/dist/{chunk-B2ZJRZDA.js → chunk-LVPFJH2Z.js} +7 -5
  7. package/dist/chunk-LVPFJH2Z.js.map +1 -0
  8. package/dist/{chunk-QZI5POCC.js → chunk-OVXX4G35.js} +2 -2
  9. package/dist/{chunk-6PBM7FKI.js → chunk-QA3NXBAG.js} +8 -7
  10. package/dist/chunk-QA3NXBAG.js.map +1 -0
  11. package/dist/{chunk-XT2EJFCU.js → chunk-SYFNJGH5.js} +30 -8
  12. package/dist/chunk-SYFNJGH5.js.map +1 -0
  13. package/dist/{chunk-RU5G3HNF.js → chunk-XAZTHG6W.js} +20 -9
  14. package/dist/chunk-XAZTHG6W.js.map +1 -0
  15. package/dist/index.js +4 -4
  16. package/dist/kernel.d.ts +0 -19
  17. package/dist/materializer-cli-entry.js +7 -7
  18. package/dist/materializer-cli.d.ts +4 -3
  19. package/dist/materializer-cli.js +7 -7
  20. package/dist/native.d.ts +1 -1
  21. package/dist/native.js +2 -2
  22. package/dist/office-import.d.ts +15 -5
  23. package/dist/office-import.js +25 -8
  24. package/dist/office-import.js.map +1 -1
  25. package/dist/production-document.js +4 -4
  26. package/dist/production-presentation.js +4 -4
  27. package/dist/production-spreadsheet.js +4 -4
  28. package/dist/reference.js +1 -365
  29. package/dist/reference.js.map +1 -1
  30. package/dist/runtime-cli-entry.js +3 -3
  31. package/dist/runtime-cli.js +2 -2
  32. package/dist/runtime-development.js +3 -3
  33. package/dist/runtime-receipt.d.ts +3 -2
  34. package/dist/runtime.d.ts +8 -6
  35. package/dist/runtime.js +1 -1
  36. package/dist/snapshot.d.ts +13 -6
  37. package/dist/snapshot.js +23 -11
  38. package/dist/snapshot.js.map +1 -1
  39. package/dist/spreadsheet-xlsx-codec.js +2 -2
  40. package/dist/spreadsheet.js +1 -1
  41. package/package.json +5 -5
  42. package/src/kernel.ts +1 -397
  43. package/src/materializer-cli.ts +20 -9
  44. package/src/native.ts +7 -5
  45. package/src/office-import.ts +47 -18
  46. package/src/production-spreadsheet-native.ts +4 -3
  47. package/src/runtime-receipt.ts +16 -5
  48. package/src/runtime.ts +22 -10
  49. package/src/snapshot.ts +39 -17
  50. package/src/spreadsheet-formula.ts +35 -7
  51. package/dist/chunk-6PBM7FKI.js.map +0 -1
  52. package/dist/chunk-B2ZJRZDA.js.map +0 -1
  53. package/dist/chunk-N2IHNWBS.js.map +0 -1
  54. package/dist/chunk-RU5G3HNF.js.map +0 -1
  55. package/dist/chunk-XT2EJFCU.js.map +0 -1
  56. /package/dist/{chunk-ZCJYRJGB.js.map → chunk-4XJE7NRA.js.map} +0 -0
  57. /package/dist/{chunk-QZI5POCC.js.map → chunk-OVXX4G35.js.map} +0 -0
@@ -1,6 +1,16 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { decodeEditableArtifactCausalFrontier } from "@opengeni/contracts/editable-artifact-causal-frontier";
3
- import { EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES } from "@opengeni/contracts/editable-artifacts";
3
+ import {
4
+ EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
5
+ currentEditableArtifactCompatibility,
6
+ } from "@opengeni/contracts/editable-artifacts";
7
+ import {
8
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
9
+ DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION,
10
+ PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION,
11
+ SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
12
+ SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
13
+ } from "@opengeni/contracts/editable-artifact-versions";
4
14
  import type { ArtifactSnapshot } from "./snapshot";
5
15
 
6
16
  export type ArtifactOfficeModality = "spreadsheet" | "document" | "presentation";
@@ -23,19 +33,29 @@ export type PreparedArtifactOfficeImport = Readonly<{
23
33
  byteSize: number;
24
34
  contentHash: `sha256:${string}`;
25
35
  stateHash: `sha256:${string}`;
26
- modelSchemaVersion: 1;
36
+ modelSchemaVersion: typeof SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION;
27
37
  kernelVersion: string;
28
38
  coveredCausalFrontier: readonly Readonly<{ replicaId: string; counter: number }>[];
29
- operationProtocolVersion: 1;
30
- crdtStateVersion: 1;
39
+ operationProtocolVersion: typeof COMMITTED_TRANSACTION_PROTOCOL_VERSION;
40
+ crdtStateVersion: typeof SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION;
31
41
  }>
32
42
  | Readonly<{
33
- modality: "document" | "presentation";
43
+ modality: "document";
34
44
  bytes: Uint8Array;
35
45
  byteSize: number;
36
46
  contentHash: `sha256:${string}`;
37
47
  stateHash: `sha256:${string}`;
38
- modelSchemaVersion: 1;
48
+ modelSchemaVersion: typeof DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION;
49
+ kernelVersion: string;
50
+ nativeRevision: number;
51
+ }>
52
+ | Readonly<{
53
+ modality: "presentation";
54
+ bytes: Uint8Array;
55
+ byteSize: number;
56
+ contentHash: `sha256:${string}`;
57
+ stateHash: `sha256:${string}`;
58
+ modelSchemaVersion: typeof PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION;
39
59
  kernelVersion: string;
40
60
  nativeRevision: number;
41
61
  }>;
@@ -119,13 +139,14 @@ function validateSnapshot(
119
139
  expectedKernelVersion: string,
120
140
  ): PreparedArtifactOfficeImport["snapshot"] {
121
141
  const snapshot = exactNamespace(value, "artifact snapshot") as unknown as ArtifactSnapshot;
142
+ const current = currentEditableArtifactCompatibility(modality);
122
143
  if (
123
144
  snapshot.schemaVersion !== 1 ||
124
145
  snapshot.modality !== modality ||
125
146
  snapshot.runtimeTarget !== expectedRuntimeTarget ||
126
147
  snapshot.kernelVersion !== expectedKernelVersion ||
127
- snapshot.modelSchemaVersion !== 1 ||
128
- snapshot.snapshotVersion !== 1 ||
148
+ snapshot.modelSchemaVersion !== current.modelSchemaVersion ||
149
+ snapshot.snapshotVersion !== current.snapshotVersion ||
129
150
  !/^sha256:[0-9a-f]{64}$/u.test(snapshot.stateHash)
130
151
  ) {
131
152
  throw new TypeError("Imported snapshot differs from the verified native runtime boundary");
@@ -143,35 +164,43 @@ function validateSnapshot(
143
164
  byteSize: bytes.byteLength,
144
165
  contentHash: sha256(bytes),
145
166
  stateHash: snapshot.stateHash as `sha256:${string}`,
146
- modelSchemaVersion: 1 as const,
147
167
  kernelVersion: snapshot.kernelVersion,
148
168
  };
149
169
  if (snapshot.modality === "spreadsheet") {
150
170
  if (
151
171
  !(snapshot.coveredCausalFrontierBytes instanceof Uint8Array) ||
152
- snapshot.operationProtocolVersion !== 1 ||
153
- snapshot.crdtStateVersion !== 1
172
+ snapshot.operationProtocolVersion !== COMMITTED_TRANSACTION_PROTOCOL_VERSION ||
173
+ snapshot.crdtStateVersion !== SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION
154
174
  ) {
155
175
  throw new TypeError("Imported spreadsheet snapshot coverage is invalid");
156
176
  }
157
177
  return Object.freeze({
158
178
  ...common,
159
179
  modality: "spreadsheet" as const,
180
+ modelSchemaVersion: SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
160
181
  coveredCausalFrontier: decodeEditableArtifactCausalFrontier(
161
182
  snapshot.coveredCausalFrontierBytes,
162
183
  ),
163
- operationProtocolVersion: 1 as const,
164
- crdtStateVersion: 1 as const,
184
+ operationProtocolVersion: COMMITTED_TRANSACTION_PROTOCOL_VERSION,
185
+ crdtStateVersion: SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
165
186
  });
166
187
  }
167
188
  if (!Number.isSafeInteger(snapshot.nativeRevision) || snapshot.nativeRevision < 0) {
168
189
  throw new TypeError("Imported serialized snapshot revision is invalid");
169
190
  }
170
- return Object.freeze({
171
- ...common,
172
- modality: snapshot.modality,
173
- nativeRevision: snapshot.nativeRevision,
174
- });
191
+ return snapshot.modality === "document"
192
+ ? Object.freeze({
193
+ ...common,
194
+ modality: "document" as const,
195
+ modelSchemaVersion: DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION,
196
+ nativeRevision: snapshot.nativeRevision,
197
+ })
198
+ : Object.freeze({
199
+ ...common,
200
+ modality: "presentation" as const,
201
+ modelSchemaVersion: PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION,
202
+ nativeRevision: snapshot.nativeRevision,
203
+ });
175
204
  }
176
205
 
177
206
  function officeImporter(
@@ -1,4 +1,5 @@
1
1
  import {
2
+ SPREADSHEET_ARTIFACT_COMMAND_VERSION,
2
3
  SPREADSHEET_ARTIFACT_PROJECTION_MAX_BYTES,
3
4
  SPREADSHEET_ARTIFACT_VIEWPORT_MAX_CELLS,
4
5
  decodeSpreadsheetMetadataKernelProjection,
@@ -292,7 +293,7 @@ function nativeFormula(value: unknown): SpreadsheetCellInput {
292
293
  throw new TypeError("Spreadsheet formulas must be strings or null");
293
294
  const trimmed = value.trim();
294
295
  if (trimmed.length === 0) return null;
295
- return { formula: trimmed.startsWith("=") ? trimmed : `=${trimmed}`, cached: null };
296
+ return { formula: trimmed.startsWith("=") ? trimmed : `=${trimmed}` };
296
297
  }
297
298
 
298
299
  function normalizedSheetName(value: unknown): string {
@@ -485,7 +486,7 @@ class SpreadsheetProjectionAuthor {
485
486
  : [{ replicaId: this.#replicaId, counter: this.#transactionCounter }],
486
487
  selectiveUndoOperationIds: [],
487
488
  },
488
- commands: { version: 1, commands },
489
+ commands: { version: SPREADSHEET_ARTIFACT_COMMAND_VERSION, commands },
489
490
  resolvedBaseBytes: this.#session.frontier(),
490
491
  });
491
492
  this.#transactionCounter = counter;
@@ -578,7 +579,7 @@ function* cellCommands(
578
579
  }
579
580
 
580
581
  function nativeCell(cell: SerializedCell): SpreadsheetCellInput {
581
- if (cell.formula !== null) return { formula: cell.formula, cached: null };
582
+ if (cell.formula !== null) return { formula: cell.formula };
582
583
  const value = cell.value;
583
584
  if (typeof value === "object" && value !== null) {
584
585
  if ("type" in value && value.type === "date") {
@@ -19,8 +19,8 @@ const WASM_RUNTIME_FILES = [
19
19
  const MAX_CAPABILITIES_BYTES = 1024 * 1024;
20
20
 
21
21
  export type ArtifactKernelBuildReceipt = Readonly<{
22
- schemaVersion: 1;
23
- producer: "opengeni-artifact-kernel-smoke-v1";
22
+ schemaVersion: 2;
23
+ producer: "opengeni-artifact-kernel-smoke-v2";
24
24
  target: ArtifactRuntimeTarget;
25
25
  kind: "native" | "wasm";
26
26
  buildIdentity: string;
@@ -28,6 +28,7 @@ export type ArtifactKernelBuildReceipt = Readonly<{
28
28
  bytes: number;
29
29
  sha256: `sha256:${string}`;
30
30
  }>;
31
+ spreadsheetFormulaProjectionCorpusSha256: `sha256:${string}`;
31
32
  runtimeFiles: readonly Readonly<{
32
33
  path: string;
33
34
  bytes: number;
@@ -48,11 +49,12 @@ export function validateArtifactKernelBuildReceipt(
48
49
  "kind",
49
50
  "buildIdentity",
50
51
  "capabilities",
52
+ "spreadsheetFormulaProjectionCorpusSha256",
51
53
  "runtimeFiles",
52
54
  ],
53
55
  "target build receipt",
54
56
  );
55
- if (record.schemaVersion !== 1 || record.producer !== "opengeni-artifact-kernel-smoke-v1") {
57
+ if (record.schemaVersion !== 2 || record.producer !== "opengeni-artifact-kernel-smoke-v2") {
56
58
  invalid("target build receipt schema/producer is invalid");
57
59
  }
58
60
  if (
@@ -77,6 +79,13 @@ export function validateArtifactKernelBuildReceipt(
77
79
  if (capabilities.bytes > MAX_CAPABILITIES_BYTES) {
78
80
  invalid("target build capabilities exceed their maximum size");
79
81
  }
82
+ const spreadsheetFormulaProjectionCorpusSha256 = record.spreadsheetFormulaProjectionCorpusSha256;
83
+ if (
84
+ typeof spreadsheetFormulaProjectionCorpusSha256 !== "string" ||
85
+ !/^sha256:[0-9a-f]{64}$/u.test(spreadsheetFormulaProjectionCorpusSha256)
86
+ ) {
87
+ invalid("target build formula projection corpus digest is invalid");
88
+ }
80
89
  if (
81
90
  !Array.isArray(record.runtimeFiles) ||
82
91
  record.runtimeFiles.length === 0 ||
@@ -97,12 +106,14 @@ export function validateArtifactKernelBuildReceipt(
97
106
  invalid("target build receipt runtimeFiles do not match the target package");
98
107
  }
99
108
  return {
100
- schemaVersion: 1,
101
- producer: "opengeni-artifact-kernel-smoke-v1",
109
+ schemaVersion: 2,
110
+ producer: "opengeni-artifact-kernel-smoke-v2",
102
111
  target: descriptor.target,
103
112
  kind: descriptor.kind,
104
113
  buildIdentity: record.buildIdentity,
105
114
  capabilities: { bytes: capabilities.bytes, sha256: capabilities.sha256 },
115
+ spreadsheetFormulaProjectionCorpusSha256:
116
+ spreadsheetFormulaProjectionCorpusSha256 as `sha256:${string}`,
106
117
  runtimeFiles,
107
118
  };
108
119
  }
package/src/runtime.ts CHANGED
@@ -1,3 +1,12 @@
1
+ import {
2
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
3
+ SPREADSHEET_ARTIFACT_COMMAND_VERSION,
4
+ SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
5
+ SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
6
+ SPREADSHEET_KERNEL_COMMAND_VERSION,
7
+ SPREADSHEET_KERNEL_SNAPSHOT_VERSION,
8
+ } from "@opengeni/contracts/editable-artifact-versions";
9
+
1
10
  /**
2
11
  * Exact artifact-kernel distribution and runtime boundary.
3
12
  *
@@ -163,13 +172,14 @@ export type ArtifactRuntimeKernelDependencies = Pick<
163
172
  export type ArtifactKernelCapabilities = {
164
173
  readonly abiVersion: 1;
165
174
  readonly buildIdentityFormat: "utf8";
166
- readonly commandSchemaVersion: 1;
167
- readonly spreadsheetCommandVersion: 1;
168
- readonly kernelSnapshotVersion: 1;
175
+ readonly commandSchemaVersion: typeof SPREADSHEET_KERNEL_COMMAND_VERSION;
176
+ readonly spreadsheetCommandVersion: typeof SPREADSHEET_ARTIFACT_COMMAND_VERSION;
177
+ readonly spreadsheetModelSchemaVersion: typeof SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION;
178
+ readonly kernelSnapshotVersion: typeof SPREADSHEET_KERNEL_SNAPSHOT_VERSION;
169
179
  readonly receiptSchemaVersion: 1;
170
- readonly collaborationSnapshotVersion: 1;
180
+ readonly collaborationSnapshotVersion: typeof SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION;
171
181
  readonly editableArtifactIntentVersion: 1;
172
- readonly committedTransactionVersion: 1;
182
+ readonly committedTransactionVersion: typeof COMMITTED_TRANSACTION_PROTOCOL_VERSION;
173
183
  readonly queryVersion: 1;
174
184
  readonly queryResponseVersion: 1;
175
185
  readonly collaboration: true;
@@ -237,6 +247,7 @@ export type ArtifactRuntimeErrorCode =
237
247
  | "ARTIFACT_RUNTIME_INCOMPATIBLE"
238
248
  | "ARTIFACT_RUNTIME_INTEGRITY"
239
249
  | "ARTIFACT_RUNTIME_MANIFEST_INVALID"
250
+ | "ARTIFACT_RUNTIME_PROJECTION_PARITY"
240
251
  | "ARTIFACT_RUNTIME_UNAVAILABLE"
241
252
  | "ARTIFACT_RUNTIME_UNSUPPORTED_TARGET";
242
253
 
@@ -1208,13 +1219,14 @@ function parseCapabilities(value: unknown): ArtifactKernelCapabilities {
1208
1219
  const required = {
1209
1220
  abiVersion: 1,
1210
1221
  buildIdentityFormat: "utf8",
1211
- commandSchemaVersion: 1,
1212
- spreadsheetCommandVersion: 1,
1213
- kernelSnapshotVersion: 1,
1222
+ commandSchemaVersion: SPREADSHEET_KERNEL_COMMAND_VERSION,
1223
+ spreadsheetCommandVersion: SPREADSHEET_ARTIFACT_COMMAND_VERSION,
1224
+ spreadsheetModelSchemaVersion: SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
1225
+ kernelSnapshotVersion: SPREADSHEET_KERNEL_SNAPSHOT_VERSION,
1214
1226
  receiptSchemaVersion: 1,
1215
- collaborationSnapshotVersion: 1,
1227
+ collaborationSnapshotVersion: SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
1216
1228
  editableArtifactIntentVersion: 1,
1217
- committedTransactionVersion: 1,
1229
+ committedTransactionVersion: COMMITTED_TRANSACTION_PROTOCOL_VERSION,
1218
1230
  queryVersion: 1,
1219
1231
  queryResponseVersion: 1,
1220
1232
  collaboration: true,
package/src/snapshot.ts CHANGED
@@ -1,7 +1,11 @@
1
1
  import {
2
2
  COMMITTED_TRANSACTION_PROTOCOL_VERSION,
3
- EDITABLE_ARTIFACT_MODEL_SCHEMA_VERSION,
4
- EDITABLE_ARTIFACT_SNAPSHOT_VERSION,
3
+ DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION,
4
+ DOCUMENT_ARTIFACT_SNAPSHOT_VERSION,
5
+ PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION,
6
+ PRESENTATION_ARTIFACT_SNAPSHOT_VERSION,
7
+ SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
8
+ SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
5
9
  } from "@opengeni/contracts/editable-artifact-versions";
6
10
  import type { Document } from "./document";
7
11
  import {
@@ -15,11 +19,8 @@ import type { Workbook } from "./spreadsheet";
15
19
 
16
20
  type ArtifactSnapshotCommon = Readonly<{
17
21
  schemaVersion: 1;
18
- modality: "spreadsheet" | "document" | "presentation";
19
22
  runtimeTarget: string;
20
23
  kernelVersion: string;
21
- modelSchemaVersion: 1;
22
- snapshotVersion: 1;
23
24
  stateHash: string;
24
25
  snapshotBytes: Uint8Array;
25
26
  }>;
@@ -29,13 +30,24 @@ export type ArtifactSnapshot =
29
30
  | (ArtifactSnapshotCommon &
30
31
  Readonly<{
31
32
  modality: "spreadsheet";
33
+ modelSchemaVersion: typeof SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION;
34
+ snapshotVersion: typeof SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION;
32
35
  coveredCausalFrontierBytes: Uint8Array;
33
- operationProtocolVersion: 1;
34
- crdtStateVersion: 1;
36
+ operationProtocolVersion: typeof COMMITTED_TRANSACTION_PROTOCOL_VERSION;
37
+ crdtStateVersion: typeof SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION;
35
38
  }>)
36
39
  | (ArtifactSnapshotCommon &
37
40
  Readonly<{
38
- modality: "document" | "presentation";
41
+ modality: "document";
42
+ modelSchemaVersion: typeof DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION;
43
+ snapshotVersion: typeof DOCUMENT_ARTIFACT_SNAPSHOT_VERSION;
44
+ nativeRevision: number;
45
+ }>)
46
+ | (ArtifactSnapshotCommon &
47
+ Readonly<{
48
+ modality: "presentation";
49
+ modelSchemaVersion: typeof PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION;
50
+ snapshotVersion: typeof PRESENTATION_ARTIFACT_SNAPSHOT_VERSION;
39
51
  nativeRevision: number;
40
52
  }>);
41
53
 
@@ -47,11 +59,8 @@ export function createArtifactSnapshot(
47
59
  const native = state.native;
48
60
  const common = {
49
61
  schemaVersion: 1 as const,
50
- modality: state.modality,
51
62
  runtimeTarget: native.target,
52
63
  kernelVersion: native.buildIdentity,
53
- modelSchemaVersion: EDITABLE_ARTIFACT_MODEL_SCHEMA_VERSION,
54
- snapshotVersion: EDITABLE_ARTIFACT_SNAPSHOT_VERSION,
55
64
  stateHash: native.stateHash(),
56
65
  snapshotBytes: Uint8Array.from(native.snapshot()),
57
66
  };
@@ -62,9 +71,11 @@ export function createArtifactSnapshot(
62
71
  return Object.freeze({
63
72
  ...common,
64
73
  modality: "spreadsheet" as const,
74
+ modelSchemaVersion: SPREADSHEET_ARTIFACT_MODEL_SCHEMA_VERSION,
75
+ snapshotVersion: SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
65
76
  coveredCausalFrontierBytes: Uint8Array.from(native.frontier()),
66
77
  operationProtocolVersion: COMMITTED_TRANSACTION_PROTOCOL_VERSION,
67
- crdtStateVersion: EDITABLE_ARTIFACT_SNAPSHOT_VERSION,
78
+ crdtStateVersion: SPREADSHEET_COLLABORATION_SNAPSHOT_VERSION,
68
79
  });
69
80
  }
70
81
  if (
@@ -73,11 +84,22 @@ export function createArtifactSnapshot(
73
84
  ) {
74
85
  throw new Error("Serialized composite does not own its expected native session");
75
86
  }
76
- return Object.freeze({
77
- ...common,
78
- modality: state.modality,
79
- nativeRevision: safeNativeRevision(native.revision()),
80
- });
87
+ const nativeRevision = safeNativeRevision(native.revision());
88
+ return state.modality === "document"
89
+ ? Object.freeze({
90
+ ...common,
91
+ modality: "document" as const,
92
+ modelSchemaVersion: DOCUMENT_ARTIFACT_MODEL_SCHEMA_VERSION,
93
+ snapshotVersion: DOCUMENT_ARTIFACT_SNAPSHOT_VERSION,
94
+ nativeRevision,
95
+ })
96
+ : Object.freeze({
97
+ ...common,
98
+ modality: "presentation" as const,
99
+ modelSchemaVersion: PRESENTATION_ARTIFACT_MODEL_SCHEMA_VERSION,
100
+ snapshotVersion: PRESENTATION_ARTIFACT_SNAPSHOT_VERSION,
101
+ nativeRevision,
102
+ });
81
103
  }
82
104
 
83
105
  function safeNativeRevision(value: bigint): number {
@@ -509,7 +509,9 @@ class FormulaParser {
509
509
  if (this.isOperator("^")) {
510
510
  this.consume();
511
511
  const right = this.withNesting(() => this.power());
512
- left = calculate(left, right, (a, b) => finiteNumber(Math.pow(numeric(a), numeric(b))));
512
+ left = calculate(left, right, (a, b) =>
513
+ finiteNumber(deterministicIntegerPower(numeric(a), numeric(b))),
514
+ );
513
515
  }
514
516
  return left;
515
517
  }
@@ -974,24 +976,24 @@ function callFunction(name: string, args: Evaluated[], budget: FormulaEvaluation
974
976
  return Math.abs(numeric(args[0] ?? 0));
975
977
  case "ROUND": {
976
978
  const digits = numeric(args[1] ?? 0);
977
- const factor = 10 ** digits;
979
+ const factor = deterministicIntegerPower(10, digits);
978
980
  const value = numeric(args[0] ?? 0) * factor;
979
981
  return (Math.sign(value) * Math.round(Math.abs(value))) / factor;
980
982
  }
981
983
  case "ROUNDUP": {
982
984
  const digits = numeric(args[1] ?? 0);
983
- const factor = 10 ** digits;
985
+ const factor = deterministicIntegerPower(10, digits);
984
986
  const value = numeric(args[0] ?? 0) * factor;
985
987
  return (Math.sign(value) * Math.ceil(Math.abs(value))) / factor;
986
988
  }
987
989
  case "ROUNDDOWN": {
988
990
  const digits = numeric(args[1] ?? 0);
989
- const factor = 10 ** digits;
991
+ const factor = deterministicIntegerPower(10, digits);
990
992
  const value = numeric(args[0] ?? 0) * factor;
991
993
  return (Math.sign(value) * Math.floor(Math.abs(value))) / factor;
992
994
  }
993
995
  case "POWER":
994
- return finiteNumber(Math.pow(numeric(args[0] ?? 0), numeric(args[1] ?? 0)));
996
+ return finiteNumber(deterministicIntegerPower(numeric(args[0] ?? 0), numeric(args[1] ?? 0)));
995
997
  case "SQRT": {
996
998
  const value = numeric(args[0] ?? 0);
997
999
  if (value < 0) throw new FormulaFault("#NUM!");
@@ -1067,7 +1069,30 @@ function dateValue(value: Evaluated | undefined): Date {
1067
1069
 
1068
1070
  function finiteNumber(value: number): number {
1069
1071
  if (!Number.isFinite(value)) throw new FormulaFault("#NUM!");
1070
- return value;
1072
+ return value === 0 ? 0 : value;
1073
+ }
1074
+
1075
+ /** Mirrors the kernel's explicitly ordered path for exact integer exponents. */
1076
+ function deterministicIntegerPower(base: number, exponent: number): number {
1077
+ if (
1078
+ Number.isFinite(exponent) &&
1079
+ Number.isInteger(exponent) &&
1080
+ Math.abs(exponent) <= Number.MAX_SAFE_INTEGER
1081
+ ) {
1082
+ const reciprocal = exponent < 0;
1083
+ let remaining = Math.abs(exponent);
1084
+ // Match the kernel at underflow boundaries: forming the positive power
1085
+ // first can overflow before a representable reciprocal is taken.
1086
+ let factor = reciprocal ? 1 / base : base;
1087
+ let result = 1;
1088
+ while (remaining !== 0) {
1089
+ if (remaining % 2 === 1) result *= factor;
1090
+ remaining = Math.floor(remaining / 2);
1091
+ if (remaining !== 0) factor *= factor;
1092
+ }
1093
+ return result;
1094
+ }
1095
+ return Math.pow(base, exponent);
1071
1096
  }
1072
1097
 
1073
1098
  function boundedText(value: string, maximum: number): string {
@@ -1079,7 +1104,10 @@ function boundedText(value: string, maximum: number): string {
1079
1104
 
1080
1105
  function boundedScalar(value: Scalar, maximum: number): Scalar {
1081
1106
  if (typeof value === "string") return boundedText(value, maximum);
1082
- if (typeof value === "number" && !Number.isFinite(value)) throw new FormulaFault("#NUM!");
1107
+ if (typeof value === "number") {
1108
+ if (!Number.isFinite(value)) throw new FormulaFault("#NUM!");
1109
+ return value === 0 ? 0 : value;
1110
+ }
1083
1111
  return value;
1084
1112
  }
1085
1113