@opengeni/sdk 0.46.0 → 0.52.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.
- package/README.md +94 -2
- package/dist/artifact-client.d.ts +20 -2
- package/dist/artifacts.d.ts +4 -0
- package/dist/artifacts.js +11 -0
- package/dist/artifacts.js.map +1 -0
- package/dist/chunk-FHI4DFIG.js +128 -0
- package/dist/chunk-FHI4DFIG.js.map +1 -0
- package/dist/chunk-FRJFNDIR.js +218 -0
- package/dist/chunk-FRJFNDIR.js.map +1 -0
- package/dist/{chunk-QKV5OCLJ.js → chunk-ILQZR5N6.js} +1380 -595
- package/dist/chunk-ILQZR5N6.js.map +1 -0
- package/dist/chunk-MZWTWOX6.js +661 -0
- package/dist/chunk-MZWTWOX6.js.map +1 -0
- package/dist/chunk-VWE2QIVO.js +1162 -0
- package/dist/chunk-VWE2QIVO.js.map +1 -0
- package/dist/chunk-VYCTL62C.js +230 -0
- package/dist/chunk-VYCTL62C.js.map +1 -0
- package/dist/client.d.ts +176 -20
- package/dist/codex-realtime-controller.d.ts +6 -6
- package/dist/codex-realtime-lifecycle.d.ts +1 -1
- package/dist/codex-realtime-v3.d.ts +3 -3
- package/dist/codex-realtime.d.ts +1 -1
- package/dist/company-profile.d.ts +100 -0
- package/dist/core.d.ts +5 -5
- package/dist/core.js +9 -4
- package/dist/desktop.d.ts +1 -1
- package/dist/editable-artifact-resources.d.ts +96 -0
- package/dist/editable-artifacts/browser-session.d.ts +35 -0
- package/dist/editable-artifacts/controller.d.ts +60 -0
- package/dist/editable-artifacts/errors.d.ts +23 -0
- package/dist/editable-artifacts/http-live-transport.d.ts +46 -0
- package/dist/editable-artifacts/index.d.ts +19 -0
- package/dist/editable-artifacts/pool.d.ts +15 -0
- package/dist/editable-artifacts/session.d.ts +96 -0
- package/dist/editable-artifacts/storage.d.ts +72 -0
- package/dist/editable-artifacts/types.d.ts +416 -0
- package/dist/editable-artifacts/worker/browser-client.d.ts +80 -0
- package/dist/editable-artifacts/worker/browser-entry.d.ts +6 -0
- package/dist/editable-artifacts/worker/kernel-adapter.d.ts +45 -0
- package/dist/editable-artifacts/worker/rpc-protocol.d.ts +92 -0
- package/dist/editable-artifacts/worker/runtime.d.ts +66 -0
- package/dist/editable-artifacts/worker/wire-codec.d.ts +52 -0
- package/dist/editable-artifacts-worker.d.ts +8 -0
- package/dist/editable-artifacts-worker.js +1708 -0
- package/dist/editable-artifacts-worker.js.map +1 -0
- package/dist/editable-artifacts.d.ts +6 -0
- package/dist/editable-artifacts.js +5954 -0
- package/dist/editable-artifacts.js.map +1 -0
- package/dist/gateway-realtime-transport.d.ts +1 -1
- package/dist/index.d.ts +41 -35
- package/dist/index.js +104 -68
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +885 -0
- package/dist/interaction.js +55 -0
- package/dist/interaction.js.map +1 -0
- package/dist/memory-slack-client.d.ts +13 -0
- package/dist/memory-slack-delivery.d.ts +99 -0
- package/dist/memory-slack.d.ts +4 -0
- package/dist/memory-slack.js +44 -0
- package/dist/memory-slack.js.map +1 -0
- package/dist/proxy.d.ts +3 -3
- package/dist/realtime.d.ts +12 -12
- package/dist/retained-artifacts.d.ts +12 -0
- package/dist/stream.d.ts +16 -1
- package/dist/terminal.d.ts +1 -1
- package/dist/transcription.d.ts +2 -2
- package/dist/types.d.ts +1203 -23
- package/dist/workspace-control-stream.d.ts +2 -2
- package/dist/workspace-state.d.ts +1 -1
- package/package.json +29 -3
- package/src/artifact-client.ts +129 -0
- package/src/artifacts.ts +17 -0
- package/src/client.ts +1801 -206
- package/src/company-profile.ts +92 -0
- package/src/editable-artifact-resources.ts +119 -0
- package/src/editable-artifacts/browser-session.ts +176 -0
- package/src/editable-artifacts/controller.ts +2632 -0
- package/src/editable-artifacts/errors.ts +52 -0
- package/src/editable-artifacts/http-live-transport.ts +1449 -0
- package/src/editable-artifacts/index.ts +175 -0
- package/src/editable-artifacts/pool.ts +81 -0
- package/src/editable-artifacts/session.ts +778 -0
- package/src/editable-artifacts/storage.ts +1718 -0
- package/src/editable-artifacts/types.ts +499 -0
- package/src/editable-artifacts/worker/browser-client.ts +1167 -0
- package/src/editable-artifacts/worker/browser-entry.ts +57 -0
- package/src/editable-artifacts/worker/kernel-adapter.ts +1039 -0
- package/src/editable-artifacts/worker/rpc-protocol.ts +622 -0
- package/src/editable-artifacts/worker/runtime.ts +1204 -0
- package/src/editable-artifacts/worker/wire-codec.ts +852 -0
- package/src/editable-artifacts-worker.ts +8 -0
- package/src/editable-artifacts.ts +6 -0
- package/src/index.ts +194 -0
- package/src/interaction.ts +1830 -0
- package/src/memory-slack-client.ts +71 -0
- package/src/memory-slack-delivery.ts +128 -0
- package/src/memory-slack.ts +19 -0
- package/src/retained-artifacts.ts +326 -0
- package/src/stream.ts +102 -4
- package/src/transcription.ts +2 -2
- package/src/types.ts +1430 -33
- package/src/workspace-control-stream.ts +15 -5
- package/dist/chunk-QKV5OCLJ.js.map +0 -1
|
@@ -0,0 +1,1039 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDITABLE_ARTIFACT_COMMAND_MAX_BYTES,
|
|
3
|
+
EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
|
|
4
|
+
EDITABLE_ARTIFACT_INTENT_VERSION,
|
|
5
|
+
EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
|
|
6
|
+
SPREADSHEET_ARTIFACT_COMMAND_VERSION,
|
|
7
|
+
} from "@opengeni/contracts/editable-artifacts";
|
|
8
|
+
import { MAX_COMMITTED_TRANSACTION_BYTES } from "@opengeni/contracts/editable-artifact-committed-transaction";
|
|
9
|
+
import type { ArtifactWorkerInitializeInput } from "./wire-codec";
|
|
10
|
+
import type { EditableArtifactModality } from "../types";
|
|
11
|
+
import { ArtifactWorkerProtocolError } from "./rpc-protocol";
|
|
12
|
+
|
|
13
|
+
export type ArtifactWorkerKernelSession = {
|
|
14
|
+
readonly modality: EditableArtifactModality;
|
|
15
|
+
/** Replays one exact whole canonical OGACO001 transaction. */
|
|
16
|
+
applyCommitted: (committedTransactionBytes: Uint8Array) => void;
|
|
17
|
+
/** Authors and applies one exact OGATX001 intent over the current frontier. */
|
|
18
|
+
applyPending: (intentBytes: Uint8Array) => void;
|
|
19
|
+
/** Applies one exact document/presentation command and returns its native receipt. */
|
|
20
|
+
applyCommands: (commandBytes: Uint8Array) => Uint8Array;
|
|
21
|
+
/** Native serialized-model revision; unavailable for spreadsheets. */
|
|
22
|
+
nativeRevision: () => number;
|
|
23
|
+
/** Stages an atomic candidate without serializing/reopening the model. */
|
|
24
|
+
fork: () => ArtifactWorkerKernelSession;
|
|
25
|
+
/** Kernel-defined canonical hash; never synthesized from transport bytes. */
|
|
26
|
+
stateHash: () => Promise<string>;
|
|
27
|
+
snapshot: () => Uint8Array;
|
|
28
|
+
/** Canonical bounded OGAKQ request to canonical OGAKV projection. */
|
|
29
|
+
query: (queryBytes: Uint8Array) => Uint8Array;
|
|
30
|
+
dispose: () => void;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** A valid local intent whose structural preconditions no longer project. */
|
|
34
|
+
export class ArtifactWorkerPendingProjectionError extends Error {
|
|
35
|
+
readonly code: string;
|
|
36
|
+
|
|
37
|
+
constructor(code: string, message = "pending transaction cannot project over confirmed state") {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "ArtifactWorkerPendingProjectionError";
|
|
40
|
+
this.code = requireErrorCode(code, "pending projection code");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Entirely Worker-owned adapter over one compatible collaboration WASM build. */
|
|
45
|
+
export type ArtifactWorkerKernelAdapter = {
|
|
46
|
+
readonly modality: EditableArtifactModality;
|
|
47
|
+
readonly protocolVersion: number;
|
|
48
|
+
readonly kernelVersion: string;
|
|
49
|
+
readonly modelSchemaVersion: number;
|
|
50
|
+
readonly commandVersion: number;
|
|
51
|
+
readonly maximumSnapshotBytes: number;
|
|
52
|
+
readonly maximumCommandBytes: number;
|
|
53
|
+
readonly maximumIntentBytes: number;
|
|
54
|
+
readonly maximumCommittedTransactionBytes: number;
|
|
55
|
+
readonly maximumQueryBytes: number;
|
|
56
|
+
readonly maximumQueryResponseBytes: number;
|
|
57
|
+
canonicalizeSnapshot: (snapshotBytes: Uint8Array) => Uint8Array;
|
|
58
|
+
open: (snapshotBytes: Uint8Array) => ArtifactWorkerKernelSession;
|
|
59
|
+
dispose?: () => void;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type ArtifactWorkerKernelAdapterFactory = (
|
|
63
|
+
input: ArtifactWorkerInitializeInput,
|
|
64
|
+
) => Promise<ArtifactWorkerKernelAdapter>;
|
|
65
|
+
|
|
66
|
+
type WasmCollaborationSession = {
|
|
67
|
+
authorTransaction: (intentBytes: Uint8Array, resolvedBaseBytes: Uint8Array) => Uint8Array;
|
|
68
|
+
applyCommitted: (committedTransactionBytes: Uint8Array) => void;
|
|
69
|
+
snapshot: () => Uint8Array;
|
|
70
|
+
frontier: () => Uint8Array;
|
|
71
|
+
fork: () => WasmCollaborationSession;
|
|
72
|
+
stateHash: () => string;
|
|
73
|
+
revision: () => bigint;
|
|
74
|
+
query: (queryEnvelope: Uint8Array) => Uint8Array;
|
|
75
|
+
isClosed?: () => boolean;
|
|
76
|
+
close?: () => void;
|
|
77
|
+
dispose?: () => void;
|
|
78
|
+
free?: () => void;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
type WasmModalitySession = {
|
|
82
|
+
applyCommands: (commandBytes: Uint8Array) => Uint8Array;
|
|
83
|
+
snapshot: () => Uint8Array;
|
|
84
|
+
revision: () => bigint;
|
|
85
|
+
fork: () => WasmModalitySession;
|
|
86
|
+
stateHash: () => string;
|
|
87
|
+
query: (queryEnvelope: Uint8Array) => Uint8Array;
|
|
88
|
+
isClosed?: () => boolean;
|
|
89
|
+
close?: () => void;
|
|
90
|
+
dispose?: () => void;
|
|
91
|
+
free?: () => void;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
type WasmKernelModuleBase = {
|
|
95
|
+
default?: (input: { module_or_path: string | URL }) => Promise<unknown>;
|
|
96
|
+
capabilities: () => Uint8Array;
|
|
97
|
+
buildIdentity: () => Uint8Array;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
type WasmKernelModule =
|
|
101
|
+
| (WasmKernelModuleBase &
|
|
102
|
+
Readonly<{
|
|
103
|
+
modality: "spreadsheet";
|
|
104
|
+
canonicalizeSnapshot: (snapshotBytes: Uint8Array) => Uint8Array;
|
|
105
|
+
open: (snapshotBytes: Uint8Array) => WasmCollaborationSession;
|
|
106
|
+
}>)
|
|
107
|
+
| (WasmKernelModuleBase &
|
|
108
|
+
Readonly<{
|
|
109
|
+
modality: "document" | "presentation";
|
|
110
|
+
canonicalizeSnapshot: (snapshotBytes: Uint8Array) => Uint8Array;
|
|
111
|
+
open: (snapshotBytes: Uint8Array) => WasmModalitySession;
|
|
112
|
+
}>);
|
|
113
|
+
|
|
114
|
+
type WasmCapabilities = Readonly<{
|
|
115
|
+
abiVersion: number;
|
|
116
|
+
buildIdentityFormat: "utf8";
|
|
117
|
+
canonicalStateHash: "sha256:canonical-snapshot";
|
|
118
|
+
collaboration: boolean;
|
|
119
|
+
collaborationSnapshotVersion: number;
|
|
120
|
+
commandSchemaVersion: number;
|
|
121
|
+
committedTransactionVersion: number;
|
|
122
|
+
document: boolean;
|
|
123
|
+
documentCommandVersion: number;
|
|
124
|
+
documentQueryResponseVersion: number;
|
|
125
|
+
documentQueryVersion: number;
|
|
126
|
+
documentReceiptVersion: number;
|
|
127
|
+
documentSnapshotVersion: number;
|
|
128
|
+
documentStatefulSessions: boolean;
|
|
129
|
+
editableArtifactIntentVersion: number;
|
|
130
|
+
kernelSnapshotVersion: number;
|
|
131
|
+
spreadsheetCommandVersion: number;
|
|
132
|
+
maxCellsPerBatch: number;
|
|
133
|
+
maxCommandBytes: number;
|
|
134
|
+
maxCommands: number;
|
|
135
|
+
maxSnapshotBytes: number;
|
|
136
|
+
maxSpreadsheetCommandBytes: number;
|
|
137
|
+
maxIntentBytes: number;
|
|
138
|
+
maxCommittedTransactionBytes: number;
|
|
139
|
+
maxDocumentCommandBytes: number;
|
|
140
|
+
maxDocumentCommands: number;
|
|
141
|
+
maxDocumentQueryBytes: number;
|
|
142
|
+
maxDocumentQueryResponseBytes: number;
|
|
143
|
+
maxDocumentSnapshotBytes: number;
|
|
144
|
+
maxMetadataScannedCells: number;
|
|
145
|
+
maxQueryBytes: number;
|
|
146
|
+
maxQueryResponseBytes: number;
|
|
147
|
+
maxViewportArea: number;
|
|
148
|
+
maxViewportCells: number;
|
|
149
|
+
maxMetadataSheets: number;
|
|
150
|
+
maxPresentationCommandBytes: number;
|
|
151
|
+
maxPresentationQueryBytes: number;
|
|
152
|
+
maxPresentationResponseBytes: number;
|
|
153
|
+
maxPresentationSnapshotBytes: number;
|
|
154
|
+
maxTextLayoutFontBundleBytes: number;
|
|
155
|
+
maxTextLayoutRequestBytes: number;
|
|
156
|
+
maxTextLayoutResponseBytes: number;
|
|
157
|
+
presentation: boolean;
|
|
158
|
+
presentationCommandVersion: number;
|
|
159
|
+
presentationQueryResponseVersion: number;
|
|
160
|
+
presentationQueryVersion: number;
|
|
161
|
+
presentationSnapshotVersion: number;
|
|
162
|
+
presentationStatefulSessions: boolean;
|
|
163
|
+
queryResponseVersion: number;
|
|
164
|
+
queryVersion: number;
|
|
165
|
+
receiptSchemaVersion: number;
|
|
166
|
+
retainedRenderPatchVersion: number;
|
|
167
|
+
retainedRenderTileVersion: number;
|
|
168
|
+
safeRust: true;
|
|
169
|
+
statefulSessions: boolean;
|
|
170
|
+
textLayout: boolean;
|
|
171
|
+
textLayoutFontBundleVersion: number;
|
|
172
|
+
textLayoutRequestVersion: number;
|
|
173
|
+
textLayoutResponseVersion: number;
|
|
174
|
+
textLayoutStatefulSessions: boolean;
|
|
175
|
+
sessionForks: boolean;
|
|
176
|
+
transport: "bounded-uint8array";
|
|
177
|
+
workbookMetadataQueries: boolean;
|
|
178
|
+
}>;
|
|
179
|
+
|
|
180
|
+
const MAX_BUILD_IDENTITY_BYTES = 512;
|
|
181
|
+
const MAX_CAPABILITIES_BYTES = 8 * 1024;
|
|
182
|
+
const MAX_FRONTIER_BYTES = 8 + 2 + 2 + 4 + 1_024 * 16 + 8;
|
|
183
|
+
const WASM_MAX_CELLS_PER_BATCH = 500_000;
|
|
184
|
+
const WASM_MAX_COMMAND_BYTES = 8 * 1024 * 1024;
|
|
185
|
+
const WASM_MAX_SNAPSHOT_BYTES = EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES;
|
|
186
|
+
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/u;
|
|
187
|
+
|
|
188
|
+
export const loadBrowserWasmKernelAdapter: ArtifactWorkerKernelAdapterFactory = async (input) => {
|
|
189
|
+
const rawWasm = await import(/* @vite-ignore */ input.wasmGlueUrl);
|
|
190
|
+
const wasm = validateWasmModule(rawWasm, input.modality);
|
|
191
|
+
if (wasm.default) await wasm.default({ module_or_path: input.wasmBinaryUrl });
|
|
192
|
+
|
|
193
|
+
const buildIdentity = decodeUtf8(
|
|
194
|
+
requireBoundedBytes(wasm.buildIdentity(), "WASM build identity", MAX_BUILD_IDENTITY_BYTES),
|
|
195
|
+
"WASM build identity",
|
|
196
|
+
);
|
|
197
|
+
if (buildIdentity.length === 0) {
|
|
198
|
+
throw adapterError("incompatible_wasm_build", "WASM build identity is empty");
|
|
199
|
+
}
|
|
200
|
+
const capabilities = decodeCapabilities(
|
|
201
|
+
requireBoundedBytes(wasm.capabilities(), "WASM capabilities", MAX_CAPABILITIES_BYTES),
|
|
202
|
+
);
|
|
203
|
+
assertRequestedLimits(input, capabilities);
|
|
204
|
+
|
|
205
|
+
const profile = capabilityProfile(input.modality, capabilities);
|
|
206
|
+
const adapter: ArtifactWorkerKernelAdapter = {
|
|
207
|
+
modality: input.modality,
|
|
208
|
+
protocolVersion: capabilities.editableArtifactIntentVersion,
|
|
209
|
+
kernelVersion: buildIdentity,
|
|
210
|
+
modelSchemaVersion: profile.modelSchemaVersion,
|
|
211
|
+
commandVersion: profile.commandVersion,
|
|
212
|
+
maximumSnapshotBytes: profile.maximumSnapshotBytes,
|
|
213
|
+
maximumCommandBytes: profile.maximumCommandBytes,
|
|
214
|
+
maximumIntentBytes: capabilities.maxIntentBytes,
|
|
215
|
+
maximumCommittedTransactionBytes: capabilities.maxCommittedTransactionBytes,
|
|
216
|
+
maximumQueryBytes: profile.maximumQueryBytes,
|
|
217
|
+
maximumQueryResponseBytes: profile.maximumQueryResponseBytes,
|
|
218
|
+
canonicalizeSnapshot(snapshotBytes) {
|
|
219
|
+
return requireOwnedOutput(
|
|
220
|
+
wasm.canonicalizeSnapshot(snapshotBytes),
|
|
221
|
+
`canonical ${input.modality} snapshot`,
|
|
222
|
+
input.maximumSnapshotBytes,
|
|
223
|
+
);
|
|
224
|
+
},
|
|
225
|
+
open(snapshotBytes) {
|
|
226
|
+
if (wasm.modality === "spreadsheet") {
|
|
227
|
+
return wrapWasmCollaborationSession(wasm.open(snapshotBytes), input);
|
|
228
|
+
}
|
|
229
|
+
return wrapWasmModalitySession(wasm.open(snapshotBytes), input);
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
return Object.freeze(adapter);
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
function wrapWasmCollaborationSession(
|
|
236
|
+
session: WasmCollaborationSession,
|
|
237
|
+
limits: ArtifactWorkerInitializeInput,
|
|
238
|
+
): ArtifactWorkerKernelSession {
|
|
239
|
+
requireSessionAbi(session);
|
|
240
|
+
let disposed = false;
|
|
241
|
+
const requireOpen = (): void => {
|
|
242
|
+
if (disposed || session.isClosed?.() === true) {
|
|
243
|
+
throw adapterError("session_closed", "artifact collaboration session is closed");
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
return {
|
|
247
|
+
modality: "spreadsheet",
|
|
248
|
+
applyCommitted(committedTransactionBytes) {
|
|
249
|
+
requireOpen();
|
|
250
|
+
requireInputBytes(
|
|
251
|
+
committedTransactionBytes,
|
|
252
|
+
"committed transaction",
|
|
253
|
+
limits.maximumCommittedTransactionBytes,
|
|
254
|
+
);
|
|
255
|
+
session.applyCommitted(committedTransactionBytes);
|
|
256
|
+
},
|
|
257
|
+
applyPending(intentBytes) {
|
|
258
|
+
requireOpen();
|
|
259
|
+
requireInputBytes(intentBytes, "pending intent", limits.maximumIntentBytes);
|
|
260
|
+
const frontier = requireOwnedOutput(
|
|
261
|
+
session.frontier(),
|
|
262
|
+
"causal frontier",
|
|
263
|
+
MAX_FRONTIER_BYTES,
|
|
264
|
+
);
|
|
265
|
+
try {
|
|
266
|
+
// The exact OGATX contains its sole OGASC mutation payload. The return
|
|
267
|
+
// value is a whole OGACO proof for this speculative branch; validating
|
|
268
|
+
// its bound is sufficient because speculative OGACO is never persisted.
|
|
269
|
+
requireOwnedOutput(
|
|
270
|
+
session.authorTransaction(intentBytes, frontier),
|
|
271
|
+
"speculative committed transaction",
|
|
272
|
+
limits.maximumCommittedTransactionBytes,
|
|
273
|
+
);
|
|
274
|
+
} catch (error) {
|
|
275
|
+
const projectionCode = classifyProjectionFailure(error);
|
|
276
|
+
if (projectionCode !== null) {
|
|
277
|
+
throw new ArtifactWorkerPendingProjectionError(projectionCode);
|
|
278
|
+
}
|
|
279
|
+
throw error;
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
applyCommands() {
|
|
283
|
+
throw adapterError(
|
|
284
|
+
"unsupported_modality",
|
|
285
|
+
"spreadsheet session does not apply serialized commands",
|
|
286
|
+
);
|
|
287
|
+
},
|
|
288
|
+
nativeRevision() {
|
|
289
|
+
throw adapterError("unsupported_modality", "spreadsheet session has no native revision");
|
|
290
|
+
},
|
|
291
|
+
fork() {
|
|
292
|
+
requireOpen();
|
|
293
|
+
return wrapWasmCollaborationSession(session.fork(), limits);
|
|
294
|
+
},
|
|
295
|
+
async stateHash() {
|
|
296
|
+
requireOpen();
|
|
297
|
+
const result = session.stateHash();
|
|
298
|
+
if (!SHA256_PATTERN.test(result)) {
|
|
299
|
+
throw adapterError("invalid_state_hash", "WASM kernel returned an invalid state hash");
|
|
300
|
+
}
|
|
301
|
+
return result;
|
|
302
|
+
},
|
|
303
|
+
snapshot() {
|
|
304
|
+
requireOpen();
|
|
305
|
+
return requireOwnedOutput(
|
|
306
|
+
session.snapshot(),
|
|
307
|
+
"collaboration snapshot",
|
|
308
|
+
limits.maximumSnapshotBytes,
|
|
309
|
+
);
|
|
310
|
+
},
|
|
311
|
+
query(queryBytes) {
|
|
312
|
+
requireOpen();
|
|
313
|
+
requireInputBytes(queryBytes, "kernel query", limits.maximumQueryBytes);
|
|
314
|
+
return requireOwnedOutput(
|
|
315
|
+
session.query(queryBytes),
|
|
316
|
+
"kernel projection",
|
|
317
|
+
limits.maximumQueryResponseBytes,
|
|
318
|
+
);
|
|
319
|
+
},
|
|
320
|
+
dispose() {
|
|
321
|
+
if (disposed) return;
|
|
322
|
+
disposed = true;
|
|
323
|
+
try {
|
|
324
|
+
session.dispose?.();
|
|
325
|
+
} finally {
|
|
326
|
+
try {
|
|
327
|
+
session.close?.();
|
|
328
|
+
} finally {
|
|
329
|
+
session.free?.();
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function wrapWasmModalitySession(
|
|
337
|
+
session: WasmModalitySession,
|
|
338
|
+
limits: ArtifactWorkerInitializeInput,
|
|
339
|
+
): ArtifactWorkerKernelSession {
|
|
340
|
+
requireModalitySessionAbi(session);
|
|
341
|
+
let disposed = false;
|
|
342
|
+
const requireOpen = (): void => {
|
|
343
|
+
if (disposed || session.isClosed?.() === true) {
|
|
344
|
+
throw adapterError("session_closed", `artifact ${limits.modality} session is closed`);
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
return {
|
|
348
|
+
modality: limits.modality,
|
|
349
|
+
applyCommitted() {
|
|
350
|
+
throw adapterError(
|
|
351
|
+
"unsupported_modality",
|
|
352
|
+
"serialized session requires decoded OGAST replay",
|
|
353
|
+
);
|
|
354
|
+
},
|
|
355
|
+
applyPending() {
|
|
356
|
+
throw adapterError("unsupported_modality", "serialized session requires exact command bytes");
|
|
357
|
+
},
|
|
358
|
+
applyCommands(commandBytes) {
|
|
359
|
+
requireOpen();
|
|
360
|
+
requireInputBytes(commandBytes, `${limits.modality} command`, limits.maximumCommandBytes);
|
|
361
|
+
return requireOwnedOutput(
|
|
362
|
+
session.applyCommands(commandBytes),
|
|
363
|
+
`${limits.modality} native receipt`,
|
|
364
|
+
limits.maximumCommittedTransactionBytes,
|
|
365
|
+
);
|
|
366
|
+
},
|
|
367
|
+
nativeRevision() {
|
|
368
|
+
requireOpen();
|
|
369
|
+
const revision = session.revision();
|
|
370
|
+
if (revision < 0n || revision > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
371
|
+
throw adapterError("invalid_revision", "WASM kernel returned an unsafe native revision");
|
|
372
|
+
}
|
|
373
|
+
return Number(revision);
|
|
374
|
+
},
|
|
375
|
+
fork() {
|
|
376
|
+
requireOpen();
|
|
377
|
+
return wrapWasmModalitySession(session.fork(), limits);
|
|
378
|
+
},
|
|
379
|
+
async stateHash() {
|
|
380
|
+
requireOpen();
|
|
381
|
+
const result = session.stateHash();
|
|
382
|
+
if (!SHA256_PATTERN.test(result)) {
|
|
383
|
+
throw adapterError("invalid_state_hash", "WASM kernel returned an invalid state hash");
|
|
384
|
+
}
|
|
385
|
+
return result;
|
|
386
|
+
},
|
|
387
|
+
snapshot() {
|
|
388
|
+
requireOpen();
|
|
389
|
+
return requireOwnedOutput(
|
|
390
|
+
session.snapshot(),
|
|
391
|
+
`${limits.modality} snapshot`,
|
|
392
|
+
limits.maximumSnapshotBytes,
|
|
393
|
+
);
|
|
394
|
+
},
|
|
395
|
+
query(queryBytes) {
|
|
396
|
+
requireOpen();
|
|
397
|
+
requireInputBytes(queryBytes, `${limits.modality} query`, limits.maximumQueryBytes);
|
|
398
|
+
return requireOwnedOutput(
|
|
399
|
+
session.query(queryBytes),
|
|
400
|
+
`${limits.modality} projection`,
|
|
401
|
+
limits.maximumQueryResponseBytes,
|
|
402
|
+
);
|
|
403
|
+
},
|
|
404
|
+
dispose() {
|
|
405
|
+
if (disposed) return;
|
|
406
|
+
disposed = true;
|
|
407
|
+
try {
|
|
408
|
+
session.dispose?.();
|
|
409
|
+
} finally {
|
|
410
|
+
try {
|
|
411
|
+
session.close?.();
|
|
412
|
+
} finally {
|
|
413
|
+
session.free?.();
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
},
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
function requireModalitySessionAbi(session: WasmModalitySession): void {
|
|
421
|
+
if (
|
|
422
|
+
!isObject(session) ||
|
|
423
|
+
typeof session.applyCommands !== "function" ||
|
|
424
|
+
typeof session.snapshot !== "function" ||
|
|
425
|
+
typeof session.revision !== "function" ||
|
|
426
|
+
typeof session.fork !== "function" ||
|
|
427
|
+
typeof session.stateHash !== "function" ||
|
|
428
|
+
typeof session.query !== "function"
|
|
429
|
+
) {
|
|
430
|
+
session?.dispose?.();
|
|
431
|
+
session?.close?.();
|
|
432
|
+
session?.free?.();
|
|
433
|
+
throw adapterError(
|
|
434
|
+
"incompatible_wasm_build",
|
|
435
|
+
"WASM modality session lacks the required stateful ABI",
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function requireSessionAbi(session: WasmCollaborationSession): void {
|
|
441
|
+
if (
|
|
442
|
+
!isObject(session) ||
|
|
443
|
+
typeof session.authorTransaction !== "function" ||
|
|
444
|
+
typeof session.applyCommitted !== "function" ||
|
|
445
|
+
typeof session.snapshot !== "function" ||
|
|
446
|
+
typeof session.frontier !== "function" ||
|
|
447
|
+
typeof session.fork !== "function" ||
|
|
448
|
+
typeof session.stateHash !== "function" ||
|
|
449
|
+
typeof session.revision !== "function" ||
|
|
450
|
+
typeof session.query !== "function"
|
|
451
|
+
) {
|
|
452
|
+
session?.dispose?.();
|
|
453
|
+
session?.close?.();
|
|
454
|
+
session?.free?.();
|
|
455
|
+
throw adapterError(
|
|
456
|
+
"incompatible_wasm_build",
|
|
457
|
+
"WASM collaboration session lacks the required stateful ABI",
|
|
458
|
+
);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function validateWasmModule(input: unknown, modality: EditableArtifactModality): WasmKernelModule {
|
|
463
|
+
if (!isObject(input)) throw adapterError("invalid_wasm_module", "WASM glue module is invalid");
|
|
464
|
+
const capabilities = callable(input, "capabilities");
|
|
465
|
+
const buildIdentity = callable(input, "buildIdentity");
|
|
466
|
+
const initializer = optionalCallable(input, "default");
|
|
467
|
+
const base: WasmKernelModuleBase = {
|
|
468
|
+
capabilities: capabilities as WasmKernelModuleBase["capabilities"],
|
|
469
|
+
buildIdentity: buildIdentity as WasmKernelModuleBase["buildIdentity"],
|
|
470
|
+
...(initializer
|
|
471
|
+
? { default: initializer as NonNullable<WasmKernelModuleBase["default"]> }
|
|
472
|
+
: {}),
|
|
473
|
+
};
|
|
474
|
+
const canonicalizeName =
|
|
475
|
+
modality === "spreadsheet"
|
|
476
|
+
? "canonicalizeCollaborationSnapshot"
|
|
477
|
+
: modality === "document"
|
|
478
|
+
? "canonicalizeDocumentSnapshot"
|
|
479
|
+
: "canonicalizePresentationSnapshot";
|
|
480
|
+
const sessionName =
|
|
481
|
+
modality === "spreadsheet"
|
|
482
|
+
? "ArtifactCollaborationSession"
|
|
483
|
+
: modality === "document"
|
|
484
|
+
? "ArtifactDocumentSession"
|
|
485
|
+
: "ArtifactPresentationSession";
|
|
486
|
+
const canonicalizeSnapshot = callable(input, canonicalizeName);
|
|
487
|
+
const sessionClass = data(input, sessionName);
|
|
488
|
+
if (!isObject(sessionClass)) {
|
|
489
|
+
throw adapterError("invalid_wasm_module", `WASM glue has no ${sessionName} export`);
|
|
490
|
+
}
|
|
491
|
+
const open = callable(sessionClass, "open");
|
|
492
|
+
if (modality === "spreadsheet") {
|
|
493
|
+
return {
|
|
494
|
+
...base,
|
|
495
|
+
modality,
|
|
496
|
+
canonicalizeSnapshot: canonicalizeSnapshot as (snapshotBytes: Uint8Array) => Uint8Array,
|
|
497
|
+
open: open as (snapshotBytes: Uint8Array) => WasmCollaborationSession,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
return {
|
|
501
|
+
...base,
|
|
502
|
+
modality,
|
|
503
|
+
canonicalizeSnapshot: canonicalizeSnapshot as (snapshotBytes: Uint8Array) => Uint8Array,
|
|
504
|
+
open: open as (snapshotBytes: Uint8Array) => WasmModalitySession,
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
function decodeCapabilities(bytes: Uint8Array): WasmCapabilities {
|
|
509
|
+
let value: unknown;
|
|
510
|
+
try {
|
|
511
|
+
const text = decodeUtf8(bytes, "WASM capabilities");
|
|
512
|
+
value = JSON.parse(text) as unknown;
|
|
513
|
+
if (JSON.stringify(value) !== text) {
|
|
514
|
+
throw new TypeError("capabilities JSON is not canonical");
|
|
515
|
+
}
|
|
516
|
+
} catch (error) {
|
|
517
|
+
throw new ArtifactWorkerProtocolError(
|
|
518
|
+
"incompatible_wasm_build",
|
|
519
|
+
"WASM capabilities are not canonical UTF-8 JSON",
|
|
520
|
+
{ cause: error },
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (!isPlainRecord(value)) {
|
|
524
|
+
throw adapterError("incompatible_wasm_build", "WASM capabilities must be a plain record");
|
|
525
|
+
}
|
|
526
|
+
const expectedKeys = [
|
|
527
|
+
"abiVersion",
|
|
528
|
+
"buildIdentityFormat",
|
|
529
|
+
"canonicalStateHash",
|
|
530
|
+
"collaboration",
|
|
531
|
+
"collaborationSnapshotVersion",
|
|
532
|
+
"commandSchemaVersion",
|
|
533
|
+
"committedTransactionVersion",
|
|
534
|
+
"document",
|
|
535
|
+
"documentCommandVersion",
|
|
536
|
+
"documentQueryResponseVersion",
|
|
537
|
+
"documentQueryVersion",
|
|
538
|
+
"documentReceiptVersion",
|
|
539
|
+
"documentSnapshotVersion",
|
|
540
|
+
"documentStatefulSessions",
|
|
541
|
+
"editableArtifactIntentVersion",
|
|
542
|
+
"kernelSnapshotVersion",
|
|
543
|
+
"maxCellsPerBatch",
|
|
544
|
+
"maxCommandBytes",
|
|
545
|
+
"maxCommands",
|
|
546
|
+
"maxCommittedTransactionBytes",
|
|
547
|
+
"maxDocumentCommandBytes",
|
|
548
|
+
"maxDocumentCommands",
|
|
549
|
+
"maxDocumentQueryBytes",
|
|
550
|
+
"maxDocumentQueryResponseBytes",
|
|
551
|
+
"maxDocumentSnapshotBytes",
|
|
552
|
+
"maxIntentBytes",
|
|
553
|
+
"maxMetadataScannedCells",
|
|
554
|
+
"maxMetadataSheets",
|
|
555
|
+
"maxPresentationCommandBytes",
|
|
556
|
+
"maxPresentationQueryBytes",
|
|
557
|
+
"maxPresentationResponseBytes",
|
|
558
|
+
"maxPresentationSnapshotBytes",
|
|
559
|
+
"maxQueryBytes",
|
|
560
|
+
"maxQueryResponseBytes",
|
|
561
|
+
"maxSnapshotBytes",
|
|
562
|
+
"maxSpreadsheetCommandBytes",
|
|
563
|
+
"maxTextLayoutFontBundleBytes",
|
|
564
|
+
"maxTextLayoutRequestBytes",
|
|
565
|
+
"maxTextLayoutResponseBytes",
|
|
566
|
+
"maxViewportArea",
|
|
567
|
+
"maxViewportCells",
|
|
568
|
+
"presentation",
|
|
569
|
+
"presentationCommandVersion",
|
|
570
|
+
"presentationQueryResponseVersion",
|
|
571
|
+
"presentationQueryVersion",
|
|
572
|
+
"presentationSnapshotVersion",
|
|
573
|
+
"presentationStatefulSessions",
|
|
574
|
+
"queryResponseVersion",
|
|
575
|
+
"queryVersion",
|
|
576
|
+
"receiptSchemaVersion",
|
|
577
|
+
"retainedRenderPatchVersion",
|
|
578
|
+
"retainedRenderTileVersion",
|
|
579
|
+
"safeRust",
|
|
580
|
+
"sessionForks",
|
|
581
|
+
"spreadsheetCommandVersion",
|
|
582
|
+
"statefulSessions",
|
|
583
|
+
"textLayout",
|
|
584
|
+
"textLayoutFontBundleVersion",
|
|
585
|
+
"textLayoutRequestVersion",
|
|
586
|
+
"textLayoutResponseVersion",
|
|
587
|
+
"textLayoutStatefulSessions",
|
|
588
|
+
"transport",
|
|
589
|
+
"workbookMetadataQueries",
|
|
590
|
+
] as const;
|
|
591
|
+
const keys = Object.keys(value).sort();
|
|
592
|
+
if (
|
|
593
|
+
keys.length !== expectedKeys.length ||
|
|
594
|
+
keys.some((key, index) => key !== expectedKeys[index])
|
|
595
|
+
) {
|
|
596
|
+
throw adapterError(
|
|
597
|
+
"incompatible_wasm_build",
|
|
598
|
+
"WASM collaboration capabilities have unknown or missing fields",
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
const normalized = {
|
|
602
|
+
abiVersion: positiveInteger(value.abiVersion, "abiVersion"),
|
|
603
|
+
buildIdentityFormat: value.buildIdentityFormat,
|
|
604
|
+
canonicalStateHash: value.canonicalStateHash,
|
|
605
|
+
collaboration: strictBoolean(value.collaboration, "collaboration"),
|
|
606
|
+
collaborationSnapshotVersion: positiveInteger(
|
|
607
|
+
value.collaborationSnapshotVersion,
|
|
608
|
+
"collaborationSnapshotVersion",
|
|
609
|
+
),
|
|
610
|
+
commandSchemaVersion: positiveInteger(value.commandSchemaVersion, "commandSchemaVersion"),
|
|
611
|
+
committedTransactionVersion: positiveInteger(
|
|
612
|
+
value.committedTransactionVersion,
|
|
613
|
+
"committedTransactionVersion",
|
|
614
|
+
),
|
|
615
|
+
document: strictBoolean(value.document, "document"),
|
|
616
|
+
documentCommandVersion: positiveInteger(value.documentCommandVersion, "documentCommandVersion"),
|
|
617
|
+
documentQueryResponseVersion: positiveInteger(
|
|
618
|
+
value.documentQueryResponseVersion,
|
|
619
|
+
"documentQueryResponseVersion",
|
|
620
|
+
),
|
|
621
|
+
documentQueryVersion: positiveInteger(value.documentQueryVersion, "documentQueryVersion"),
|
|
622
|
+
documentReceiptVersion: positiveInteger(value.documentReceiptVersion, "documentReceiptVersion"),
|
|
623
|
+
documentSnapshotVersion: positiveInteger(
|
|
624
|
+
value.documentSnapshotVersion,
|
|
625
|
+
"documentSnapshotVersion",
|
|
626
|
+
),
|
|
627
|
+
documentStatefulSessions: strictBoolean(
|
|
628
|
+
value.documentStatefulSessions,
|
|
629
|
+
"documentStatefulSessions",
|
|
630
|
+
),
|
|
631
|
+
editableArtifactIntentVersion: positiveInteger(
|
|
632
|
+
value.editableArtifactIntentVersion,
|
|
633
|
+
"editableArtifactIntentVersion",
|
|
634
|
+
),
|
|
635
|
+
kernelSnapshotVersion: positiveInteger(value.kernelSnapshotVersion, "kernelSnapshotVersion"),
|
|
636
|
+
spreadsheetCommandVersion: positiveInteger(
|
|
637
|
+
value.spreadsheetCommandVersion,
|
|
638
|
+
"spreadsheetCommandVersion",
|
|
639
|
+
),
|
|
640
|
+
maxCellsPerBatch: positiveInteger(value.maxCellsPerBatch, "maxCellsPerBatch"),
|
|
641
|
+
maxCommands: positiveInteger(value.maxCommands, "maxCommands"),
|
|
642
|
+
maxSnapshotBytes: positiveInteger(value.maxSnapshotBytes, "maxSnapshotBytes"),
|
|
643
|
+
maxCommandBytes: positiveInteger(value.maxCommandBytes, "maxCommandBytes"),
|
|
644
|
+
maxSpreadsheetCommandBytes: positiveInteger(
|
|
645
|
+
value.maxSpreadsheetCommandBytes,
|
|
646
|
+
"maxSpreadsheetCommandBytes",
|
|
647
|
+
),
|
|
648
|
+
maxIntentBytes: positiveInteger(value.maxIntentBytes, "maxIntentBytes"),
|
|
649
|
+
maxCommittedTransactionBytes: positiveInteger(
|
|
650
|
+
value.maxCommittedTransactionBytes,
|
|
651
|
+
"maxCommittedTransactionBytes",
|
|
652
|
+
),
|
|
653
|
+
maxDocumentCommandBytes: positiveInteger(
|
|
654
|
+
value.maxDocumentCommandBytes,
|
|
655
|
+
"maxDocumentCommandBytes",
|
|
656
|
+
),
|
|
657
|
+
maxDocumentCommands: positiveInteger(value.maxDocumentCommands, "maxDocumentCommands"),
|
|
658
|
+
maxDocumentQueryBytes: positiveInteger(value.maxDocumentQueryBytes, "maxDocumentQueryBytes"),
|
|
659
|
+
maxDocumentQueryResponseBytes: positiveInteger(
|
|
660
|
+
value.maxDocumentQueryResponseBytes,
|
|
661
|
+
"maxDocumentQueryResponseBytes",
|
|
662
|
+
),
|
|
663
|
+
maxDocumentSnapshotBytes: positiveInteger(
|
|
664
|
+
value.maxDocumentSnapshotBytes,
|
|
665
|
+
"maxDocumentSnapshotBytes",
|
|
666
|
+
),
|
|
667
|
+
maxMetadataScannedCells: positiveInteger(
|
|
668
|
+
value.maxMetadataScannedCells,
|
|
669
|
+
"maxMetadataScannedCells",
|
|
670
|
+
),
|
|
671
|
+
maxQueryBytes: positiveInteger(value.maxQueryBytes, "maxQueryBytes"),
|
|
672
|
+
maxQueryResponseBytes: positiveInteger(value.maxQueryResponseBytes, "maxQueryResponseBytes"),
|
|
673
|
+
maxViewportArea: positiveInteger(value.maxViewportArea, "maxViewportArea"),
|
|
674
|
+
maxViewportCells: positiveInteger(value.maxViewportCells, "maxViewportCells"),
|
|
675
|
+
maxMetadataSheets: positiveInteger(value.maxMetadataSheets, "maxMetadataSheets"),
|
|
676
|
+
maxPresentationCommandBytes: positiveInteger(
|
|
677
|
+
value.maxPresentationCommandBytes,
|
|
678
|
+
"maxPresentationCommandBytes",
|
|
679
|
+
),
|
|
680
|
+
maxPresentationQueryBytes: positiveInteger(
|
|
681
|
+
value.maxPresentationQueryBytes,
|
|
682
|
+
"maxPresentationQueryBytes",
|
|
683
|
+
),
|
|
684
|
+
maxPresentationResponseBytes: positiveInteger(
|
|
685
|
+
value.maxPresentationResponseBytes,
|
|
686
|
+
"maxPresentationResponseBytes",
|
|
687
|
+
),
|
|
688
|
+
maxPresentationSnapshotBytes: positiveInteger(
|
|
689
|
+
value.maxPresentationSnapshotBytes,
|
|
690
|
+
"maxPresentationSnapshotBytes",
|
|
691
|
+
),
|
|
692
|
+
maxTextLayoutFontBundleBytes: positiveInteger(
|
|
693
|
+
value.maxTextLayoutFontBundleBytes,
|
|
694
|
+
"maxTextLayoutFontBundleBytes",
|
|
695
|
+
),
|
|
696
|
+
maxTextLayoutRequestBytes: positiveInteger(
|
|
697
|
+
value.maxTextLayoutRequestBytes,
|
|
698
|
+
"maxTextLayoutRequestBytes",
|
|
699
|
+
),
|
|
700
|
+
maxTextLayoutResponseBytes: positiveInteger(
|
|
701
|
+
value.maxTextLayoutResponseBytes,
|
|
702
|
+
"maxTextLayoutResponseBytes",
|
|
703
|
+
),
|
|
704
|
+
queryResponseVersion: positiveInteger(value.queryResponseVersion, "queryResponseVersion"),
|
|
705
|
+
queryVersion: positiveInteger(value.queryVersion, "queryVersion"),
|
|
706
|
+
receiptSchemaVersion: positiveInteger(value.receiptSchemaVersion, "receiptSchemaVersion"),
|
|
707
|
+
retainedRenderPatchVersion: positiveInteger(
|
|
708
|
+
value.retainedRenderPatchVersion,
|
|
709
|
+
"retainedRenderPatchVersion",
|
|
710
|
+
),
|
|
711
|
+
retainedRenderTileVersion: positiveInteger(
|
|
712
|
+
value.retainedRenderTileVersion,
|
|
713
|
+
"retainedRenderTileVersion",
|
|
714
|
+
),
|
|
715
|
+
presentation: strictBoolean(value.presentation, "presentation"),
|
|
716
|
+
presentationCommandVersion: positiveInteger(
|
|
717
|
+
value.presentationCommandVersion,
|
|
718
|
+
"presentationCommandVersion",
|
|
719
|
+
),
|
|
720
|
+
presentationQueryResponseVersion: positiveInteger(
|
|
721
|
+
value.presentationQueryResponseVersion,
|
|
722
|
+
"presentationQueryResponseVersion",
|
|
723
|
+
),
|
|
724
|
+
presentationQueryVersion: positiveInteger(
|
|
725
|
+
value.presentationQueryVersion,
|
|
726
|
+
"presentationQueryVersion",
|
|
727
|
+
),
|
|
728
|
+
presentationSnapshotVersion: positiveInteger(
|
|
729
|
+
value.presentationSnapshotVersion,
|
|
730
|
+
"presentationSnapshotVersion",
|
|
731
|
+
),
|
|
732
|
+
presentationStatefulSessions: strictBoolean(
|
|
733
|
+
value.presentationStatefulSessions,
|
|
734
|
+
"presentationStatefulSessions",
|
|
735
|
+
),
|
|
736
|
+
safeRust: strictBoolean(value.safeRust, "safeRust"),
|
|
737
|
+
statefulSessions: strictBoolean(value.statefulSessions, "statefulSessions"),
|
|
738
|
+
textLayout: strictBoolean(value.textLayout, "textLayout"),
|
|
739
|
+
textLayoutFontBundleVersion: positiveInteger(
|
|
740
|
+
value.textLayoutFontBundleVersion,
|
|
741
|
+
"textLayoutFontBundleVersion",
|
|
742
|
+
),
|
|
743
|
+
textLayoutRequestVersion: positiveInteger(
|
|
744
|
+
value.textLayoutRequestVersion,
|
|
745
|
+
"textLayoutRequestVersion",
|
|
746
|
+
),
|
|
747
|
+
textLayoutResponseVersion: positiveInteger(
|
|
748
|
+
value.textLayoutResponseVersion,
|
|
749
|
+
"textLayoutResponseVersion",
|
|
750
|
+
),
|
|
751
|
+
textLayoutStatefulSessions: strictBoolean(
|
|
752
|
+
value.textLayoutStatefulSessions,
|
|
753
|
+
"textLayoutStatefulSessions",
|
|
754
|
+
),
|
|
755
|
+
sessionForks: strictBoolean(value.sessionForks, "sessionForks"),
|
|
756
|
+
transport: value.transport,
|
|
757
|
+
workbookMetadataQueries: strictBoolean(
|
|
758
|
+
value.workbookMetadataQueries,
|
|
759
|
+
"workbookMetadataQueries",
|
|
760
|
+
),
|
|
761
|
+
};
|
|
762
|
+
if (
|
|
763
|
+
normalized.abiVersion !== 1 ||
|
|
764
|
+
normalized.buildIdentityFormat !== "utf8" ||
|
|
765
|
+
normalized.canonicalStateHash !== "sha256:canonical-snapshot" ||
|
|
766
|
+
normalized.editableArtifactIntentVersion !== EDITABLE_ARTIFACT_INTENT_VERSION ||
|
|
767
|
+
normalized.spreadsheetCommandVersion !== SPREADSHEET_ARTIFACT_COMMAND_VERSION ||
|
|
768
|
+
normalized.collaborationSnapshotVersion !== 1 ||
|
|
769
|
+
normalized.commandSchemaVersion !== 1 ||
|
|
770
|
+
normalized.committedTransactionVersion !== 1 ||
|
|
771
|
+
normalized.documentCommandVersion !== 1 ||
|
|
772
|
+
normalized.documentQueryResponseVersion !== 1 ||
|
|
773
|
+
normalized.documentQueryVersion !== 1 ||
|
|
774
|
+
normalized.documentReceiptVersion !== 1 ||
|
|
775
|
+
normalized.documentSnapshotVersion !== 1 ||
|
|
776
|
+
normalized.documentStatefulSessions !== normalized.document ||
|
|
777
|
+
normalized.kernelSnapshotVersion !== 1 ||
|
|
778
|
+
normalized.maxCellsPerBatch !== WASM_MAX_CELLS_PER_BATCH ||
|
|
779
|
+
normalized.maxCommandBytes !== WASM_MAX_COMMAND_BYTES ||
|
|
780
|
+
normalized.maxSpreadsheetCommandBytes !== EDITABLE_ARTIFACT_COMMAND_MAX_BYTES ||
|
|
781
|
+
normalized.maxCommands !== 10_000 ||
|
|
782
|
+
normalized.maxIntentBytes !== EDITABLE_ARTIFACT_INTENT_MAX_BYTES ||
|
|
783
|
+
normalized.maxCommittedTransactionBytes !== MAX_COMMITTED_TRANSACTION_BYTES ||
|
|
784
|
+
normalized.maxDocumentCommandBytes !== WASM_MAX_COMMAND_BYTES ||
|
|
785
|
+
normalized.maxDocumentCommands !== 4_096 ||
|
|
786
|
+
normalized.maxDocumentQueryBytes !== 256 ||
|
|
787
|
+
normalized.maxDocumentQueryResponseBytes !== 8 * 1024 * 1024 ||
|
|
788
|
+
normalized.maxDocumentSnapshotBytes !== WASM_MAX_SNAPSHOT_BYTES ||
|
|
789
|
+
normalized.maxMetadataScannedCells !== 4_000_000 ||
|
|
790
|
+
normalized.maxQueryBytes !== 68 ||
|
|
791
|
+
normalized.maxQueryResponseBytes !== 8 * 1024 * 1024 ||
|
|
792
|
+
normalized.maxViewportArea !== 1_048_576 ||
|
|
793
|
+
normalized.maxViewportCells !== 262_144 ||
|
|
794
|
+
normalized.maxMetadataSheets !== 10_000 ||
|
|
795
|
+
normalized.maxPresentationCommandBytes !== 4 * 1024 * 1024 ||
|
|
796
|
+
normalized.maxPresentationQueryBytes !== 96 ||
|
|
797
|
+
normalized.maxPresentationResponseBytes !== 8 * 1024 * 1024 ||
|
|
798
|
+
normalized.maxPresentationSnapshotBytes !== WASM_MAX_SNAPSHOT_BYTES ||
|
|
799
|
+
normalized.maxTextLayoutFontBundleBytes !== 48 * 1024 * 1024 ||
|
|
800
|
+
normalized.maxTextLayoutRequestBytes !== 4 * 1024 * 1024 ||
|
|
801
|
+
normalized.maxTextLayoutResponseBytes !== 32 * 1024 * 1024 ||
|
|
802
|
+
normalized.maxSnapshotBytes !== WASM_MAX_SNAPSHOT_BYTES ||
|
|
803
|
+
normalized.queryResponseVersion !== 1 ||
|
|
804
|
+
normalized.queryVersion !== 1 ||
|
|
805
|
+
normalized.receiptSchemaVersion !== 1 ||
|
|
806
|
+
normalized.retainedRenderPatchVersion !== 1 ||
|
|
807
|
+
normalized.retainedRenderTileVersion !== 1 ||
|
|
808
|
+
normalized.presentationCommandVersion !== 1 ||
|
|
809
|
+
normalized.presentationQueryResponseVersion !== 1 ||
|
|
810
|
+
normalized.presentationQueryVersion !== 1 ||
|
|
811
|
+
normalized.presentationSnapshotVersion !== 1 ||
|
|
812
|
+
normalized.presentationStatefulSessions !== normalized.presentation ||
|
|
813
|
+
normalized.safeRust !== true ||
|
|
814
|
+
normalized.statefulSessions !== true ||
|
|
815
|
+
normalized.textLayoutFontBundleVersion !== 1 ||
|
|
816
|
+
normalized.textLayoutRequestVersion !== 1 ||
|
|
817
|
+
normalized.textLayoutResponseVersion !== 1 ||
|
|
818
|
+
normalized.textLayoutStatefulSessions !== normalized.textLayout ||
|
|
819
|
+
normalized.sessionForks !== true ||
|
|
820
|
+
normalized.transport !== "bounded-uint8array" ||
|
|
821
|
+
normalized.workbookMetadataQueries !== normalized.collaboration
|
|
822
|
+
) {
|
|
823
|
+
throw adapterError(
|
|
824
|
+
"incompatible_wasm_build",
|
|
825
|
+
"WASM capabilities do not match the SDK protocol",
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
return Object.freeze(normalized) as WasmCapabilities;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function assertRequestedLimits(
|
|
832
|
+
input: ArtifactWorkerInitializeInput,
|
|
833
|
+
capabilities: WasmCapabilities,
|
|
834
|
+
): void {
|
|
835
|
+
const profile = capabilityProfile(input.modality, capabilities);
|
|
836
|
+
const pairs = [
|
|
837
|
+
["maximumSnapshotBytes", input.maximumSnapshotBytes, profile.maximumSnapshotBytes],
|
|
838
|
+
["maximumCommandBytes", input.maximumCommandBytes, profile.maximumCommandBytes],
|
|
839
|
+
["maximumIntentBytes", input.maximumIntentBytes, capabilities.maxIntentBytes],
|
|
840
|
+
[
|
|
841
|
+
"maximumCommittedTransactionBytes",
|
|
842
|
+
input.maximumCommittedTransactionBytes,
|
|
843
|
+
capabilities.maxCommittedTransactionBytes,
|
|
844
|
+
],
|
|
845
|
+
["maximumQueryBytes", input.maximumQueryBytes, profile.maximumQueryBytes],
|
|
846
|
+
[
|
|
847
|
+
"maximumQueryResponseBytes",
|
|
848
|
+
input.maximumQueryResponseBytes,
|
|
849
|
+
profile.maximumQueryResponseBytes,
|
|
850
|
+
],
|
|
851
|
+
] as const;
|
|
852
|
+
for (const [label, requested, advertised] of pairs) {
|
|
853
|
+
if (requested > advertised) {
|
|
854
|
+
throw adapterError(
|
|
855
|
+
"incompatible_wasm_build",
|
|
856
|
+
`${label} exceeds the loaded WASM build capability`,
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function capabilityProfile(modality: EditableArtifactModality, capabilities: WasmCapabilities) {
|
|
863
|
+
if (modality === "spreadsheet") {
|
|
864
|
+
if (!capabilities.collaboration || !capabilities.workbookMetadataQueries) {
|
|
865
|
+
throw adapterError(
|
|
866
|
+
"incompatible_wasm_build",
|
|
867
|
+
"WASM module does not provide the spreadsheet collaboration ABI",
|
|
868
|
+
);
|
|
869
|
+
}
|
|
870
|
+
return {
|
|
871
|
+
modelSchemaVersion: capabilities.collaborationSnapshotVersion,
|
|
872
|
+
commandVersion: capabilities.spreadsheetCommandVersion,
|
|
873
|
+
maximumSnapshotBytes: capabilities.maxSnapshotBytes,
|
|
874
|
+
maximumCommandBytes: capabilities.maxSpreadsheetCommandBytes,
|
|
875
|
+
maximumQueryBytes: capabilities.maxQueryBytes,
|
|
876
|
+
maximumQueryResponseBytes: capabilities.maxQueryResponseBytes,
|
|
877
|
+
} as const;
|
|
878
|
+
}
|
|
879
|
+
if (modality === "document") {
|
|
880
|
+
if (!capabilities.document || !capabilities.documentStatefulSessions) {
|
|
881
|
+
throw adapterError(
|
|
882
|
+
"incompatible_wasm_build",
|
|
883
|
+
"WASM module does not provide the document ABI",
|
|
884
|
+
);
|
|
885
|
+
}
|
|
886
|
+
return {
|
|
887
|
+
modelSchemaVersion: capabilities.documentSnapshotVersion,
|
|
888
|
+
commandVersion: capabilities.documentCommandVersion,
|
|
889
|
+
maximumSnapshotBytes: capabilities.maxDocumentSnapshotBytes,
|
|
890
|
+
maximumCommandBytes: capabilities.maxDocumentCommandBytes,
|
|
891
|
+
maximumQueryBytes: capabilities.maxDocumentQueryBytes,
|
|
892
|
+
maximumQueryResponseBytes: capabilities.maxDocumentQueryResponseBytes,
|
|
893
|
+
} as const;
|
|
894
|
+
}
|
|
895
|
+
if (!capabilities.presentation || !capabilities.presentationStatefulSessions) {
|
|
896
|
+
throw adapterError(
|
|
897
|
+
"incompatible_wasm_build",
|
|
898
|
+
"WASM module does not provide the presentation ABI",
|
|
899
|
+
);
|
|
900
|
+
}
|
|
901
|
+
return {
|
|
902
|
+
modelSchemaVersion: capabilities.presentationSnapshotVersion,
|
|
903
|
+
commandVersion: capabilities.presentationCommandVersion,
|
|
904
|
+
maximumSnapshotBytes: capabilities.maxPresentationSnapshotBytes,
|
|
905
|
+
maximumCommandBytes: capabilities.maxPresentationCommandBytes,
|
|
906
|
+
maximumQueryBytes: capabilities.maxPresentationQueryBytes,
|
|
907
|
+
maximumQueryResponseBytes: capabilities.maxPresentationResponseBytes,
|
|
908
|
+
} as const;
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
function classifyProjectionFailure(error: unknown): string | null {
|
|
912
|
+
const message = error instanceof Error ? error.message : "";
|
|
913
|
+
const match = /^\[([A-Z][A-Z0-9_]{0,127})\]/u.exec(message);
|
|
914
|
+
if (!match) return null;
|
|
915
|
+
switch (match[1]) {
|
|
916
|
+
case "ARTIFACT_COLLABORATION_REJECTED":
|
|
917
|
+
return "structural_conflict";
|
|
918
|
+
case "ARTIFACT_PROJECTION_REJECTED":
|
|
919
|
+
return "projection_conflict";
|
|
920
|
+
default:
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
925
|
+
function requireInputBytes(value: unknown, label: string, maximumBytes: number): Uint8Array {
|
|
926
|
+
if (!(value instanceof Uint8Array) || value.byteLength === 0) {
|
|
927
|
+
throw adapterError("invalid_kernel_input", `${label} must be non-empty bytes`);
|
|
928
|
+
}
|
|
929
|
+
if (value.byteLength > maximumBytes) {
|
|
930
|
+
throw adapterError("limit_exceeded", `${label} exceeds its byte limit`);
|
|
931
|
+
}
|
|
932
|
+
return value;
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
function requireBoundedBytes(value: unknown, label: string, maximumBytes: number): Uint8Array {
|
|
936
|
+
if (!(value instanceof Uint8Array) || value.byteLength === 0) {
|
|
937
|
+
throw adapterError("invalid_kernel_output", `${label} must be non-empty bytes`);
|
|
938
|
+
}
|
|
939
|
+
if (value.byteLength > maximumBytes) {
|
|
940
|
+
throw adapterError("limit_exceeded", `${label} exceeds its byte limit`);
|
|
941
|
+
}
|
|
942
|
+
return value;
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
/**
|
|
946
|
+
* Wasm-bindgen returns a fresh Uint8Array. Keep its exact ArrayBuffer when
|
|
947
|
+
* possible so the Worker can transfer it without another large copy; normalize
|
|
948
|
+
* only aliased/subarray outputs from test or custom glue.
|
|
949
|
+
*/
|
|
950
|
+
function requireOwnedOutput(value: unknown, label: string, maximumBytes: number): Uint8Array {
|
|
951
|
+
const bytes = requireBoundedBytes(value, label, maximumBytes);
|
|
952
|
+
if (
|
|
953
|
+
bytes.buffer instanceof ArrayBuffer &&
|
|
954
|
+
bytes.byteOffset === 0 &&
|
|
955
|
+
bytes.byteLength === bytes.buffer.byteLength
|
|
956
|
+
) {
|
|
957
|
+
return bytes;
|
|
958
|
+
}
|
|
959
|
+
return bytes.slice();
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
function decodeUtf8(bytes: Uint8Array, label: string): string {
|
|
963
|
+
try {
|
|
964
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
965
|
+
} catch (error) {
|
|
966
|
+
throw new ArtifactWorkerProtocolError("incompatible_wasm_build", `${label} is not UTF-8`, {
|
|
967
|
+
cause: error,
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function callable(
|
|
973
|
+
record: Record<PropertyKey, unknown>,
|
|
974
|
+
key: string,
|
|
975
|
+
): (...args: never[]) => unknown {
|
|
976
|
+
const value = data(record, key);
|
|
977
|
+
if (typeof value !== "function") {
|
|
978
|
+
throw adapterError("invalid_wasm_module", `${key} must be a function`);
|
|
979
|
+
}
|
|
980
|
+
return value as (...args: never[]) => unknown;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
function optionalCallable(
|
|
984
|
+
record: Record<PropertyKey, unknown>,
|
|
985
|
+
key: string,
|
|
986
|
+
): ((...args: never[]) => unknown) | undefined {
|
|
987
|
+
const value = data(record, key, false);
|
|
988
|
+
if (value === undefined) return undefined;
|
|
989
|
+
if (typeof value !== "function") {
|
|
990
|
+
throw adapterError("invalid_wasm_module", `${key} must be a function`);
|
|
991
|
+
}
|
|
992
|
+
return value as (...args: never[]) => unknown;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
function data(record: Record<PropertyKey, unknown>, key: string, required = true): unknown {
|
|
996
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
997
|
+
// ES module namespace exports are accessor descriptors by specification.
|
|
998
|
+
if (descriptor) return "value" in descriptor ? descriptor.value : Reflect.get(record, key);
|
|
999
|
+
if (required) throw adapterError("invalid_wasm_module", `module is missing ${key}`);
|
|
1000
|
+
return undefined;
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
function isObject(value: unknown): value is Record<PropertyKey, unknown> {
|
|
1004
|
+
return value !== null && (typeof value === "object" || typeof value === "function");
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
1008
|
+
return (
|
|
1009
|
+
typeof value === "object" &&
|
|
1010
|
+
value !== null &&
|
|
1011
|
+
!Array.isArray(value) &&
|
|
1012
|
+
Object.getPrototypeOf(value) === Object.prototype
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
function adapterError(code: string, message: string): ArtifactWorkerProtocolError {
|
|
1017
|
+
return new ArtifactWorkerProtocolError(code, message);
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
function requireErrorCode(value: unknown, label: string): string {
|
|
1021
|
+
if (typeof value !== "string" || !/^[a-z][a-z0-9_]{0,127}$/u.test(value)) {
|
|
1022
|
+
throw adapterError("invalid_wasm_module", `${label} is invalid`);
|
|
1023
|
+
}
|
|
1024
|
+
return value;
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
function positiveInteger(value: unknown, label: string): number {
|
|
1028
|
+
if (!Number.isSafeInteger(value) || (value as number) <= 0) {
|
|
1029
|
+
throw adapterError("incompatible_wasm_build", `${label} must be a positive safe integer`);
|
|
1030
|
+
}
|
|
1031
|
+
return value as number;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
function strictBoolean(value: unknown, label: string): boolean {
|
|
1035
|
+
if (typeof value !== "boolean") {
|
|
1036
|
+
throw adapterError("incompatible_wasm_build", `${label} must be a boolean`);
|
|
1037
|
+
}
|
|
1038
|
+
return value;
|
|
1039
|
+
}
|