@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,852 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EDITABLE_ARTIFACT_COMMAND_MAX_BYTES,
|
|
3
|
+
EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
|
|
4
|
+
EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES,
|
|
5
|
+
} from "@opengeni/contracts/editable-artifacts";
|
|
6
|
+
import { MAX_COMMITTED_TRANSACTION_BYTES } from "@opengeni/contracts/editable-artifact-committed-transaction";
|
|
7
|
+
import type {
|
|
8
|
+
EditableArtifactBlockedPending,
|
|
9
|
+
EditableArtifactCommittedTransaction,
|
|
10
|
+
EditableArtifactModality,
|
|
11
|
+
EditableArtifactPendingTransaction,
|
|
12
|
+
EditableArtifactSnapshot,
|
|
13
|
+
EditableArtifactWorkerKernel,
|
|
14
|
+
} from "../types";
|
|
15
|
+
import {
|
|
16
|
+
ArtifactWorkerBinaryReader,
|
|
17
|
+
ArtifactWorkerBinaryWriter,
|
|
18
|
+
ArtifactWorkerProtocolError,
|
|
19
|
+
} from "./rpc-protocol";
|
|
20
|
+
|
|
21
|
+
const MAX_ID_BYTES = 256;
|
|
22
|
+
const MAX_VERSION_BYTES = 512;
|
|
23
|
+
const MAX_ERROR_BYTES = 8 * 1024;
|
|
24
|
+
const MAX_URL_BYTES = 8 * 1024;
|
|
25
|
+
const SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;
|
|
26
|
+
const HARD_MAX_SNAPSHOT_BYTES = EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES;
|
|
27
|
+
const HARD_MAX_COMMAND_BYTES = EDITABLE_ARTIFACT_COMMAND_MAX_BYTES;
|
|
28
|
+
const HARD_MAX_INTENT_BYTES = EDITABLE_ARTIFACT_INTENT_MAX_BYTES;
|
|
29
|
+
const HARD_MAX_COMMITTED_TRANSACTION_BYTES = MAX_COMMITTED_TRANSACTION_BYTES;
|
|
30
|
+
const HARD_MAX_QUERY_BYTES = 256;
|
|
31
|
+
const HARD_MAX_QUERY_RESPONSE_BYTES = 8 * 1024 * 1024;
|
|
32
|
+
const HARD_MAX_PENDING_TRANSACTIONS = 1_024;
|
|
33
|
+
|
|
34
|
+
export type ArtifactWorkerInitializeInput = {
|
|
35
|
+
modality: EditableArtifactModality;
|
|
36
|
+
kernelVersion: string;
|
|
37
|
+
protocolVersion: number;
|
|
38
|
+
modelSchemaVersion: number;
|
|
39
|
+
commandVersion: number;
|
|
40
|
+
wasmGlueUrl: string;
|
|
41
|
+
wasmBinaryUrl: string;
|
|
42
|
+
maximumSnapshotBytes: number;
|
|
43
|
+
maximumCommandBytes: number;
|
|
44
|
+
maximumIntentBytes: number;
|
|
45
|
+
maximumCommittedTransactionBytes: number;
|
|
46
|
+
maximumQueryBytes: number;
|
|
47
|
+
maximumQueryResponseBytes: number;
|
|
48
|
+
maximumPendingTransactions: number;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type ArtifactWorkerAuthorPendingInput = Parameters<
|
|
52
|
+
EditableArtifactWorkerKernel["authorPending"]
|
|
53
|
+
>[0];
|
|
54
|
+
|
|
55
|
+
export type ArtifactWorkerErrorPayload = {
|
|
56
|
+
code: string;
|
|
57
|
+
message: string;
|
|
58
|
+
retryable: boolean;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export function encodeInitialize(input: ArtifactWorkerInitializeInput): Uint8Array {
|
|
62
|
+
validateInitializeLimits(input);
|
|
63
|
+
const writer = new ArtifactWorkerBinaryWriter();
|
|
64
|
+
writer
|
|
65
|
+
.u8(modalityTag(input.modality))
|
|
66
|
+
.string(requireVersion(input.kernelVersion, "kernelVersion"), MAX_VERSION_BYTES)
|
|
67
|
+
.u32(positiveSafeInteger(input.protocolVersion, "protocolVersion"))
|
|
68
|
+
.u32(positiveSafeInteger(input.modelSchemaVersion, "modelSchemaVersion"))
|
|
69
|
+
.u32(positiveSafeInteger(input.commandVersion, "commandVersion"))
|
|
70
|
+
.string(requireUrl(input.wasmGlueUrl, "wasmGlueUrl"), MAX_URL_BYTES)
|
|
71
|
+
.string(requireUrl(input.wasmBinaryUrl, "wasmBinaryUrl"), MAX_URL_BYTES)
|
|
72
|
+
.safeUint(positiveSafeInteger(input.maximumSnapshotBytes, "maximumSnapshotBytes"))
|
|
73
|
+
.safeUint(positiveSafeInteger(input.maximumCommandBytes, "maximumCommandBytes"))
|
|
74
|
+
.safeUint(positiveSafeInteger(input.maximumIntentBytes, "maximumIntentBytes"))
|
|
75
|
+
.safeUint(
|
|
76
|
+
positiveSafeInteger(
|
|
77
|
+
input.maximumCommittedTransactionBytes,
|
|
78
|
+
"maximumCommittedTransactionBytes",
|
|
79
|
+
),
|
|
80
|
+
)
|
|
81
|
+
.safeUint(positiveSafeInteger(input.maximumQueryBytes, "maximumQueryBytes"))
|
|
82
|
+
.safeUint(positiveSafeInteger(input.maximumQueryResponseBytes, "maximumQueryResponseBytes"))
|
|
83
|
+
.safeUint(positiveSafeInteger(input.maximumPendingTransactions, "maximumPendingTransactions"));
|
|
84
|
+
return writer.finish();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function decodeInitialize(bytes: Uint8Array): ArtifactWorkerInitializeInput {
|
|
88
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
89
|
+
const value = {
|
|
90
|
+
modality: decodeModality(reader.u8()),
|
|
91
|
+
kernelVersion: requireVersion(reader.string(MAX_VERSION_BYTES), "kernelVersion"),
|
|
92
|
+
protocolVersion: positiveSafeInteger(reader.u32(), "protocolVersion"),
|
|
93
|
+
modelSchemaVersion: positiveSafeInteger(reader.u32(), "modelSchemaVersion"),
|
|
94
|
+
commandVersion: positiveSafeInteger(reader.u32(), "commandVersion"),
|
|
95
|
+
wasmGlueUrl: requireUrl(reader.string(MAX_URL_BYTES), "wasmGlueUrl"),
|
|
96
|
+
wasmBinaryUrl: requireUrl(reader.string(MAX_URL_BYTES), "wasmBinaryUrl"),
|
|
97
|
+
maximumSnapshotBytes: positiveSafeInteger(reader.safeUint(), "maximumSnapshotBytes"),
|
|
98
|
+
maximumCommandBytes: positiveSafeInteger(reader.safeUint(), "maximumCommandBytes"),
|
|
99
|
+
maximumIntentBytes: positiveSafeInteger(reader.safeUint(), "maximumIntentBytes"),
|
|
100
|
+
maximumCommittedTransactionBytes: positiveSafeInteger(
|
|
101
|
+
reader.safeUint(),
|
|
102
|
+
"maximumCommittedTransactionBytes",
|
|
103
|
+
),
|
|
104
|
+
maximumQueryBytes: positiveSafeInteger(reader.safeUint(), "maximumQueryBytes"),
|
|
105
|
+
maximumQueryResponseBytes: positiveSafeInteger(reader.safeUint(), "maximumQueryResponseBytes"),
|
|
106
|
+
maximumPendingTransactions: positiveSafeInteger(
|
|
107
|
+
reader.safeUint(),
|
|
108
|
+
"maximumPendingTransactions",
|
|
109
|
+
),
|
|
110
|
+
};
|
|
111
|
+
reader.done();
|
|
112
|
+
validateInitializeLimits(value);
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function encodeSnapshotMetadata(snapshot: EditableArtifactSnapshot): Uint8Array {
|
|
117
|
+
const writer = new ArtifactWorkerBinaryWriter();
|
|
118
|
+
writer
|
|
119
|
+
.u8(modalityTag(snapshot.modality))
|
|
120
|
+
.string(requireIdentifier(snapshot.artifactId, "snapshot.artifactId"), MAX_ID_BYTES)
|
|
121
|
+
.safeUint(nonNegativeSafeInteger(snapshot.sequence, "snapshot.sequence"))
|
|
122
|
+
.string(requireSha256(snapshot.stateHash, "snapshot.stateHash"), 71)
|
|
123
|
+
.string(requireSha256(snapshot.digest, "snapshot.digest"), 71)
|
|
124
|
+
.string(requireVersion(snapshot.kernelVersion, "snapshot.kernelVersion"), MAX_VERSION_BYTES)
|
|
125
|
+
.u32(positiveSafeInteger(snapshot.modelSchemaVersion, "snapshot.modelSchemaVersion"));
|
|
126
|
+
if (snapshot.modality === "spreadsheet") {
|
|
127
|
+
writer
|
|
128
|
+
.frontier(snapshot.causalFrontier)
|
|
129
|
+
.u32(positiveSafeInteger(snapshot.protocolVersion, "snapshot.protocolVersion"));
|
|
130
|
+
} else {
|
|
131
|
+
writer.safeUint(nonNegativeSafeInteger(snapshot.nativeRevision, "snapshot.nativeRevision"));
|
|
132
|
+
}
|
|
133
|
+
return writer.finish();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function decodeSnapshotMetadata(
|
|
137
|
+
bytes: Uint8Array,
|
|
138
|
+
snapshotBytes: Uint8Array,
|
|
139
|
+
): EditableArtifactSnapshot {
|
|
140
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
141
|
+
const modality = decodeModality(reader.u8());
|
|
142
|
+
const common = {
|
|
143
|
+
artifactId: requireIdentifier(reader.string(MAX_ID_BYTES), "snapshot.artifactId"),
|
|
144
|
+
sequence: nonNegativeSafeInteger(reader.safeUint(), "snapshot.sequence"),
|
|
145
|
+
stateHash: requireSha256(reader.string(71), "snapshot.stateHash"),
|
|
146
|
+
digest: requireSha256(reader.string(71), "snapshot.digest"),
|
|
147
|
+
kernelVersion: requireVersion(reader.string(MAX_VERSION_BYTES), "snapshot.kernelVersion"),
|
|
148
|
+
modelSchemaVersion: positiveSafeInteger(reader.u32(), "snapshot.modelSchemaVersion"),
|
|
149
|
+
bytes: snapshotBytes,
|
|
150
|
+
} as const;
|
|
151
|
+
const value: EditableArtifactSnapshot =
|
|
152
|
+
modality === "spreadsheet"
|
|
153
|
+
? {
|
|
154
|
+
...common,
|
|
155
|
+
modality,
|
|
156
|
+
causalFrontier: reader.frontier(),
|
|
157
|
+
protocolVersion: positiveSafeInteger(reader.u32(), "snapshot.protocolVersion"),
|
|
158
|
+
}
|
|
159
|
+
: {
|
|
160
|
+
...common,
|
|
161
|
+
modality,
|
|
162
|
+
nativeRevision: nonNegativeSafeInteger(reader.safeUint(), "snapshot.nativeRevision"),
|
|
163
|
+
};
|
|
164
|
+
reader.done();
|
|
165
|
+
return Object.freeze(value);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export function encodeCommittedMetadata(
|
|
169
|
+
transaction: EditableArtifactCommittedTransaction,
|
|
170
|
+
): Uint8Array {
|
|
171
|
+
const writer = new ArtifactWorkerBinaryWriter();
|
|
172
|
+
encodeCommittedFields(writer, transaction);
|
|
173
|
+
return writer.finish();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function decodeCommittedMetadata(
|
|
177
|
+
bytes: Uint8Array,
|
|
178
|
+
committedTransactionBytes: Uint8Array,
|
|
179
|
+
): EditableArtifactCommittedTransaction {
|
|
180
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
181
|
+
const modality = decodeModality(reader.u8());
|
|
182
|
+
const common = {
|
|
183
|
+
artifactId: requireIdentifier(reader.string(MAX_ID_BYTES), "transaction.artifactId"),
|
|
184
|
+
transactionId: requireIdentifier(reader.string(MAX_ID_BYTES), "transaction.transactionId"),
|
|
185
|
+
requestHash: requireSha256(reader.string(71), "transaction.requestHash"),
|
|
186
|
+
startSequence: positiveSafeInteger(reader.safeUint(), "transaction.startSequence"),
|
|
187
|
+
endSequence: positiveSafeInteger(reader.safeUint(), "transaction.endSequence"),
|
|
188
|
+
priorStateHash: requireSha256(reader.string(71), "transaction.priorStateHash"),
|
|
189
|
+
stateHash: requireSha256(reader.string(71), "transaction.stateHash"),
|
|
190
|
+
committedTransactionBytes,
|
|
191
|
+
} as const;
|
|
192
|
+
const value: EditableArtifactCommittedTransaction =
|
|
193
|
+
modality === "spreadsheet"
|
|
194
|
+
? {
|
|
195
|
+
...common,
|
|
196
|
+
modality,
|
|
197
|
+
causalFrontier: reader.frontier(),
|
|
198
|
+
protocolVersion: positiveSafeInteger(reader.u32(), "transaction.protocolVersion"),
|
|
199
|
+
}
|
|
200
|
+
: {
|
|
201
|
+
...common,
|
|
202
|
+
modality,
|
|
203
|
+
priorNativeRevision: nonNegativeSafeInteger(
|
|
204
|
+
reader.safeUint(),
|
|
205
|
+
"transaction.priorNativeRevision",
|
|
206
|
+
),
|
|
207
|
+
nativeRevision: nonNegativeSafeInteger(reader.safeUint(), "transaction.nativeRevision"),
|
|
208
|
+
commitProtocolVersion: positiveSafeInteger(
|
|
209
|
+
reader.u32(),
|
|
210
|
+
"transaction.commitProtocolVersion",
|
|
211
|
+
),
|
|
212
|
+
};
|
|
213
|
+
reader.done();
|
|
214
|
+
if (value.endSequence < value.startSequence) {
|
|
215
|
+
throw wireError("invalid_sequence", "transaction endSequence precedes startSequence");
|
|
216
|
+
}
|
|
217
|
+
return Object.freeze(value);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
export function encodePendingListMetadata(
|
|
221
|
+
transactions: readonly EditableArtifactPendingTransaction[],
|
|
222
|
+
): Uint8Array {
|
|
223
|
+
const writer = new ArtifactWorkerBinaryWriter();
|
|
224
|
+
if (!Array.isArray(transactions))
|
|
225
|
+
throw wireError("invalid_pending", "pending transactions must be an array");
|
|
226
|
+
writer.u32(transactions.length);
|
|
227
|
+
for (const transaction of transactions) encodePendingFields(writer, transaction);
|
|
228
|
+
return writer.finish();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export function decodePendingListMetadata(
|
|
232
|
+
bytes: Uint8Array,
|
|
233
|
+
segments: readonly Uint8Array[],
|
|
234
|
+
maximumTransactions: number,
|
|
235
|
+
): readonly EditableArtifactPendingTransaction[] {
|
|
236
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
237
|
+
const count = reader.u32();
|
|
238
|
+
if (count > maximumTransactions || count * 2 !== segments.length) {
|
|
239
|
+
throw wireError("invalid_pending", "pending metadata and command segments do not match");
|
|
240
|
+
}
|
|
241
|
+
const transactions: EditableArtifactPendingTransaction[] = [];
|
|
242
|
+
for (let index = 0; index < count; index += 1) {
|
|
243
|
+
transactions.push(decodePendingFields(reader, segments[index * 2]!, segments[index * 2 + 1]!));
|
|
244
|
+
}
|
|
245
|
+
reader.done();
|
|
246
|
+
return Object.freeze(transactions);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function encodeReconcileMetadata(
|
|
250
|
+
committed: EditableArtifactCommittedTransaction,
|
|
251
|
+
pending: readonly EditableArtifactPendingTransaction[],
|
|
252
|
+
): Uint8Array {
|
|
253
|
+
const writer = new ArtifactWorkerBinaryWriter();
|
|
254
|
+
encodeCommittedFields(writer, committed);
|
|
255
|
+
writer.u32(pending.length);
|
|
256
|
+
for (const transaction of pending) encodePendingFields(writer, transaction);
|
|
257
|
+
return writer.finish();
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
export function decodeReconcileMetadata(
|
|
261
|
+
bytes: Uint8Array,
|
|
262
|
+
segments: readonly Uint8Array[],
|
|
263
|
+
maximumPendingTransactions: number,
|
|
264
|
+
): {
|
|
265
|
+
committed: EditableArtifactCommittedTransaction;
|
|
266
|
+
pending: readonly EditableArtifactPendingTransaction[];
|
|
267
|
+
} {
|
|
268
|
+
if (segments.length < 1) throw wireError("invalid_segment", "reconcile requires operation bytes");
|
|
269
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
270
|
+
const committed = decodeCommittedFields(reader, segments[0]!);
|
|
271
|
+
if (committed.endSequence < committed.startSequence) {
|
|
272
|
+
throw wireError("invalid_sequence", "transaction endSequence precedes startSequence");
|
|
273
|
+
}
|
|
274
|
+
const count = reader.u32();
|
|
275
|
+
if (count > maximumPendingTransactions || segments.length !== count * 2 + 1) {
|
|
276
|
+
throw wireError("invalid_pending", "reconcile pending metadata and segments do not match");
|
|
277
|
+
}
|
|
278
|
+
const pending: EditableArtifactPendingTransaction[] = [];
|
|
279
|
+
for (let index = 0; index < count; index += 1) {
|
|
280
|
+
pending.push(decodePendingFields(reader, segments[index * 2 + 1]!, segments[index * 2 + 2]!));
|
|
281
|
+
}
|
|
282
|
+
reader.done();
|
|
283
|
+
return { committed, pending: Object.freeze(pending) };
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
export function encodeAuthorPendingMetadata(input: ArtifactWorkerAuthorPendingInput): Uint8Array {
|
|
287
|
+
const writer = new ArtifactWorkerBinaryWriter();
|
|
288
|
+
encodeAuthorPendingFields(writer, input);
|
|
289
|
+
return writer.finish();
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export function decodeAuthorPendingMetadata(
|
|
293
|
+
bytes: Uint8Array,
|
|
294
|
+
commandBytes: Uint8Array,
|
|
295
|
+
): ArtifactWorkerAuthorPendingInput {
|
|
296
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
297
|
+
const value = decodeAuthorPendingFields(reader, commandBytes);
|
|
298
|
+
reader.done();
|
|
299
|
+
return value;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function encodeStateResponse(stateHash: string, digest?: string): Uint8Array {
|
|
303
|
+
const writer = new ArtifactWorkerBinaryWriter(256);
|
|
304
|
+
writer
|
|
305
|
+
.string(requireSha256(stateHash, "stateHash"), 71)
|
|
306
|
+
.optionalString(digest === undefined ? null : requireSha256(digest, "digest"), 71);
|
|
307
|
+
return writer.finish();
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export function decodeStateResponse(bytes: Uint8Array): { stateHash: string; digest?: string } {
|
|
311
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
312
|
+
const stateHash = requireSha256(reader.string(71), "stateHash");
|
|
313
|
+
const digest = reader.optionalString(71);
|
|
314
|
+
reader.done();
|
|
315
|
+
return digest === null ? { stateHash } : { stateHash, digest: requireSha256(digest, "digest") };
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
export function encodeProjectionResponse(
|
|
319
|
+
blockedPending: readonly EditableArtifactBlockedPending[],
|
|
320
|
+
stateHash?: string,
|
|
321
|
+
): Uint8Array {
|
|
322
|
+
if (!Array.isArray(blockedPending) || blockedPending.length > HARD_MAX_PENDING_TRANSACTIONS) {
|
|
323
|
+
throw wireError("invalid_pending", "blocked pending result exceeds its item limit");
|
|
324
|
+
}
|
|
325
|
+
const writer = new ArtifactWorkerBinaryWriter();
|
|
326
|
+
writer.optionalString(stateHash === undefined ? null : requireSha256(stateHash, "stateHash"), 71);
|
|
327
|
+
writer.u32(blockedPending.length);
|
|
328
|
+
const seen = new Set<string>();
|
|
329
|
+
for (const blocked of blockedPending) {
|
|
330
|
+
const clientTransactionId = requireIdentifier(
|
|
331
|
+
blocked.clientTransactionId,
|
|
332
|
+
"blocked.clientTransactionId",
|
|
333
|
+
);
|
|
334
|
+
if (seen.has(clientTransactionId))
|
|
335
|
+
throw wireError("noncanonical", "blocked transaction is duplicated");
|
|
336
|
+
seen.add(clientTransactionId);
|
|
337
|
+
writer.string(clientTransactionId, MAX_ID_BYTES).string(requireErrorCode(blocked.code), 128);
|
|
338
|
+
}
|
|
339
|
+
return writer.finish();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
export function decodeProjectionResponse(bytes: Uint8Array): {
|
|
343
|
+
stateHash?: string;
|
|
344
|
+
blockedPending: readonly EditableArtifactBlockedPending[];
|
|
345
|
+
} {
|
|
346
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
347
|
+
const stateHash = reader.optionalString(71);
|
|
348
|
+
const count = reader.u32();
|
|
349
|
+
if (count > HARD_MAX_PENDING_TRANSACTIONS) {
|
|
350
|
+
throw wireError("invalid_pending", "blocked pending result exceeds its item limit");
|
|
351
|
+
}
|
|
352
|
+
const blockedPending: EditableArtifactBlockedPending[] = [];
|
|
353
|
+
const seen = new Set<string>();
|
|
354
|
+
for (let index = 0; index < count; index += 1) {
|
|
355
|
+
const clientTransactionId = requireIdentifier(
|
|
356
|
+
reader.string(MAX_ID_BYTES),
|
|
357
|
+
"blocked.clientTransactionId",
|
|
358
|
+
);
|
|
359
|
+
if (seen.has(clientTransactionId))
|
|
360
|
+
throw wireError("noncanonical", "blocked transaction is duplicated");
|
|
361
|
+
seen.add(clientTransactionId);
|
|
362
|
+
blockedPending.push(
|
|
363
|
+
Object.freeze({ clientTransactionId, code: requireErrorCode(reader.string(128)) }),
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
reader.done();
|
|
367
|
+
return {
|
|
368
|
+
...(stateHash === null ? {} : { stateHash: requireSha256(stateHash, "stateHash") }),
|
|
369
|
+
blockedPending: Object.freeze(blockedPending),
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
export function encodeErrorPayload(error: ArtifactWorkerErrorPayload): Uint8Array {
|
|
374
|
+
const writer = new ArtifactWorkerBinaryWriter(MAX_ERROR_BYTES * 2 + 32);
|
|
375
|
+
writer
|
|
376
|
+
.string(requireErrorCode(error.code), 128)
|
|
377
|
+
.string(requireBoundedString(error.message, "error.message", MAX_ERROR_BYTES), MAX_ERROR_BYTES)
|
|
378
|
+
.u8(error.retryable ? 1 : 0);
|
|
379
|
+
return writer.finish();
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export function decodeErrorPayload(bytes: Uint8Array): ArtifactWorkerErrorPayload {
|
|
383
|
+
const reader = new ArtifactWorkerBinaryReader(bytes);
|
|
384
|
+
const value = {
|
|
385
|
+
code: requireErrorCode(reader.string(128)),
|
|
386
|
+
message: requireBoundedString(reader.string(MAX_ERROR_BYTES), "error.message", MAX_ERROR_BYTES),
|
|
387
|
+
retryable: decodeBoolean(reader.u8(), "error.retryable"),
|
|
388
|
+
};
|
|
389
|
+
reader.done();
|
|
390
|
+
return value;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
export function sha256Hex(bytes: Uint8Array): Promise<string> {
|
|
394
|
+
if (!(bytes instanceof Uint8Array))
|
|
395
|
+
throw wireError("invalid_bytes", "SHA-256 input must be bytes");
|
|
396
|
+
const owned = new Uint8Array(bytes);
|
|
397
|
+
return globalThis.crypto.subtle
|
|
398
|
+
.digest("SHA-256", owned)
|
|
399
|
+
.then(
|
|
400
|
+
(digest) =>
|
|
401
|
+
`sha256:${[...new Uint8Array(digest)]
|
|
402
|
+
.map((value) => value.toString(16).padStart(2, "0"))
|
|
403
|
+
.join("")}`,
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export function requireMatchingHash(actual: string, expected: string, label: string): void {
|
|
408
|
+
requireSha256(actual, `${label}.actual`);
|
|
409
|
+
requireSha256(expected, `${label}.expected`);
|
|
410
|
+
if (actual !== expected)
|
|
411
|
+
throw wireError("kernel_diverged", `${label} hash does not match authority`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function encodeCommittedFields(
|
|
415
|
+
writer: ArtifactWorkerBinaryWriter,
|
|
416
|
+
transaction: EditableArtifactCommittedTransaction,
|
|
417
|
+
): void {
|
|
418
|
+
writer
|
|
419
|
+
.u8(modalityTag(transaction.modality))
|
|
420
|
+
.string(requireIdentifier(transaction.artifactId, "transaction.artifactId"), MAX_ID_BYTES)
|
|
421
|
+
.string(requireIdentifier(transaction.transactionId, "transaction.transactionId"), MAX_ID_BYTES)
|
|
422
|
+
.string(requireSha256(transaction.requestHash, "transaction.requestHash"), 71)
|
|
423
|
+
.safeUint(positiveSafeInteger(transaction.startSequence, "transaction.startSequence"))
|
|
424
|
+
.safeUint(positiveSafeInteger(transaction.endSequence, "transaction.endSequence"))
|
|
425
|
+
.string(requireSha256(transaction.priorStateHash, "transaction.priorStateHash"), 71)
|
|
426
|
+
.string(requireSha256(transaction.stateHash, "transaction.stateHash"), 71);
|
|
427
|
+
if (transaction.modality === "spreadsheet") {
|
|
428
|
+
writer
|
|
429
|
+
.frontier(transaction.causalFrontier)
|
|
430
|
+
.u32(positiveSafeInteger(transaction.protocolVersion, "transaction.protocolVersion"));
|
|
431
|
+
} else {
|
|
432
|
+
writer
|
|
433
|
+
.safeUint(
|
|
434
|
+
nonNegativeSafeInteger(transaction.priorNativeRevision, "transaction.priorNativeRevision"),
|
|
435
|
+
)
|
|
436
|
+
.safeUint(nonNegativeSafeInteger(transaction.nativeRevision, "transaction.nativeRevision"))
|
|
437
|
+
.u32(
|
|
438
|
+
positiveSafeInteger(transaction.commitProtocolVersion, "transaction.commitProtocolVersion"),
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function decodeCommittedFields(
|
|
444
|
+
reader: ArtifactWorkerBinaryReader,
|
|
445
|
+
committedTransactionBytes: Uint8Array,
|
|
446
|
+
): EditableArtifactCommittedTransaction {
|
|
447
|
+
const modality = decodeModality(reader.u8());
|
|
448
|
+
const common = {
|
|
449
|
+
artifactId: requireIdentifier(reader.string(MAX_ID_BYTES), "transaction.artifactId"),
|
|
450
|
+
transactionId: requireIdentifier(reader.string(MAX_ID_BYTES), "transaction.transactionId"),
|
|
451
|
+
requestHash: requireSha256(reader.string(71), "transaction.requestHash"),
|
|
452
|
+
startSequence: positiveSafeInteger(reader.safeUint(), "transaction.startSequence"),
|
|
453
|
+
endSequence: positiveSafeInteger(reader.safeUint(), "transaction.endSequence"),
|
|
454
|
+
priorStateHash: requireSha256(reader.string(71), "transaction.priorStateHash"),
|
|
455
|
+
stateHash: requireSha256(reader.string(71), "transaction.stateHash"),
|
|
456
|
+
committedTransactionBytes,
|
|
457
|
+
} as const;
|
|
458
|
+
return Object.freeze(
|
|
459
|
+
modality === "spreadsheet"
|
|
460
|
+
? {
|
|
461
|
+
...common,
|
|
462
|
+
modality,
|
|
463
|
+
causalFrontier: reader.frontier(),
|
|
464
|
+
protocolVersion: positiveSafeInteger(reader.u32(), "transaction.protocolVersion"),
|
|
465
|
+
}
|
|
466
|
+
: {
|
|
467
|
+
...common,
|
|
468
|
+
modality,
|
|
469
|
+
priorNativeRevision: nonNegativeSafeInteger(
|
|
470
|
+
reader.safeUint(),
|
|
471
|
+
"transaction.priorNativeRevision",
|
|
472
|
+
),
|
|
473
|
+
nativeRevision: nonNegativeSafeInteger(reader.safeUint(), "transaction.nativeRevision"),
|
|
474
|
+
commitProtocolVersion: positiveSafeInteger(
|
|
475
|
+
reader.u32(),
|
|
476
|
+
"transaction.commitProtocolVersion",
|
|
477
|
+
),
|
|
478
|
+
},
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
function modalityTag(modality: EditableArtifactModality): number {
|
|
483
|
+
switch (modality) {
|
|
484
|
+
case "document":
|
|
485
|
+
return 1;
|
|
486
|
+
case "spreadsheet":
|
|
487
|
+
return 2;
|
|
488
|
+
case "presentation":
|
|
489
|
+
return 3;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
function decodeModality(tag: number): EditableArtifactModality {
|
|
494
|
+
if (tag === 1) return "document";
|
|
495
|
+
if (tag === 2) return "spreadsheet";
|
|
496
|
+
if (tag === 3) return "presentation";
|
|
497
|
+
throw wireError("invalid_modality", "artifact modality tag is invalid");
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function encodePendingFields(
|
|
501
|
+
writer: ArtifactWorkerBinaryWriter,
|
|
502
|
+
transaction: EditableArtifactPendingTransaction,
|
|
503
|
+
): void {
|
|
504
|
+
writer
|
|
505
|
+
.u8(modalityTag(transaction.modality))
|
|
506
|
+
.string(requireIdentifier(transaction.artifactId, "pending.artifactId"), MAX_ID_BYTES)
|
|
507
|
+
.string(
|
|
508
|
+
requireIdentifier(transaction.clientTransactionId, "pending.clientTransactionId"),
|
|
509
|
+
MAX_ID_BYTES,
|
|
510
|
+
)
|
|
511
|
+
.string(requireSha256(transaction.requestHash, "pending.requestHash"), 71)
|
|
512
|
+
.u32(positiveSafeInteger(transaction.protocolVersion, "pending.protocolVersion"))
|
|
513
|
+
.u32(positiveSafeInteger(transaction.modelSchemaVersion, "pending.modelSchemaVersion"))
|
|
514
|
+
.u32(positiveSafeInteger(transaction.commandVersion, "pending.commandVersion"))
|
|
515
|
+
.string(requireReplicaId(transaction.replicaId, "pending.replicaId"), 16)
|
|
516
|
+
.safeUint(positiveSafeInteger(transaction.replicaCounter, "pending.replicaCounter"))
|
|
517
|
+
.optionalString(
|
|
518
|
+
transaction.previousLocalTransactionId === null
|
|
519
|
+
? null
|
|
520
|
+
: requireIdentifier(
|
|
521
|
+
transaction.previousLocalTransactionId,
|
|
522
|
+
"pending.previousLocalTransactionId",
|
|
523
|
+
),
|
|
524
|
+
MAX_ID_BYTES,
|
|
525
|
+
)
|
|
526
|
+
.safeUint(
|
|
527
|
+
nonNegativeSafeInteger(transaction.observedHeadSequence, "pending.observedHeadSequence"),
|
|
528
|
+
);
|
|
529
|
+
if (transaction.modality === "spreadsheet") {
|
|
530
|
+
writer.frontier(transaction.causalBase).u32(transaction.selectiveUndoTargets.length);
|
|
531
|
+
encodeSortedIdentifiers(
|
|
532
|
+
writer,
|
|
533
|
+
transaction.selectiveUndoTargets,
|
|
534
|
+
"pending.selectiveUndoTargets",
|
|
535
|
+
);
|
|
536
|
+
} else {
|
|
537
|
+
writer.safeUint(
|
|
538
|
+
nonNegativeSafeInteger(transaction.observedNativeRevision, "pending.observedNativeRevision"),
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
writer.safeUint(nonNegativeSafeInteger(transaction.createdAt, "pending.createdAt"));
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
function decodePendingFields(
|
|
545
|
+
reader: ArtifactWorkerBinaryReader,
|
|
546
|
+
commandBytes: Uint8Array,
|
|
547
|
+
intentBytes: Uint8Array,
|
|
548
|
+
): EditableArtifactPendingTransaction {
|
|
549
|
+
const modality = decodeModality(reader.u8());
|
|
550
|
+
const artifactId = requireIdentifier(reader.string(MAX_ID_BYTES), "pending.artifactId");
|
|
551
|
+
const clientTransactionId = requireIdentifier(
|
|
552
|
+
reader.string(MAX_ID_BYTES),
|
|
553
|
+
"pending.clientTransactionId",
|
|
554
|
+
);
|
|
555
|
+
const requestHash = requireSha256(reader.string(71), "pending.requestHash");
|
|
556
|
+
const protocolVersion = positiveSafeInteger(reader.u32(), "pending.protocolVersion");
|
|
557
|
+
const modelSchemaVersion = positiveSafeInteger(reader.u32(), "pending.modelSchemaVersion");
|
|
558
|
+
const commandVersion = positiveSafeInteger(reader.u32(), "pending.commandVersion");
|
|
559
|
+
const replicaId = requireReplicaId(reader.string(16), "pending.replicaId");
|
|
560
|
+
const replicaCounter = positiveSafeInteger(reader.safeUint(), "pending.replicaCounter");
|
|
561
|
+
const previousLocalTransactionId = optionalIdentifier(
|
|
562
|
+
reader.optionalString(MAX_ID_BYTES),
|
|
563
|
+
"pending.previousLocalTransactionId",
|
|
564
|
+
);
|
|
565
|
+
const observedHeadSequence = nonNegativeSafeInteger(
|
|
566
|
+
reader.safeUint(),
|
|
567
|
+
"pending.observedHeadSequence",
|
|
568
|
+
);
|
|
569
|
+
const common = {
|
|
570
|
+
artifactId,
|
|
571
|
+
clientTransactionId,
|
|
572
|
+
requestHash,
|
|
573
|
+
protocolVersion,
|
|
574
|
+
modelSchemaVersion,
|
|
575
|
+
commandVersion,
|
|
576
|
+
replicaId,
|
|
577
|
+
replicaCounter,
|
|
578
|
+
previousLocalTransactionId,
|
|
579
|
+
observedHeadSequence,
|
|
580
|
+
commandBytes,
|
|
581
|
+
intentBytes,
|
|
582
|
+
} as const;
|
|
583
|
+
if (modality === "spreadsheet") {
|
|
584
|
+
const causalBase = reader.frontier();
|
|
585
|
+
const selectiveUndoTargets = decodeSortedIdentifiers(
|
|
586
|
+
reader,
|
|
587
|
+
reader.u32(),
|
|
588
|
+
"pending.selectiveUndoTargets",
|
|
589
|
+
);
|
|
590
|
+
return Object.freeze({
|
|
591
|
+
...common,
|
|
592
|
+
modality,
|
|
593
|
+
causalBase,
|
|
594
|
+
selectiveUndoTargets,
|
|
595
|
+
createdAt: nonNegativeSafeInteger(reader.safeUint(), "pending.createdAt"),
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
const observedNativeRevision = nonNegativeSafeInteger(
|
|
599
|
+
reader.safeUint(),
|
|
600
|
+
"pending.observedNativeRevision",
|
|
601
|
+
);
|
|
602
|
+
return Object.freeze({
|
|
603
|
+
...common,
|
|
604
|
+
modality,
|
|
605
|
+
observedNativeRevision,
|
|
606
|
+
createdAt: nonNegativeSafeInteger(reader.safeUint(), "pending.createdAt"),
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function encodeAuthorPendingFields(
|
|
611
|
+
writer: ArtifactWorkerBinaryWriter,
|
|
612
|
+
input: ArtifactWorkerAuthorPendingInput,
|
|
613
|
+
): void {
|
|
614
|
+
writer
|
|
615
|
+
.u8(modalityTag(input.modality))
|
|
616
|
+
.u32(positiveSafeInteger(input.protocolVersion, "author.protocolVersion"))
|
|
617
|
+
.string(requireVersion(input.kernelVersion, "author.kernelVersion"), MAX_VERSION_BYTES)
|
|
618
|
+
.u32(positiveSafeInteger(input.modelSchemaVersion, "author.modelSchemaVersion"))
|
|
619
|
+
.u32(positiveSafeInteger(input.commandVersion, "author.commandVersion"))
|
|
620
|
+
.string(requireIdentifier(input.artifactId, "author.artifactId"), MAX_ID_BYTES)
|
|
621
|
+
.string(
|
|
622
|
+
requireIdentifier(input.clientTransactionId, "author.clientTransactionId"),
|
|
623
|
+
MAX_ID_BYTES,
|
|
624
|
+
)
|
|
625
|
+
.string(requireReplicaId(input.replicaId, "author.replicaId"), 16)
|
|
626
|
+
.safeUint(positiveSafeInteger(input.replicaCounter, "author.replicaCounter"))
|
|
627
|
+
.optionalString(
|
|
628
|
+
input.previousLocalTransactionId === null
|
|
629
|
+
? null
|
|
630
|
+
: requireIdentifier(input.previousLocalTransactionId, "author.previousLocalTransactionId"),
|
|
631
|
+
MAX_ID_BYTES,
|
|
632
|
+
)
|
|
633
|
+
.safeUint(nonNegativeSafeInteger(input.observedHeadSequence, "author.observedHeadSequence"));
|
|
634
|
+
if (input.modality === "spreadsheet") {
|
|
635
|
+
const causalBase = input.causalBase ?? [];
|
|
636
|
+
const selectiveUndoTargets = input.selectiveUndoTargets ?? [];
|
|
637
|
+
writer.frontier(causalBase).u32(selectiveUndoTargets.length);
|
|
638
|
+
encodeSortedIdentifiers(writer, selectiveUndoTargets, "author.selectiveUndoTargets");
|
|
639
|
+
}
|
|
640
|
+
writer.safeUint(input.commandBytes.byteLength);
|
|
641
|
+
writer.safeUint(nonNegativeSafeInteger(input.createdAt, "author.createdAt"));
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function decodeAuthorPendingFields(
|
|
645
|
+
reader: ArtifactWorkerBinaryReader,
|
|
646
|
+
commandBytes: Uint8Array,
|
|
647
|
+
): ArtifactWorkerAuthorPendingInput {
|
|
648
|
+
const modality = decodeModality(reader.u8());
|
|
649
|
+
const value: ArtifactWorkerAuthorPendingInput = {
|
|
650
|
+
modality,
|
|
651
|
+
protocolVersion: positiveSafeInteger(reader.u32(), "author.protocolVersion"),
|
|
652
|
+
kernelVersion: requireVersion(reader.string(MAX_VERSION_BYTES), "author.kernelVersion"),
|
|
653
|
+
modelSchemaVersion: positiveSafeInteger(reader.u32(), "author.modelSchemaVersion"),
|
|
654
|
+
commandVersion: positiveSafeInteger(reader.u32(), "author.commandVersion"),
|
|
655
|
+
artifactId: requireIdentifier(reader.string(MAX_ID_BYTES), "author.artifactId"),
|
|
656
|
+
clientTransactionId: requireIdentifier(
|
|
657
|
+
reader.string(MAX_ID_BYTES),
|
|
658
|
+
"author.clientTransactionId",
|
|
659
|
+
),
|
|
660
|
+
replicaId: requireReplicaId(reader.string(16), "author.replicaId"),
|
|
661
|
+
replicaCounter: positiveSafeInteger(reader.safeUint(), "author.replicaCounter"),
|
|
662
|
+
previousLocalTransactionId: optionalIdentifier(
|
|
663
|
+
reader.optionalString(MAX_ID_BYTES),
|
|
664
|
+
"author.previousLocalTransactionId",
|
|
665
|
+
),
|
|
666
|
+
observedHeadSequence: nonNegativeSafeInteger(reader.safeUint(), "author.observedHeadSequence"),
|
|
667
|
+
...(modality === "spreadsheet"
|
|
668
|
+
? {
|
|
669
|
+
causalBase: reader.frontier(),
|
|
670
|
+
selectiveUndoTargets: decodeSortedIdentifiers(
|
|
671
|
+
reader,
|
|
672
|
+
reader.u32(),
|
|
673
|
+
"author.selectiveUndoTargets",
|
|
674
|
+
),
|
|
675
|
+
}
|
|
676
|
+
: {}),
|
|
677
|
+
commandBytes,
|
|
678
|
+
createdAt: 0,
|
|
679
|
+
};
|
|
680
|
+
const declaredCommandBytes = reader.safeUint();
|
|
681
|
+
if (declaredCommandBytes !== commandBytes.byteLength) {
|
|
682
|
+
throw wireError("invalid_segment", "author command length does not match its segment");
|
|
683
|
+
}
|
|
684
|
+
value.createdAt = nonNegativeSafeInteger(reader.safeUint(), "author.createdAt");
|
|
685
|
+
return Object.freeze(value);
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
function requireUrl(value: string, label: string): string {
|
|
689
|
+
const bounded = requireBoundedString(value, label, MAX_URL_BYTES);
|
|
690
|
+
let url: URL;
|
|
691
|
+
try {
|
|
692
|
+
url = new URL(bounded);
|
|
693
|
+
} catch (error) {
|
|
694
|
+
throw wireError("invalid_url", `${label} must be an absolute URL`, error);
|
|
695
|
+
}
|
|
696
|
+
if (!new Set(["https:", "http:", "file:"]).has(url.protocol)) {
|
|
697
|
+
throw wireError("invalid_url", `${label} uses an unsupported URL scheme`);
|
|
698
|
+
}
|
|
699
|
+
if (url.username || url.password || url.hash) {
|
|
700
|
+
throw wireError("invalid_url", `${label} must not contain credentials or a fragment`);
|
|
701
|
+
}
|
|
702
|
+
url.username = "";
|
|
703
|
+
url.password = "";
|
|
704
|
+
if (url.href !== bounded) {
|
|
705
|
+
throw wireError("noncanonical", `${label} must be a canonical URL without credentials`);
|
|
706
|
+
}
|
|
707
|
+
return bounded;
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
function requireIdentifier(value: string, label: string): string {
|
|
711
|
+
const bounded = requireBoundedString(value, label, MAX_ID_BYTES);
|
|
712
|
+
if (/\p{Cc}/u.test(bounded))
|
|
713
|
+
throw wireError("invalid_identifier", `${label} contains control characters`);
|
|
714
|
+
return bounded;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function requireVersion(value: string, label: string): string {
|
|
718
|
+
const bounded = requireBoundedString(value, label, MAX_VERSION_BYTES);
|
|
719
|
+
if (/\p{Cc}/u.test(bounded))
|
|
720
|
+
throw wireError("invalid_identifier", `${label} contains control characters`);
|
|
721
|
+
return bounded;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function optionalIdentifier(value: string | null, label: string): string | null {
|
|
725
|
+
return value === null ? null : requireIdentifier(value, label);
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
function requireReplicaId(value: string, label: string): string {
|
|
729
|
+
if (!/^(?!0{16}$)[0-9a-f]{16}$/.test(value)) {
|
|
730
|
+
throw wireError(
|
|
731
|
+
"invalid_identifier",
|
|
732
|
+
`${label} must be 16 lowercase nonzero hexadecimal digits`,
|
|
733
|
+
);
|
|
734
|
+
}
|
|
735
|
+
return value;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
function requireSha256(value: string, label: string): string {
|
|
739
|
+
if (!SHA256_PATTERN.test(value))
|
|
740
|
+
throw wireError("invalid_hash", `${label} must be 64 lowercase hex characters`);
|
|
741
|
+
return value;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function requireErrorCode(value: string): string {
|
|
745
|
+
if (!/^[a-z][a-z0-9_]{0,127}$/.test(value))
|
|
746
|
+
throw wireError("invalid_error", "invalid worker error code");
|
|
747
|
+
return value;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
function requireBoundedString(value: string, label: string, maximumBytes: number): string {
|
|
751
|
+
if (typeof value !== "string" || value.length === 0)
|
|
752
|
+
throw wireError("invalid_string", `${label} is empty`);
|
|
753
|
+
if (new TextEncoder().encode(value).byteLength > maximumBytes) {
|
|
754
|
+
throw wireError("string_too_large", `${label} exceeds its byte limit`);
|
|
755
|
+
}
|
|
756
|
+
return value;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
function positiveSafeInteger(value: number, label: string): number {
|
|
760
|
+
if (!Number.isSafeInteger(value) || value <= 0)
|
|
761
|
+
throw wireError("invalid_number", `${label} must be positive`);
|
|
762
|
+
return value;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
function nonNegativeSafeInteger(value: number, label: string): number {
|
|
766
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
767
|
+
throw wireError("invalid_number", `${label} must be a non-negative safe integer`);
|
|
768
|
+
}
|
|
769
|
+
return value;
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
function decodeBoolean(value: number, label: string): boolean {
|
|
773
|
+
if (value === 0) return false;
|
|
774
|
+
if (value === 1) return true;
|
|
775
|
+
throw wireError("noncanonical", `${label} has an invalid boolean tag`);
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
function encodeSortedIdentifiers(
|
|
779
|
+
writer: ArtifactWorkerBinaryWriter,
|
|
780
|
+
values: readonly string[],
|
|
781
|
+
label: string,
|
|
782
|
+
): void {
|
|
783
|
+
if (!Array.isArray(values) || values.length > 10_000) {
|
|
784
|
+
throw wireError("invalid_identifiers", `${label} exceeds its item limit`);
|
|
785
|
+
}
|
|
786
|
+
let previous = "";
|
|
787
|
+
for (let index = 0; index < values.length; index += 1) {
|
|
788
|
+
const value = requireIdentifier(values[index]!, `${label}[${index}]`);
|
|
789
|
+
if (index > 0 && previous >= value) {
|
|
790
|
+
throw wireError("noncanonical", `${label} must be unique and code-unit sorted`);
|
|
791
|
+
}
|
|
792
|
+
writer.string(value, MAX_ID_BYTES);
|
|
793
|
+
previous = value;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function decodeSortedIdentifiers(
|
|
798
|
+
reader: ArtifactWorkerBinaryReader,
|
|
799
|
+
count: number,
|
|
800
|
+
label: string,
|
|
801
|
+
): readonly string[] {
|
|
802
|
+
if (count > 10_000) throw wireError("invalid_identifiers", `${label} exceeds its item limit`);
|
|
803
|
+
const values: string[] = [];
|
|
804
|
+
let previous = "";
|
|
805
|
+
for (let index = 0; index < count; index += 1) {
|
|
806
|
+
const value = requireIdentifier(reader.string(MAX_ID_BYTES), `${label}[${index}]`);
|
|
807
|
+
if (index > 0 && previous >= value) {
|
|
808
|
+
throw wireError("noncanonical", `${label} must be unique and code-unit sorted`);
|
|
809
|
+
}
|
|
810
|
+
values.push(value);
|
|
811
|
+
previous = value;
|
|
812
|
+
}
|
|
813
|
+
return Object.freeze(values);
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
function wireError(code: string, message: string, cause?: unknown): ArtifactWorkerProtocolError {
|
|
817
|
+
return new ArtifactWorkerProtocolError(
|
|
818
|
+
code,
|
|
819
|
+
message,
|
|
820
|
+
cause === undefined ? undefined : { cause },
|
|
821
|
+
);
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
function validateInitializeLimits(input: ArtifactWorkerInitializeInput): void {
|
|
825
|
+
requireVersion(input.kernelVersion, "kernelVersion");
|
|
826
|
+
for (const [value, label] of [
|
|
827
|
+
[input.protocolVersion, "protocolVersion"],
|
|
828
|
+
[input.modelSchemaVersion, "modelSchemaVersion"],
|
|
829
|
+
[input.commandVersion, "commandVersion"],
|
|
830
|
+
] as const) {
|
|
831
|
+
positiveSafeInteger(value, label);
|
|
832
|
+
if (value > 65_535) throw wireError("invalid_version", `${label} exceeds uint16`);
|
|
833
|
+
}
|
|
834
|
+
const limits = [
|
|
835
|
+
[input.maximumSnapshotBytes, HARD_MAX_SNAPSHOT_BYTES, "maximumSnapshotBytes"],
|
|
836
|
+
[input.maximumCommandBytes, HARD_MAX_COMMAND_BYTES, "maximumCommandBytes"],
|
|
837
|
+
[input.maximumIntentBytes, HARD_MAX_INTENT_BYTES, "maximumIntentBytes"],
|
|
838
|
+
[
|
|
839
|
+
input.maximumCommittedTransactionBytes,
|
|
840
|
+
HARD_MAX_COMMITTED_TRANSACTION_BYTES,
|
|
841
|
+
"maximumCommittedTransactionBytes",
|
|
842
|
+
],
|
|
843
|
+
[input.maximumQueryBytes, HARD_MAX_QUERY_BYTES, "maximumQueryBytes"],
|
|
844
|
+
[input.maximumQueryResponseBytes, HARD_MAX_QUERY_RESPONSE_BYTES, "maximumQueryResponseBytes"],
|
|
845
|
+
[input.maximumPendingTransactions, HARD_MAX_PENDING_TRANSACTIONS, "maximumPendingTransactions"],
|
|
846
|
+
] as const;
|
|
847
|
+
for (const [value, maximum, label] of limits) {
|
|
848
|
+
positiveSafeInteger(value, label);
|
|
849
|
+
if (value > maximum)
|
|
850
|
+
throw wireError("invalid_limit", `${label} exceeds the hard safety ceiling`);
|
|
851
|
+
}
|
|
852
|
+
}
|