@opengeni/core 0.21.2 → 0.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/access/index.d.ts +9 -1
  2. package/dist/application/new-session-drafts.d.ts +1 -1
  3. package/dist/application/session-commands.d.ts +9 -4
  4. package/dist/billing/limits.d.ts +1 -1
  5. package/dist/chunk-6WKPWE5S.js +2380 -0
  6. package/dist/chunk-6WKPWE5S.js.map +1 -0
  7. package/dist/chunk-JJU6I5XD.js +2713 -0
  8. package/dist/chunk-JJU6I5XD.js.map +1 -0
  9. package/dist/chunk-NYPMQ7NO.js +2270 -0
  10. package/dist/chunk-NYPMQ7NO.js.map +1 -0
  11. package/dist/dependencies.d.ts +26 -6
  12. package/dist/domain/capabilities.d.ts +35 -3
  13. package/dist/domain/company-profile-durable-learning-adapter.d.ts +39 -0
  14. package/dist/domain/durable-learning-slack-publication.d.ts +72 -0
  15. package/dist/domain/editable-artifacts/agent-application.d.ts +192 -0
  16. package/dist/domain/editable-artifacts/durable-export-postgres.d.ts +4 -0
  17. package/dist/domain/editable-artifacts/durable-export.d.ts +212 -0
  18. package/dist/domain/editable-artifacts/errors.d.ts +47 -0
  19. package/dist/domain/editable-artifacts/hash.d.ts +11 -0
  20. package/dist/domain/editable-artifacts/in-memory.d.ts +99 -0
  21. package/dist/domain/editable-artifacts/index.d.ts +14 -0
  22. package/dist/domain/editable-artifacts/ports.d.ts +341 -0
  23. package/dist/domain/editable-artifacts/postgres-adapters.d.ts +13 -0
  24. package/dist/domain/editable-artifacts/service.d.ts +105 -0
  25. package/dist/domain/editable-artifacts/snapshot-compaction.d.ts +44 -0
  26. package/dist/domain/editable-artifacts/snapshot-verifier-genesis.d.ts +50 -0
  27. package/dist/domain/editable-artifacts/snapshot-verifier-materialization.d.ts +21 -0
  28. package/dist/domain/editable-artifacts/snapshot-verifier.d.ts +118 -0
  29. package/dist/domain/editable-artifacts/types.d.ts +410 -0
  30. package/dist/domain/memory-slack-delivery.d.ts +19 -0
  31. package/dist/domain/memory-slack-publication.d.ts +0 -2
  32. package/dist/domain/packs.d.ts +29 -1
  33. package/dist/domain/personal-connection-delegations.d.ts +9 -1
  34. package/dist/domain/scheduled-tasks.d.ts +27 -3
  35. package/dist/domain/session-tool-policy.d.ts +2 -2
  36. package/dist/domain/sessions.d.ts +44 -10
  37. package/dist/domain/skill-imports.d.ts +32 -0
  38. package/dist/domain/slack-publication-secret-safety.d.ts +8 -0
  39. package/dist/domain/timeline-annotations.d.ts +5 -0
  40. package/dist/domain/video-generation-capabilities.d.ts +9 -0
  41. package/dist/domain/video-generation.d.ts +47 -0
  42. package/dist/editable-artifact-live/application.d.ts +69 -0
  43. package/dist/editable-artifact-live/errors.d.ts +1 -0
  44. package/dist/editable-artifact-live/index.d.ts +8 -0
  45. package/dist/editable-artifact-live/outbox-dispatcher.d.ts +162 -0
  46. package/dist/editable-artifact-live/ports.d.ts +100 -0
  47. package/dist/editable-artifact-live/server.d.ts +71 -0
  48. package/dist/editable-artifact-live/ticket.d.ts +40 -0
  49. package/dist/editable-artifact-live/types.d.ts +213 -0
  50. package/dist/editable-artifact-live/wire.d.ts +34 -0
  51. package/dist/editable-artifact-live.d.ts +2 -0
  52. package/dist/editable-artifact-live.js +58 -0
  53. package/dist/editable-artifact-live.js.map +1 -0
  54. package/dist/editable-artifacts.d.ts +2 -0
  55. package/dist/editable-artifacts.js +179 -0
  56. package/dist/editable-artifacts.js.map +1 -0
  57. package/dist/index.d.ts +35 -25
  58. package/dist/index.js +3184 -652
  59. package/dist/index.js.map +1 -1
  60. package/dist/managed-session.d.ts +53 -0
  61. package/dist/rigs/index.d.ts +1 -1
  62. package/dist/rigs/provider-images.d.ts +31 -0
  63. package/dist/sandbox/fleet.d.ts +9 -1
  64. package/dist/sandbox/routing.d.ts +1 -0
  65. package/dist/session-authorization.d.ts +10 -1
  66. package/dist/workflow-wake-contract.d.ts +7 -0
  67. package/package.json +18 -10
  68. package/src/access/index.ts +43 -3
  69. package/src/application/new-session-drafts.ts +2 -0
  70. package/src/application/session-commands.ts +217 -116
  71. package/src/dependencies.ts +30 -2
  72. package/src/domain/capabilities.ts +672 -218
  73. package/src/domain/company-profile-durable-learning-adapter.ts +221 -0
  74. package/src/domain/durable-learning-slack-publication.ts +202 -0
  75. package/src/domain/editable-artifacts/agent-application.ts +687 -0
  76. package/src/domain/editable-artifacts/durable-export-postgres.ts +139 -0
  77. package/src/domain/editable-artifacts/durable-export.ts +734 -0
  78. package/src/domain/editable-artifacts/errors.ts +115 -0
  79. package/src/domain/editable-artifacts/hash.ts +131 -0
  80. package/src/domain/editable-artifacts/in-memory.ts +1662 -0
  81. package/src/domain/editable-artifacts/index.ts +14 -0
  82. package/src/domain/editable-artifacts/ports.ts +462 -0
  83. package/src/domain/editable-artifacts/postgres-adapters.ts +49 -0
  84. package/src/domain/editable-artifacts/service.ts +2402 -0
  85. package/src/domain/editable-artifacts/snapshot-compaction.ts +246 -0
  86. package/src/domain/editable-artifacts/snapshot-verifier-genesis.ts +235 -0
  87. package/src/domain/editable-artifacts/snapshot-verifier-materialization.ts +74 -0
  88. package/src/domain/editable-artifacts/snapshot-verifier.ts +687 -0
  89. package/src/domain/editable-artifacts/types.ts +835 -0
  90. package/src/domain/insights.ts +136 -33
  91. package/src/domain/memory-slack-delivery.ts +204 -0
  92. package/src/domain/memory-slack-publication.ts +10 -19
  93. package/src/domain/packs.ts +266 -5
  94. package/src/domain/personal-connection-delegations.ts +97 -6
  95. package/src/domain/resources.ts +10 -20
  96. package/src/domain/scheduled-tasks.ts +365 -25
  97. package/src/domain/session-tool-policy.ts +8 -2
  98. package/src/domain/sessions.ts +388 -155
  99. package/src/domain/skill-imports.ts +350 -0
  100. package/src/domain/slack-publication-secret-safety.ts +42 -0
  101. package/src/domain/timeline-annotations.ts +204 -0
  102. package/src/domain/video-generation-capabilities.ts +69 -0
  103. package/src/domain/video-generation.ts +249 -0
  104. package/src/editable-artifact-live/application.ts +182 -0
  105. package/src/editable-artifact-live/errors.ts +4 -0
  106. package/src/editable-artifact-live/index.ts +8 -0
  107. package/src/editable-artifact-live/outbox-dispatcher.ts +916 -0
  108. package/src/editable-artifact-live/ports.ts +139 -0
  109. package/src/editable-artifact-live/server.ts +1707 -0
  110. package/src/editable-artifact-live/ticket.ts +217 -0
  111. package/src/editable-artifact-live/types.ts +286 -0
  112. package/src/editable-artifact-live/wire.ts +95 -0
  113. package/src/editable-artifact-live.ts +2 -0
  114. package/src/editable-artifacts.ts +2 -0
  115. package/src/index.ts +14 -0
  116. package/src/managed-auth-type.ts +4 -3
  117. package/src/managed-session.ts +36 -0
  118. package/src/rigs/index.ts +26 -19
  119. package/src/rigs/provider-images.ts +110 -0
  120. package/src/sandbox/fleet.ts +56 -1
  121. package/src/sandbox/routing.ts +32 -3
  122. package/src/session-authorization.ts +20 -0
  123. package/src/workflow-wake-contract.ts +8 -0
@@ -0,0 +1,2713 @@
1
+ // src/domain/editable-artifacts/types.ts
2
+ import { EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES } from "@opengeni/contracts/editable-artifacts";
3
+ var STABLE_ID_PATTERN = /^[0-9a-f]{32}$/;
4
+ var REPLICA_ID_PATTERN = /^[0-9a-f]{16}$/;
5
+ var SHA256_PATTERN = /^sha256:[0-9a-f]{64}$/;
6
+ function assertNonzeroHex(value, pattern, label) {
7
+ if (!pattern.test(value) || /^0+$/.test(value)) {
8
+ throw new TypeError(`${label} must be fixed-width lowercase nonzero hexadecimal text`);
9
+ }
10
+ }
11
+ function editableArtifactId(value) {
12
+ assertNonzeroHex(value, STABLE_ID_PATTERN, "artifact id");
13
+ return value;
14
+ }
15
+ function editableArtifactTransactionId(value) {
16
+ assertNonzeroHex(value, STABLE_ID_PATTERN, "server transaction id");
17
+ return value;
18
+ }
19
+ function editableArtifactReceiptId(value) {
20
+ assertNonzeroHex(value, STABLE_ID_PATTERN, "receipt id");
21
+ return value;
22
+ }
23
+ function editableArtifactSnapshotId(value) {
24
+ assertNonzeroHex(value, STABLE_ID_PATTERN, "snapshot id");
25
+ return value;
26
+ }
27
+ function editableArtifactOutboxId(value) {
28
+ assertNonzeroHex(value, STABLE_ID_PATTERN, "outbox id");
29
+ return value;
30
+ }
31
+ function editableArtifactOperationId(value) {
32
+ assertNonzeroHex(value, STABLE_ID_PATTERN, "operation id");
33
+ return value;
34
+ }
35
+ function editableArtifactReplicaId(value) {
36
+ assertNonzeroHex(value, REPLICA_ID_PATTERN, "replica id");
37
+ return value;
38
+ }
39
+ function editableArtifactClientTransactionId(value) {
40
+ if (value.length < 1 || value.length > 200 || !/^[A-Za-z0-9._:-]+$/.test(value)) {
41
+ throw new TypeError("client transaction id must contain 1-200 portable identifier characters");
42
+ }
43
+ return value;
44
+ }
45
+ function editableArtifactRequestHash(value) {
46
+ if (!SHA256_PATTERN.test(value)) {
47
+ throw new TypeError("request hash must be sha256 followed by 64 lowercase hexadecimal digits");
48
+ }
49
+ return value;
50
+ }
51
+ function editableArtifactStateHash(value) {
52
+ if (!SHA256_PATTERN.test(value)) {
53
+ throw new TypeError("state hash must be sha256 followed by 64 lowercase hexadecimal digits");
54
+ }
55
+ return value;
56
+ }
57
+ function editableArtifactContentHash(value) {
58
+ if (!SHA256_PATTERN.test(value)) {
59
+ throw new TypeError("content hash must be sha256 followed by 64 lowercase hexadecimal digits");
60
+ }
61
+ return value;
62
+ }
63
+ function editableArtifactScope(input) {
64
+ return Object.freeze({
65
+ accountId: boundedIdentity(input.accountId, "account id"),
66
+ workspaceId: boundedIdentity(input.workspaceId, "workspace id")
67
+ });
68
+ }
69
+ function boundedIdentity(value, label) {
70
+ if (value.length < 1 || value.length > 256 || value.trim() !== value || !isWellFormedPersistedText(value)) {
71
+ throw new TypeError(`${label} must contain 1-256 non-padding characters`);
72
+ }
73
+ return value;
74
+ }
75
+ function boundedActorIdentity(value, label) {
76
+ const byteLength = new TextEncoder().encode(value).byteLength;
77
+ if (value.length < 1 || value.length > 256 || byteLength > 1024 || value.trim() !== value || !isWellFormedPersistedText(value)) {
78
+ throw new TypeError(
79
+ `${label} must contain 1-256 well-formed non-padding characters and at most 1024 UTF-8 bytes`
80
+ );
81
+ }
82
+ return value;
83
+ }
84
+ function isWellFormedPersistedText(value) {
85
+ if (value.includes("\0")) return false;
86
+ for (let index = 0; index < value.length; index += 1) {
87
+ const codeUnit = value.charCodeAt(index);
88
+ if (codeUnit >= 55296 && codeUnit <= 56319) {
89
+ const next = value.charCodeAt(index + 1);
90
+ if (!Number.isFinite(next) || next < 56320 || next > 57343) return false;
91
+ index += 1;
92
+ } else if (codeUnit >= 56320 && codeUnit <= 57343) {
93
+ return false;
94
+ }
95
+ }
96
+ return true;
97
+ }
98
+ function validateEditableArtifactActor(actor) {
99
+ const record = exactPlainDataRecord(actor, "editable artifact actor");
100
+ const kind = ownDataValue(record, "kind", "editable artifact actor");
101
+ const allowed = kind === "human" ? ["kind", "subjectId", "replicaId"] : kind === "agent" ? ["kind", "subjectId", "replicaId", "sessionId", "turnId", "attemptId", "generation"] : kind === "service" ? ["kind", "subjectId", "replicaId", "service"] : null;
102
+ if (!allowed) throw new TypeError("editable artifact actor kind is invalid");
103
+ const names = Object.getOwnPropertyNames(record);
104
+ if (names.length !== allowed.length || names.some((name) => !allowed.includes(name))) {
105
+ throw new TypeError("editable artifact actor contains missing or unknown properties");
106
+ }
107
+ const subjectId = ownDataValue(record, "subjectId", "editable artifact actor");
108
+ const replicaId = ownDataValue(record, "replicaId", "editable artifact actor");
109
+ if (typeof subjectId !== "string" || typeof replicaId !== "string") {
110
+ throw new TypeError("editable artifact actor identity is malformed");
111
+ }
112
+ boundedActorIdentity(subjectId, "actor subject id");
113
+ editableArtifactReplicaId(replicaId);
114
+ if (kind === "agent") {
115
+ const sessionId = ownDataValue(record, "sessionId", "agent actor");
116
+ const turnId = ownDataValue(record, "turnId", "agent actor");
117
+ const attemptId = ownDataValue(record, "attemptId", "agent actor");
118
+ const generation = ownDataValue(record, "generation", "agent actor");
119
+ if (typeof sessionId !== "string" || typeof turnId !== "string" || typeof attemptId !== "string" || typeof generation !== "number") {
120
+ throw new TypeError("editable artifact agent actor is malformed");
121
+ }
122
+ boundedActorIdentity(sessionId, "agent session id");
123
+ boundedActorIdentity(turnId, "agent turn id");
124
+ boundedActorIdentity(attemptId, "agent attempt id");
125
+ assertNonnegativeSafeInteger(generation, "agent generation");
126
+ } else if (kind === "service") {
127
+ const service = ownDataValue(record, "service", "service actor");
128
+ if (typeof service !== "string") {
129
+ throw new TypeError("editable artifact service actor is malformed");
130
+ }
131
+ boundedActorIdentity(service, "service name");
132
+ }
133
+ }
134
+ function exactPlainDataRecord(value, label) {
135
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
136
+ throw new TypeError(`${label} must be a plain data object`);
137
+ }
138
+ const prototype = Object.getPrototypeOf(value);
139
+ if (prototype !== Object.prototype && prototype !== null) {
140
+ throw new TypeError(`${label} must be a plain data object`);
141
+ }
142
+ if (Object.getOwnPropertySymbols(value).length > 0) {
143
+ throw new TypeError(`${label} must not contain symbol properties`);
144
+ }
145
+ return value;
146
+ }
147
+ function ownDataValue(record, key, label) {
148
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
149
+ if (!descriptor || !("value" in descriptor) || descriptor.enumerable !== true || descriptor.value === void 0) {
150
+ throw new TypeError(`${label} property ${key} must be own data`);
151
+ }
152
+ return descriptor.value;
153
+ }
154
+ function editableArtifactActorKey(actor) {
155
+ validateEditableArtifactActor(actor);
156
+ let actorKey;
157
+ switch (actor.kind) {
158
+ case "human":
159
+ actorKey = JSON.stringify([actor.kind, actor.subjectId]);
160
+ break;
161
+ case "agent":
162
+ actorKey = JSON.stringify([
163
+ actor.kind,
164
+ actor.subjectId,
165
+ actor.sessionId,
166
+ actor.turnId,
167
+ actor.attemptId,
168
+ actor.generation
169
+ ]);
170
+ break;
171
+ case "service":
172
+ actorKey = JSON.stringify([actor.kind, actor.subjectId, actor.service]);
173
+ break;
174
+ default:
175
+ throw new TypeError("editable artifact actor kind is invalid");
176
+ }
177
+ if (new TextEncoder().encode(actorKey).byteLength > 8192) {
178
+ throw new TypeError("editable artifact actor key exceeds 8192 UTF-8 bytes");
179
+ }
180
+ return actorKey;
181
+ }
182
+ function editableArtifactCausalFrontier(entries) {
183
+ const seen = /* @__PURE__ */ new Set();
184
+ const normalized = [...entries].map((entry) => {
185
+ const replicaId = editableArtifactReplicaId(entry.replicaId);
186
+ assertPositiveSafeInteger(entry.counter, `causal counter for ${replicaId}`);
187
+ if (seen.has(replicaId)) throw new TypeError(`duplicate causal replica: ${replicaId}`);
188
+ seen.add(replicaId);
189
+ return Object.freeze({ replicaId, counter: entry.counter });
190
+ });
191
+ normalized.sort((left, right) => compareCodeUnits(left.replicaId, right.replicaId));
192
+ return Object.freeze(normalized);
193
+ }
194
+ function causalCounter(frontier, replicaId) {
195
+ return frontier.find((entry) => entry.replicaId === replicaId)?.counter ?? 0;
196
+ }
197
+ function causalFrontierDominates(candidate, required) {
198
+ return required.every((entry) => causalCounter(candidate, entry.replicaId) >= entry.counter);
199
+ }
200
+ function causalFrontiersEqual(left, right) {
201
+ return left.length === right.length && left.every(
202
+ (entry, index) => entry.replicaId === right[index]?.replicaId && entry.counter === right[index]?.counter
203
+ );
204
+ }
205
+ function mergeCausalFrontiers(...frontiers) {
206
+ const counters = /* @__PURE__ */ new Map();
207
+ for (const frontier of frontiers) {
208
+ for (const entry of frontier) {
209
+ counters.set(entry.replicaId, Math.max(counters.get(entry.replicaId) ?? 0, entry.counter));
210
+ }
211
+ }
212
+ return editableArtifactCausalFrontier(
213
+ [...counters].map(([replicaId, counter]) => ({ replicaId, counter }))
214
+ );
215
+ }
216
+ var EDITABLE_ARTIFACT_ORIGINAL_IMPORT_MAX_BYTES = 64 * 1024 * 1024;
217
+ function assertNonnegativeSafeInteger(value, label) {
218
+ if (!Number.isSafeInteger(value) || value < 0) {
219
+ throw new TypeError(`${label} must be a nonnegative safe integer`);
220
+ }
221
+ }
222
+ function assertPositiveSafeInteger(value, label) {
223
+ if (!Number.isSafeInteger(value) || value < 1) {
224
+ throw new TypeError(`${label} must be a positive safe integer`);
225
+ }
226
+ }
227
+ function assertIsoTimestamp(value, label) {
228
+ if (!/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value) || !Number.isFinite(Date.parse(value)) || new Date(value).toISOString() !== value) {
229
+ throw new TypeError(`${label} must be a canonical UTC ISO timestamp`);
230
+ }
231
+ }
232
+ function assertBoundedArtifactTitle(value) {
233
+ const bytes = new TextEncoder().encode(value).byteLength;
234
+ if (bytes < 1 || bytes > 512 || value.trim() !== value || !isWellFormedPersistedText(value)) {
235
+ throw new TypeError("artifact title must contain 1-512 UTF-8 bytes without padding");
236
+ }
237
+ }
238
+ function assertBoundedOpaqueReference(value, label) {
239
+ const bytes = new TextEncoder().encode(value).byteLength;
240
+ if (bytes < 1 || bytes > 1024 || value.trim() !== value || !isWellFormedPersistedText(value)) {
241
+ throw new TypeError(`${label} must contain 1-1024 UTF-8 bytes without padding`);
242
+ }
243
+ }
244
+ function assertBoundedKernelVersion(value) {
245
+ if (typeof value !== "string") {
246
+ throw new TypeError("kernel version must be a string");
247
+ }
248
+ const bytes = new TextEncoder().encode(value).byteLength;
249
+ if (bytes < 1 || bytes > EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES || value.trim() !== value || !isWellFormedPersistedText(value)) {
250
+ throw new TypeError(
251
+ `kernel version must contain 1-${EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES} well-formed UTF-8 bytes without padding`
252
+ );
253
+ }
254
+ }
255
+ function compareCodeUnits(left, right) {
256
+ return left < right ? -1 : left > right ? 1 : 0;
257
+ }
258
+
259
+ // src/domain/editable-artifacts/errors.ts
260
+ var EditableArtifactDomainError = class extends Error {
261
+ constructor(code, message) {
262
+ super(message);
263
+ this.code = code;
264
+ this.name = "EditableArtifactDomainError";
265
+ }
266
+ };
267
+ var EditableArtifactNotFoundError = class extends EditableArtifactDomainError {
268
+ constructor() {
269
+ super("not_found", "Editable artifact was not found in the requested tenant scope");
270
+ }
271
+ };
272
+ var EditableArtifactInvalidRequestError = class extends EditableArtifactDomainError {
273
+ constructor(message) {
274
+ super("invalid_request", message);
275
+ }
276
+ };
277
+ var EditableArtifactForbiddenError = class extends EditableArtifactDomainError {
278
+ constructor(permission) {
279
+ super("forbidden", `Editable artifact permission denied: ${permission}`);
280
+ }
281
+ };
282
+ var EditableArtifactNotEditableError = class extends EditableArtifactDomainError {
283
+ constructor(state) {
284
+ super("not_editable", `Editable artifact cannot be changed while ${state}`);
285
+ }
286
+ };
287
+ var EditableArtifactIdempotencyConflictError = class extends EditableArtifactDomainError {
288
+ constructor() {
289
+ super(
290
+ "idempotency_conflict",
291
+ "Client transaction id was already committed with a different request hash"
292
+ );
293
+ }
294
+ };
295
+ var EditableArtifactRequestHashMismatchError = class extends EditableArtifactDomainError {
296
+ constructor() {
297
+ super("request_hash_mismatch", "Request hash does not match the canonical transaction body");
298
+ }
299
+ };
300
+ var EditableArtifactCausalFutureError = class extends EditableArtifactDomainError {
301
+ constructor() {
302
+ super("causal_future", "Causal base references state beyond the authoritative frontier");
303
+ }
304
+ };
305
+ var EditableArtifactCausalChainError = class extends EditableArtifactDomainError {
306
+ constructor(message) {
307
+ super("causal_chain_conflict", message);
308
+ }
309
+ };
310
+ var EditableArtifactStaleBaseError = class extends EditableArtifactDomainError {
311
+ constructor() {
312
+ super("stale_base", "Editable artifact changed after this transaction observed its base");
313
+ }
314
+ };
315
+ var EditableArtifactKernelContractError = class extends EditableArtifactDomainError {
316
+ constructor(message) {
317
+ super("kernel_contract_violation", message);
318
+ }
319
+ };
320
+ var EditableArtifactRetryableConflictError = class extends EditableArtifactDomainError {
321
+ constructor() {
322
+ super(
323
+ "retryable_conflict",
324
+ "Editable artifact changed repeatedly while applying the transaction; retry the same client transaction"
325
+ );
326
+ }
327
+ };
328
+ var EditableArtifactUndoTargetError = class extends EditableArtifactDomainError {
329
+ constructor(message) {
330
+ super("invalid_undo_target", message);
331
+ }
332
+ };
333
+ var EditableArtifactSnapshotConflictError = class extends EditableArtifactDomainError {
334
+ constructor(message) {
335
+ super("snapshot_conflict", message);
336
+ }
337
+ };
338
+ var EditableArtifactOutboxLeaseConflictError = class extends EditableArtifactDomainError {
339
+ constructor() {
340
+ super("outbox_lease_conflict", "Live outbox record is not leased by this publisher");
341
+ }
342
+ };
343
+
344
+ // src/domain/editable-artifacts/hash.ts
345
+ import { createHash } from "crypto";
346
+ import {
347
+ assertEditableArtifactRequestHash,
348
+ decodeEditableArtifactMutationIntent,
349
+ hashEditableArtifactMutationIntentBytes
350
+ } from "@opengeni/contracts/editable-artifacts";
351
+ var CREATE_HASH_DOMAIN = new TextEncoder().encode("OpenGeni editable artifact create\0v1\0");
352
+ var IMPORT_HASH_DOMAIN = new TextEncoder().encode("OpenGeni editable artifact import\0v1\0");
353
+ function hashEditableArtifactCreateRequest(request) {
354
+ if (request.modality !== "spreadsheet" && request.modality !== "presentation" && request.modality !== "document") {
355
+ throw new TypeError("Unknown editable artifact modality");
356
+ }
357
+ assertBoundedArtifactTitle(request.title);
358
+ const digest = createHash("sha256");
359
+ digest.update(CREATE_HASH_DOMAIN);
360
+ updateLengthFramedUtf8(digest, request.modality);
361
+ updateLengthFramedUtf8(digest, request.title);
362
+ return editableArtifactRequestHash(`sha256:${digest.digest("hex")}`);
363
+ }
364
+ function hashEditableArtifactImportRequest(request) {
365
+ assertImportRequestShape(request);
366
+ const digest = createHash("sha256");
367
+ digest.update(IMPORT_HASH_DOMAIN);
368
+ updateLengthFramedUtf8(digest, request.modality);
369
+ updateLengthFramedUtf8(digest, request.title);
370
+ updateLengthFramedUtf8(digest, request.originalImport.fileId);
371
+ updateLengthFramedUtf8(digest, String(request.originalImport.byteSize));
372
+ updateLengthFramedUtf8(digest, request.originalImport.contentHash);
373
+ updateLengthFramedUtf8(digest, request.originalImport.mimeType);
374
+ updateLengthFramedUtf8(digest, String(request.snapshot.byteSize));
375
+ updateLengthFramedUtf8(digest, request.snapshot.contentHash);
376
+ updateLengthFramedUtf8(digest, request.snapshot.stateHash);
377
+ updateLengthFramedUtf8(digest, String(request.snapshot.modelSchemaVersion));
378
+ updateLengthFramedUtf8(digest, request.snapshot.kernelVersion);
379
+ if (request.snapshot.modality === "spreadsheet") {
380
+ updateLengthFramedUtf8(digest, String(request.snapshot.operationProtocolVersion));
381
+ updateLengthFramedUtf8(digest, String(request.snapshot.crdtStateVersion));
382
+ updateLengthFramedUtf8(digest, String(request.snapshot.coveredCausalFrontier.length));
383
+ for (const entry of request.snapshot.coveredCausalFrontier) {
384
+ updateLengthFramedUtf8(digest, entry.replicaId);
385
+ updateLengthFramedUtf8(digest, String(entry.counter));
386
+ }
387
+ } else {
388
+ updateLengthFramedUtf8(digest, String(request.snapshot.nativeRevision));
389
+ }
390
+ return editableArtifactRequestHash(`sha256:${digest.digest("hex")}`);
391
+ }
392
+ function assertImportRequestShape(request) {
393
+ if (request.modality !== "spreadsheet" && request.modality !== "presentation" && request.modality !== "document") {
394
+ throw new TypeError("Unknown editable artifact modality");
395
+ }
396
+ assertBoundedArtifactTitle(request.title);
397
+ if (request.snapshot.modality !== request.modality) {
398
+ throw new TypeError("Imported snapshot modality does not match the Office source");
399
+ }
400
+ const expectedMimeType = request.modality === "spreadsheet" ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : request.modality === "document" ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/vnd.openxmlformats-officedocument.presentationml.presentation";
401
+ if (request.originalImport.mimeType !== expectedMimeType || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(
402
+ request.originalImport.fileId
403
+ ) || !Number.isSafeInteger(request.originalImport.byteSize) || request.originalImport.byteSize <= 0) {
404
+ throw new TypeError("Imported Office source facts are malformed");
405
+ }
406
+ }
407
+ function updateLengthFramedUtf8(digest, value) {
408
+ const bytes = Buffer.from(value, "utf8");
409
+ const length = Buffer.allocUnsafe(4);
410
+ length.writeUInt32BE(bytes.byteLength);
411
+ digest.update(length);
412
+ digest.update(bytes);
413
+ }
414
+ var OgatxEditableArtifactMutationIntentCodec = class {
415
+ async decodeAndVerify(input) {
416
+ const intentBytes = input.intentBytes.slice();
417
+ const requestHash = assertEditableArtifactRequestHash(input.requestHash);
418
+ const actualHash = hashEditableArtifactMutationIntentBytes(intentBytes);
419
+ if (actualHash !== requestHash) {
420
+ throw new EditableArtifactRequestHashMismatchError();
421
+ }
422
+ return decodeEditableArtifactMutationIntent(intentBytes);
423
+ }
424
+ };
425
+ var ogatxEditableArtifactMutationIntentCodec = new OgatxEditableArtifactMutationIntentCodec();
426
+
427
+ // src/domain/editable-artifacts/snapshot-verifier.ts
428
+ import { createHash as createHash2 } from "crypto";
429
+ import {
430
+ BoundedObjectReadError,
431
+ MAX_BOUNDED_OBJECT_CHUNK_BYTES
432
+ } from "@opengeni/storage";
433
+ import { decodeEditableArtifactSerializedCommit } from "@opengeni/contracts/editable-artifact-serialized-commit";
434
+ import { EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES } from "@opengeni/contracts/editable-artifacts";
435
+ var MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES = EDITABLE_ARTIFACT_PRODUCT_MAX_SNAPSHOT_BYTES;
436
+ var MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_BYTES = 256 * 1024 * 1024;
437
+ var MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_SEGMENTS = 1e5;
438
+ var MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_SEGMENT_BYTES = 8 * 1024 * 1024;
439
+ var EditableArtifactSnapshotVerificationError = class extends Error {
440
+ code;
441
+ constructor(code) {
442
+ super(snapshotErrorMessage(code));
443
+ this.name = "EditableArtifactSnapshotVerificationError";
444
+ this.code = code;
445
+ }
446
+ };
447
+ var ProductionEditableArtifactSnapshotVerifier = class {
448
+ #dependencies;
449
+ constructor(dependencies) {
450
+ const maxSnapshotBytes = dependencies.maxSnapshotBytes ?? MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES;
451
+ const maxReplayBytes = dependencies.maxReplayBytes ?? MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_BYTES;
452
+ const maxReplaySegments = dependencies.maxReplaySegments ?? MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_SEGMENTS;
453
+ const snapshotChunkBytes = dependencies.snapshotChunkBytes ?? 1024 * 1024;
454
+ for (const value of [maxSnapshotBytes, maxReplayBytes, maxReplaySegments, snapshotChunkBytes]) {
455
+ if (!Number.isSafeInteger(value) || value <= 0) {
456
+ throw new TypeError("Snapshot verifier limits must be positive safe integers");
457
+ }
458
+ }
459
+ if (maxSnapshotBytes > MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES || maxReplayBytes > MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_BYTES || maxReplaySegments > MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_SEGMENTS || snapshotChunkBytes > MAX_BOUNDED_OBJECT_CHUNK_BYTES) {
460
+ throw new TypeError("Snapshot verifier limits exceed absolute safety bounds");
461
+ }
462
+ this.#dependencies = Object.freeze({
463
+ objects: dependencies.objects,
464
+ kernel: dependencies.kernel,
465
+ replayPlans: dependencies.replayPlans,
466
+ maxSnapshotBytes,
467
+ maxReplayBytes,
468
+ maxReplaySegments,
469
+ snapshotChunkBytes
470
+ });
471
+ }
472
+ async verify(input) {
473
+ throwIfCancelled(input.signal);
474
+ const scope = validateScope(input.scope);
475
+ const artifactId = editableArtifactId(input.artifactId);
476
+ const snapshot = input.snapshot;
477
+ if (snapshot.byteSize > this.#dependencies.maxSnapshotBytes) {
478
+ throw new EditableArtifactSnapshotVerificationError("limit_exceeded");
479
+ }
480
+ let object;
481
+ try {
482
+ object = await this.#dependencies.objects.open({
483
+ opaqueReference: snapshot.blobReference,
484
+ maxBytes: this.#dependencies.maxSnapshotBytes,
485
+ expectedByteSize: snapshot.byteSize,
486
+ ...input.signal ? { signal: input.signal } : {}
487
+ });
488
+ if (object.contentType !== void 0 && object.contentType !== snapshot.mimeType) {
489
+ throw new EditableArtifactSnapshotVerificationError("content_type_mismatch");
490
+ }
491
+ const sourceDigest = createHash2("sha256");
492
+ let sourceBytes = 0;
493
+ let sourceFinished = false;
494
+ const sourceChunks = meterSnapshotChunks(
495
+ object.chunks({
496
+ chunkBytes: this.#dependencies.snapshotChunkBytes,
497
+ ...input.signal ? { signal: input.signal } : {}
498
+ }),
499
+ snapshot.byteSize,
500
+ (chunk) => {
501
+ sourceBytes += chunk.byteLength;
502
+ sourceDigest.update(chunk);
503
+ },
504
+ () => {
505
+ sourceFinished = true;
506
+ }
507
+ );
508
+ const replayPlan = this.#dependencies.replayPlans ? await this.#dependencies.replayPlans.resolve({
509
+ scope,
510
+ artifactId,
511
+ snapshot,
512
+ ...input.signal ? { signal: input.signal } : {}
513
+ }) : null;
514
+ const normalizedReplay = replayPlan ? validateReplayPlan(
515
+ replayPlan,
516
+ snapshot,
517
+ this.#dependencies.maxReplayBytes,
518
+ this.#dependencies.maxReplaySegments,
519
+ input.signal
520
+ ) : void 0;
521
+ const result = await this.#dependencies.kernel.verifyAndReconstruct({
522
+ scope,
523
+ artifactId,
524
+ modality: snapshot.modality,
525
+ expectedSnapshot: snapshot,
526
+ snapshotChunks: sourceChunks,
527
+ expectedSnapshotByteSize: snapshot.byteSize,
528
+ ...normalizedReplay ? { replayPlan: normalizedReplay.replayPlan } : {},
529
+ ...input.signal ? { signal: input.signal } : {}
530
+ });
531
+ throwIfCancelled(input.signal);
532
+ if (!sourceFinished || sourceBytes !== snapshot.byteSize) {
533
+ throw new EditableArtifactSnapshotVerificationError("truncated");
534
+ }
535
+ const sourceHash = editableArtifactContentHash(`sha256:${sourceDigest.digest("hex")}`);
536
+ if (sourceHash !== snapshot.contentHash) {
537
+ throw new EditableArtifactSnapshotVerificationError("content_hash_mismatch");
538
+ }
539
+ normalizedReplay?.assertConsumed();
540
+ validateKernelResult(result, scope, artifactId, snapshot, normalizedReplay?.replayPlan);
541
+ await object.assertUnchanged(input.signal);
542
+ } catch (error) {
543
+ throw mapVerificationFailure(error, input.signal);
544
+ } finally {
545
+ await object?.close();
546
+ }
547
+ }
548
+ };
549
+ function meterSnapshotChunks(chunks, expectedBytes, onChunk, onFinished) {
550
+ return {
551
+ async *[Symbol.asyncIterator]() {
552
+ let bytes = 0;
553
+ for await (const chunk of chunks) {
554
+ if (!(chunk instanceof Uint8Array) || chunk.byteLength === 0) {
555
+ throw new EditableArtifactSnapshotVerificationError("truncated");
556
+ }
557
+ bytes += chunk.byteLength;
558
+ if (bytes > expectedBytes) {
559
+ throw new EditableArtifactSnapshotVerificationError("limit_exceeded");
560
+ }
561
+ onChunk(chunk);
562
+ yield chunk;
563
+ }
564
+ if (bytes !== expectedBytes) {
565
+ throw new EditableArtifactSnapshotVerificationError("truncated");
566
+ }
567
+ onFinished();
568
+ }
569
+ };
570
+ }
571
+ function validateReplayPlan(plan, snapshot, maxBytes, maxSegments, signal) {
572
+ if (plan.modality !== snapshot.modality || !Number.isSafeInteger(plan.baseHeadSequence) || !Number.isSafeInteger(plan.targetHeadSequence) || plan.baseHeadSequence !== snapshot.coveredHeadSequence || plan.targetHeadSequence < plan.baseHeadSequence) {
573
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
574
+ }
575
+ const targetStateHash = editableArtifactStateHash(plan.targetStateHash);
576
+ if (plan.modality !== "spreadsheet" && (!Number.isSafeInteger(plan.targetNativeRevision) || plan.targetNativeRevision < 0)) {
577
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
578
+ }
579
+ let consumed = false;
580
+ const segments = meterReplaySegments(
581
+ plan.segments,
582
+ snapshot,
583
+ plan,
584
+ maxBytes,
585
+ maxSegments,
586
+ signal,
587
+ () => {
588
+ consumed = true;
589
+ }
590
+ );
591
+ const replayPlan = plan.modality === "spreadsheet" ? Object.freeze({
592
+ modality: "spreadsheet",
593
+ baseHeadSequence: plan.baseHeadSequence,
594
+ targetHeadSequence: plan.targetHeadSequence,
595
+ targetCausalFrontier: editableArtifactCausalFrontier(plan.targetCausalFrontier),
596
+ targetStateHash,
597
+ segments
598
+ }) : Object.freeze({
599
+ modality: plan.modality,
600
+ baseHeadSequence: plan.baseHeadSequence,
601
+ targetHeadSequence: plan.targetHeadSequence,
602
+ targetNativeRevision: plan.targetNativeRevision,
603
+ targetStateHash,
604
+ segments
605
+ });
606
+ return Object.freeze({
607
+ replayPlan,
608
+ assertConsumed() {
609
+ if (!consumed) {
610
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
611
+ }
612
+ }
613
+ });
614
+ }
615
+ function meterReplaySegments(segments, snapshot, plan, maxBytes, maxSegments, signal, onFinished) {
616
+ return {
617
+ async *[Symbol.asyncIterator]() {
618
+ let expectedSequence = plan.baseHeadSequence + 1;
619
+ let nativeRevision = snapshot.modality === "spreadsheet" ? null : snapshot.nativeRevision;
620
+ let priorStateHash = snapshot.stateHash;
621
+ let totalBytes = 0;
622
+ let count = 0;
623
+ for await (const segment of segments) {
624
+ throwIfCancelled(signal);
625
+ count += 1;
626
+ if (count > maxSegments) {
627
+ throw new EditableArtifactSnapshotVerificationError("limit_exceeded");
628
+ }
629
+ if (!Number.isSafeInteger(segment.sequenceStart) || !Number.isSafeInteger(segment.sequenceEnd) || segment.sequenceStart !== expectedSequence || segment.sequenceEnd < segment.sequenceStart || segment.sequenceEnd > plan.targetHeadSequence || segment.modality !== snapshot.modality || !(segment.bytes instanceof Uint8Array) || segment.bytes.byteLength < 1 || segment.bytes.byteLength > MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_SEGMENT_BYTES) {
630
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
631
+ }
632
+ totalBytes += segment.bytes.byteLength;
633
+ if (totalBytes > maxBytes) {
634
+ throw new EditableArtifactSnapshotVerificationError("limit_exceeded");
635
+ }
636
+ const hash = editableArtifactContentHash(
637
+ `sha256:${createHash2("sha256").update(segment.bytes).digest("hex")}`
638
+ );
639
+ if (hash !== segment.contentHash) {
640
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
641
+ }
642
+ if (snapshot.modality === "spreadsheet") {
643
+ if (segment.modality !== "spreadsheet" || segment.operationProtocolVersion !== snapshot.operationProtocolVersion) {
644
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
645
+ }
646
+ } else {
647
+ if (segment.modality !== snapshot.modality || segment.sequenceEnd !== segment.sequenceStart) {
648
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
649
+ }
650
+ try {
651
+ const summary = decodeEditableArtifactSerializedCommit(
652
+ segment.bytes,
653
+ snapshot.modality
654
+ );
655
+ if (summary.parentHeadSequence !== segment.sequenceStart - 1 || summary.resultHeadSequence !== segment.sequenceEnd || summary.priorNativeRevision !== nativeRevision || summary.priorStateHash !== priorStateHash) {
656
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
657
+ }
658
+ nativeRevision = summary.nativeReceipt.revision;
659
+ priorStateHash = editableArtifactStateHash(summary.stateHash);
660
+ } catch (error) {
661
+ if (error instanceof EditableArtifactSnapshotVerificationError) throw error;
662
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
663
+ }
664
+ }
665
+ expectedSequence = segment.sequenceEnd + 1;
666
+ yield segment.modality === "spreadsheet" ? Object.freeze({
667
+ ...segment,
668
+ modality: "spreadsheet",
669
+ bytes: segment.bytes.slice()
670
+ }) : Object.freeze({
671
+ ...segment,
672
+ modality: segment.modality,
673
+ bytes: segment.bytes.slice()
674
+ });
675
+ }
676
+ if (expectedSequence !== plan.targetHeadSequence + 1) {
677
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
678
+ }
679
+ if (plan.modality !== "spreadsheet" && (nativeRevision !== plan.targetNativeRevision || priorStateHash !== plan.targetStateHash)) {
680
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
681
+ }
682
+ onFinished();
683
+ }
684
+ };
685
+ }
686
+ function validateKernelResult(result, scope, artifactId, snapshot, replayPlan) {
687
+ if (result.canonicalReencodeVerified !== true || result.modality !== snapshot.modality || result.canonicalByteSize !== snapshot.byteSize || result.canonicalContentHash !== snapshot.contentHash) {
688
+ throw new EditableArtifactSnapshotVerificationError("canonical_mismatch");
689
+ }
690
+ if (result.scope.accountId !== scope.accountId || result.scope.workspaceId !== scope.workspaceId || result.artifactId !== artifactId) {
691
+ throw new EditableArtifactSnapshotVerificationError("identity_mismatch");
692
+ }
693
+ if (result.coveredHeadSequence !== snapshot.coveredHeadSequence) {
694
+ throw new EditableArtifactSnapshotVerificationError("coverage_mismatch");
695
+ }
696
+ if (editableArtifactStateHash(result.stateHash) !== snapshot.stateHash) {
697
+ throw new EditableArtifactSnapshotVerificationError("state_hash_mismatch");
698
+ }
699
+ if (result.modelSchemaVersion !== snapshot.modelSchemaVersion || result.kernelVersion !== snapshot.kernelVersion) {
700
+ throw new EditableArtifactSnapshotVerificationError("version_mismatch");
701
+ }
702
+ if (snapshot.modality === "spreadsheet") {
703
+ if (result.modality !== "spreadsheet" || result.fullCrdtStateVerified !== true || !causalFrontiersEqual(
704
+ editableArtifactCausalFrontier(result.coveredCausalFrontier),
705
+ snapshot.coveredCausalFrontier
706
+ )) {
707
+ throw new EditableArtifactSnapshotVerificationError("frontier_mismatch");
708
+ }
709
+ if (result.operationProtocolVersion !== snapshot.operationProtocolVersion || result.crdtStateVersion !== snapshot.crdtStateVersion) {
710
+ throw new EditableArtifactSnapshotVerificationError("version_mismatch");
711
+ }
712
+ if (replayPlan) {
713
+ if (replayPlan.modality !== "spreadsheet" || !result.replayedTarget || result.replayedTarget.headSequence !== replayPlan.targetHeadSequence || editableArtifactStateHash(result.replayedTarget.stateHash) !== replayPlan.targetStateHash || !causalFrontiersEqual(
714
+ editableArtifactCausalFrontier(result.replayedTarget.causalFrontier),
715
+ replayPlan.targetCausalFrontier
716
+ )) {
717
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
718
+ }
719
+ } else if (result.replayedTarget !== void 0) {
720
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
721
+ }
722
+ return;
723
+ }
724
+ if (result.modality !== snapshot.modality || result.nativeRevision !== snapshot.nativeRevision) {
725
+ throw new EditableArtifactSnapshotVerificationError("version_mismatch");
726
+ }
727
+ if (replayPlan) {
728
+ if (replayPlan.modality !== snapshot.modality || !result.replayedTarget || result.replayedTarget.headSequence !== replayPlan.targetHeadSequence || result.replayedTarget.nativeRevision !== replayPlan.targetNativeRevision || editableArtifactStateHash(result.replayedTarget.stateHash) !== replayPlan.targetStateHash) {
729
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
730
+ }
731
+ } else if (result.replayedTarget !== void 0) {
732
+ throw new EditableArtifactSnapshotVerificationError("replay_invalid");
733
+ }
734
+ }
735
+ function validateScope(scope) {
736
+ if (typeof scope.accountId !== "string" || scope.accountId.length < 1 || typeof scope.workspaceId !== "string" || scope.workspaceId.length < 1) {
737
+ throw new EditableArtifactSnapshotVerificationError("identity_mismatch");
738
+ }
739
+ return Object.freeze({
740
+ accountId: scope.accountId,
741
+ workspaceId: scope.workspaceId
742
+ });
743
+ }
744
+ function throwIfCancelled(signal) {
745
+ if (signal?.aborted) {
746
+ throw new EditableArtifactSnapshotVerificationError("cancelled");
747
+ }
748
+ }
749
+ function mapVerificationFailure(error, signal) {
750
+ if (error instanceof EditableArtifactSnapshotVerificationError) return error;
751
+ if (signal?.aborted) {
752
+ return new EditableArtifactSnapshotVerificationError("cancelled");
753
+ }
754
+ if (error instanceof BoundedObjectReadError) {
755
+ switch (error.code) {
756
+ case "aborted":
757
+ return new EditableArtifactSnapshotVerificationError("cancelled");
758
+ case "object_changed":
759
+ return new EditableArtifactSnapshotVerificationError("object_changed");
760
+ case "object_missing":
761
+ return new EditableArtifactSnapshotVerificationError("object_missing");
762
+ case "size_limit":
763
+ return new EditableArtifactSnapshotVerificationError("limit_exceeded");
764
+ case "truncated":
765
+ return new EditableArtifactSnapshotVerificationError("truncated");
766
+ case "backend_failure":
767
+ case "invalid_request":
768
+ return new EditableArtifactSnapshotVerificationError("storage_failure");
769
+ }
770
+ }
771
+ return new EditableArtifactSnapshotVerificationError("kernel_rejected");
772
+ }
773
+ function snapshotErrorMessage(code) {
774
+ switch (code) {
775
+ case "cancelled":
776
+ return "Snapshot verification was cancelled";
777
+ case "canonical_mismatch":
778
+ return "Snapshot is not the canonical kernel encoding";
779
+ case "content_hash_mismatch":
780
+ return "Snapshot content digest does not match immutable metadata";
781
+ case "content_type_mismatch":
782
+ return "Snapshot content type does not match immutable metadata";
783
+ case "coverage_mismatch":
784
+ return "Snapshot coverage does not match embedded state";
785
+ case "frontier_mismatch":
786
+ return "Snapshot causal frontier does not match embedded state";
787
+ case "identity_mismatch":
788
+ return "Snapshot authority identity does not match embedded state";
789
+ case "kernel_rejected":
790
+ return "Authoritative kernel rejected the snapshot";
791
+ case "limit_exceeded":
792
+ return "Snapshot exceeds a verification limit";
793
+ case "object_changed":
794
+ return "Snapshot object changed during verification";
795
+ case "object_missing":
796
+ return "Snapshot object is unavailable";
797
+ case "replay_invalid":
798
+ return "Snapshot operation-tail reconstruction is invalid";
799
+ case "state_hash_mismatch":
800
+ return "Snapshot state hash does not match reconstructed state";
801
+ case "storage_failure":
802
+ return "Snapshot object could not be read safely";
803
+ case "truncated":
804
+ return "Snapshot object is truncated";
805
+ case "version_mismatch":
806
+ return "Snapshot kernel or schema versions are incompatible";
807
+ }
808
+ }
809
+
810
+ // src/domain/editable-artifacts/service.ts
811
+ import {
812
+ EDITABLE_ARTIFACT_COMMAND_MAX_BYTES,
813
+ EDITABLE_ARTIFACT_INTENT_MAX_BYTES,
814
+ EDITABLE_ARTIFACT_INTENT_MAX_UNDO_TARGETS,
815
+ EDITABLE_ARTIFACT_INTENT_VERSION,
816
+ EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES as EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES2
817
+ } from "@opengeni/contracts/editable-artifacts";
818
+ import {
819
+ COMMITTED_TRANSACTION_PROTOCOL_VERSION,
820
+ MAX_COMMITTED_TRANSACTION_BYTES,
821
+ MAX_COMMITTED_TRANSACTION_OPERATIONS,
822
+ decodeCommittedTransactionSummary
823
+ } from "@opengeni/contracts/editable-artifact-committed-transaction";
824
+ import {
825
+ EDITABLE_ARTIFACT_SERIALIZED_COMMIT_VERSION,
826
+ decodeEditableArtifactSerializedCommit as decodeEditableArtifactSerializedCommit2,
827
+ encodeEditableArtifactSerializedCommit
828
+ } from "@opengeni/contracts/editable-artifact-serialized-commit";
829
+ import { editableArtifactCodecFor } from "@opengeni/contracts/editable-artifact-codec-registry";
830
+ var EDITABLE_ARTIFACT_MAX_OPERATIONS_PER_TRANSACTION = MAX_COMMITTED_TRANSACTION_OPERATIONS;
831
+ var EDITABLE_ARTIFACT_MAX_UNDO_TARGETS_PER_TRANSACTION = Math.min(
832
+ EDITABLE_ARTIFACT_INTENT_MAX_UNDO_TARGETS,
833
+ MAX_COMMITTED_TRANSACTION_OPERATIONS
834
+ );
835
+ var EDITABLE_ARTIFACT_MAX_COMMAND_BYTES_PER_TRANSACTION = EDITABLE_ARTIFACT_COMMAND_MAX_BYTES;
836
+ var EDITABLE_ARTIFACT_MAX_INTENT_BYTES_PER_TRANSACTION = EDITABLE_ARTIFACT_INTENT_MAX_BYTES;
837
+ var EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION = 1;
838
+ var EDITABLE_ARTIFACT_MAX_OPERATION_BYTES_PER_TRANSACTION = MAX_COMMITTED_TRANSACTION_BYTES;
839
+ var EDITABLE_ARTIFACT_MAX_COMMITTED_TRANSACTION_BYTES = EDITABLE_ARTIFACT_MAX_OPERATION_BYTES_PER_TRANSACTION;
840
+ var EDITABLE_ARTIFACT_OPERATION_PROTOCOL_VERSION = COMMITTED_TRANSACTION_PROTOCOL_VERSION;
841
+ var EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS = 4;
842
+ var EDITABLE_ARTIFACT_COMPACTION_TRANSACTION_THRESHOLD = 1024;
843
+ var EDITABLE_ARTIFACT_COMPACTION_BYTE_THRESHOLD = 16 * 1024 * 1024;
844
+ var EditableArtifactService = class {
845
+ constructor(dependencies) {
846
+ this.dependencies = dependencies;
847
+ }
848
+ inFlightCreations = /* @__PURE__ */ new Map();
849
+ inFlightTransactions = /* @__PURE__ */ new Map();
850
+ inFlightImports = /* @__PURE__ */ new Map();
851
+ inFlightCompactions = /* @__PURE__ */ new Map();
852
+ async createArtifact(input) {
853
+ const scope = editableArtifactScope(input.scope);
854
+ validateEditableArtifactActor(input.actor);
855
+ const actor = Object.freeze({ ...input.actor });
856
+ const request = normalizeCreateRequest(input.request);
857
+ const requestHash = hashEditableArtifactCreateRequest(request);
858
+ const authorityKey = editableArtifactActorKey(actor);
859
+ const inFlightKey = creationInFlightKey(scope, "create", authorityKey, request.idempotencyKey);
860
+ for (; ; ) {
861
+ const inFlight = this.inFlightCreations.get(inFlightKey);
862
+ if (inFlight) {
863
+ if (inFlight.requestHash === requestHash) {
864
+ const result = await inFlight.promise;
865
+ return copyCreateResult(result, true);
866
+ }
867
+ try {
868
+ await inFlight.promise;
869
+ } catch {
870
+ continue;
871
+ }
872
+ throw new EditableArtifactIdempotencyConflictError();
873
+ }
874
+ const promise = this.createArtifactOptimistically({
875
+ scope,
876
+ actor,
877
+ authorityKey,
878
+ request,
879
+ requestHash,
880
+ ...input.signal ? { signal: input.signal } : {}
881
+ });
882
+ this.inFlightCreations.set(inFlightKey, { requestHash, promise });
883
+ try {
884
+ return await promise;
885
+ } finally {
886
+ if (this.inFlightCreations.get(inFlightKey)?.promise === promise) {
887
+ this.inFlightCreations.delete(inFlightKey);
888
+ }
889
+ }
890
+ }
891
+ }
892
+ async createArtifactOptimistically(input) {
893
+ const { scope, actor, authorityKey, request, requestHash } = input;
894
+ const artifactId = editableArtifactId(this.dependencies.ids.next("artifact"));
895
+ let authorizationRevision = await this.requirePermission({
896
+ scope,
897
+ artifactId,
898
+ actor,
899
+ permission: "create"
900
+ });
901
+ const existing = await this.dependencies.store.findArtifactCreation(
902
+ scope,
903
+ "create",
904
+ authorityKey,
905
+ request.idempotencyKey
906
+ );
907
+ if (existing) {
908
+ if (existing.creationReceipt.requestHash !== requestHash) {
909
+ throw new EditableArtifactIdempotencyConflictError();
910
+ }
911
+ return copyCreateResult(existing, true);
912
+ }
913
+ const snapshotId = editableArtifactSnapshotId(this.dependencies.ids.next("snapshot"));
914
+ const candidate = validateSnapshotRequest(
915
+ await this.dependencies.genesis.prepare({
916
+ scope,
917
+ artifactId,
918
+ snapshotId,
919
+ modality: request.modality,
920
+ ...input.signal ? { signal: input.signal } : {}
921
+ })
922
+ );
923
+ if (candidate.snapshotId !== snapshotId || candidate.modality !== request.modality || candidate.coveredHeadSequence !== 0 || candidate.modality === "spreadsheet" && candidate.coveredCausalFrontier.length !== 0) {
924
+ throw new EditableArtifactKernelContractError(
925
+ "Genesis snapshot must use its assigned identity and empty modality boundary"
926
+ );
927
+ }
928
+ await this.dependencies.snapshotVerifier.verify({
929
+ scope,
930
+ artifactId,
931
+ actor,
932
+ snapshot: candidate
933
+ });
934
+ const publishedAt = canonicalNow(this.dependencies.clock);
935
+ if (Date.parse(candidate.verifiedAt) > Date.parse(publishedAt)) {
936
+ throw new EditableArtifactSnapshotConflictError(
937
+ "Snapshot verification time cannot be after publication"
938
+ );
939
+ }
940
+ const genesisSnapshot = Object.freeze({
941
+ ...candidate,
942
+ scope,
943
+ artifactId,
944
+ publishedAt
945
+ });
946
+ const receiptId = editableArtifactReceiptId(this.dependencies.ids.next("receipt"));
947
+ const outbox = snapshotOutboxRecord(
948
+ editableArtifactOutboxId(this.dependencies.ids.next("outbox")),
949
+ genesisSnapshot
950
+ );
951
+ for (let attempt = 1; attempt <= EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS; attempt += 1) {
952
+ const outcome = await this.dependencies.store.createArtifact({
953
+ scope,
954
+ artifactId,
955
+ authorizationActor: actor,
956
+ receiptId,
957
+ authorityKey,
958
+ idempotencyKey: request.idempotencyKey,
959
+ requestHash,
960
+ operationKind: "create",
961
+ expectedScopeAuthorizationRevision: authorizationRevision,
962
+ initialArtifactAuthorizationRevision: authorizationRevision,
963
+ modality: request.modality,
964
+ title: request.title,
965
+ createdBySubjectId: actor.subjectId,
966
+ genesisSnapshot,
967
+ outbox
968
+ });
969
+ if (outcome.kind === "result") return outcome.value;
970
+ authorizationRevision = await this.requirePermission({
971
+ scope,
972
+ artifactId,
973
+ actor,
974
+ permission: "create"
975
+ });
976
+ }
977
+ throw new EditableArtifactRetryableConflictError();
978
+ }
979
+ /**
980
+ * Publishes one verified Office import as the sequence-zero durable state.
981
+ * The caller may name immutable source/snapshot objects, but neither becomes
982
+ * authority until the native snapshot verifier and atomic store commit pass.
983
+ */
984
+ async importArtifact(input) {
985
+ const scope = editableArtifactScope(input.scope);
986
+ validateEditableArtifactActor(input.actor);
987
+ const actor = Object.freeze({ ...input.actor });
988
+ const request = normalizeImportRequest(input.request);
989
+ const requestHash = hashEditableArtifactImportRequest(request);
990
+ const authorityKey = editableArtifactActorKey(actor);
991
+ const inFlightKey = creationInFlightKey(scope, "import", authorityKey, request.idempotencyKey);
992
+ for (; ; ) {
993
+ const inFlight = this.inFlightImports.get(inFlightKey);
994
+ if (inFlight) {
995
+ if (inFlight.requestHash === requestHash) {
996
+ return copyCreateResult(await inFlight.promise, true);
997
+ }
998
+ try {
999
+ await inFlight.promise;
1000
+ } catch {
1001
+ continue;
1002
+ }
1003
+ throw new EditableArtifactIdempotencyConflictError();
1004
+ }
1005
+ const promise = this.importArtifactOptimistically({
1006
+ scope,
1007
+ actor,
1008
+ authorityKey,
1009
+ request,
1010
+ requestHash,
1011
+ ...input.signal ? { signal: input.signal } : {}
1012
+ });
1013
+ this.inFlightImports.set(inFlightKey, { requestHash, promise });
1014
+ try {
1015
+ return await promise;
1016
+ } finally {
1017
+ if (this.inFlightImports.get(inFlightKey)?.promise === promise) {
1018
+ this.inFlightImports.delete(inFlightKey);
1019
+ }
1020
+ }
1021
+ }
1022
+ }
1023
+ async importArtifactOptimistically(input) {
1024
+ const { scope, actor, authorityKey, request, requestHash } = input;
1025
+ const artifactId = editableArtifactId(this.dependencies.ids.next("artifact"));
1026
+ let authorizationRevision = await this.requirePermission({
1027
+ scope,
1028
+ artifactId,
1029
+ actor,
1030
+ permission: "import"
1031
+ });
1032
+ const existing = await this.dependencies.store.findArtifactCreation(
1033
+ scope,
1034
+ "import",
1035
+ authorityKey,
1036
+ request.idempotencyKey
1037
+ );
1038
+ if (existing) {
1039
+ if (existing.creationReceipt.requestHash !== requestHash) {
1040
+ throw new EditableArtifactIdempotencyConflictError();
1041
+ }
1042
+ return copyCreateResult(existing, true);
1043
+ }
1044
+ const snapshotId = editableArtifactSnapshotId(this.dependencies.ids.next("snapshot"));
1045
+ const verifiedAt = canonicalNow(this.dependencies.clock);
1046
+ const candidate = validateSnapshotRequest({
1047
+ ...request.snapshot,
1048
+ snapshotId,
1049
+ verifiedAt
1050
+ });
1051
+ if (candidate.modality !== request.modality || candidate.coveredHeadSequence !== 0) {
1052
+ throw new EditableArtifactKernelContractError(
1053
+ "Imported snapshot must use its assigned modality and sequence-zero boundary"
1054
+ );
1055
+ }
1056
+ await this.dependencies.snapshotVerifier.verify({
1057
+ scope,
1058
+ artifactId,
1059
+ actor,
1060
+ snapshot: candidate
1061
+ });
1062
+ const publishedAt = canonicalNow(this.dependencies.clock);
1063
+ if (Date.parse(candidate.verifiedAt) > Date.parse(publishedAt)) {
1064
+ throw new EditableArtifactSnapshotConflictError(
1065
+ "Snapshot verification time cannot be after publication"
1066
+ );
1067
+ }
1068
+ const genesisSnapshot = Object.freeze({
1069
+ ...candidate,
1070
+ scope,
1071
+ artifactId,
1072
+ publishedAt
1073
+ });
1074
+ const receiptId = editableArtifactReceiptId(this.dependencies.ids.next("receipt"));
1075
+ const originalImport = Object.freeze({
1076
+ ...request.originalImport,
1077
+ blobRefId: this.dependencies.ids.next("blob")
1078
+ });
1079
+ const outbox = snapshotOutboxRecord(
1080
+ editableArtifactOutboxId(this.dependencies.ids.next("outbox")),
1081
+ genesisSnapshot
1082
+ );
1083
+ for (let attempt = 1; attempt <= EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS; attempt += 1) {
1084
+ const outcome = await this.dependencies.store.createArtifact({
1085
+ scope,
1086
+ artifactId,
1087
+ authorizationActor: actor,
1088
+ receiptId,
1089
+ authorityKey,
1090
+ idempotencyKey: request.idempotencyKey,
1091
+ requestHash,
1092
+ operationKind: "import",
1093
+ expectedScopeAuthorizationRevision: authorizationRevision,
1094
+ initialArtifactAuthorizationRevision: authorizationRevision,
1095
+ modality: request.modality,
1096
+ title: request.title,
1097
+ createdBySubjectId: actor.subjectId,
1098
+ genesisSnapshot,
1099
+ originalImport,
1100
+ outbox
1101
+ });
1102
+ if (outcome.kind === "result") return outcome.value;
1103
+ authorizationRevision = await this.requirePermission({
1104
+ scope,
1105
+ artifactId,
1106
+ actor,
1107
+ permission: "import"
1108
+ });
1109
+ }
1110
+ throw new EditableArtifactRetryableConflictError();
1111
+ }
1112
+ async getArtifact(input) {
1113
+ const context = normalizeBoundaryContext(input);
1114
+ let authorizationRevision = await this.requirePermission({
1115
+ ...context,
1116
+ permission: "read"
1117
+ });
1118
+ for (let attempt = 1; attempt <= EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS; attempt += 1) {
1119
+ const outcome = await this.dependencies.store.readArtifactAtAuthorizationRevision(
1120
+ context.scope,
1121
+ context.artifactId,
1122
+ authorizationRevision
1123
+ );
1124
+ if (outcome.kind === "result") {
1125
+ if (!outcome.artifact) throw new EditableArtifactNotFoundError();
1126
+ return outcome.artifact;
1127
+ }
1128
+ authorizationRevision = await this.requirePermission({
1129
+ ...context,
1130
+ permission: "read"
1131
+ });
1132
+ }
1133
+ throw new EditableArtifactRetryableConflictError();
1134
+ }
1135
+ /**
1136
+ * Resolve one actor-scoped durable mutation identity after an idempotency
1137
+ * collision. Normal edits do not pay this read; agent transports use it to
1138
+ * recover a committed receipt after a lost response without process state.
1139
+ */
1140
+ async findTransactionReceipt(input) {
1141
+ const context = normalizeBoundaryContext(input);
1142
+ await this.requirePermission({ ...context, permission: "edit" });
1143
+ const result = await this.dependencies.store.readTransactionBasis(
1144
+ context.scope,
1145
+ context.artifactId,
1146
+ {
1147
+ actorKey: editableArtifactActorKey(context.actor),
1148
+ clientTransactionId: editableArtifactClientTransactionId(input.clientTransactionId),
1149
+ previousLocalTransactionId: null,
1150
+ selectiveUndoOperationIds: Object.freeze([])
1151
+ }
1152
+ );
1153
+ return result.kind === "existing" ? copyReceipt(result.receipt) : null;
1154
+ }
1155
+ /**
1156
+ * Returns one detached, internally consistent reconstruction basis for an
1157
+ * authorized current-head read. Inspection and compaction deliberately share
1158
+ * this store primitive so neither can observe a metadata/snapshot/tail mix.
1159
+ */
1160
+ async readCurrentKernelState(input) {
1161
+ const context = normalizeBoundaryContext(input);
1162
+ let authorizationRevision = await this.requirePermission({
1163
+ ...context,
1164
+ permission: "read"
1165
+ });
1166
+ for (let attempt = 1; attempt <= EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS; attempt += 1) {
1167
+ const outcome = await this.dependencies.store.readCurrentKernelState(
1168
+ context.scope,
1169
+ context.artifactId,
1170
+ authorizationRevision
1171
+ );
1172
+ if (outcome.kind === "basis") return outcome.state;
1173
+ authorizationRevision = await this.requirePermission({
1174
+ ...context,
1175
+ permission: "read"
1176
+ });
1177
+ }
1178
+ throw new EditableArtifactRetryableConflictError();
1179
+ }
1180
+ async applyTransaction(input) {
1181
+ const context = normalizeBoundaryContext(input);
1182
+ const request = normalizeTransactionEnvelope(input.request);
1183
+ const authorizationRevision = await this.requirePermission({
1184
+ ...context,
1185
+ permission: "edit"
1186
+ });
1187
+ const intent = normalizeMutationIntent(
1188
+ await this.dependencies.intentCodec.decodeAndVerify({
1189
+ intentBytes: request.intentBytes.slice(),
1190
+ requestHash: request.requestHash
1191
+ })
1192
+ );
1193
+ if (intent.artifactId !== context.artifactId) {
1194
+ throw new EditableArtifactInvalidRequestError("Mutation intent is bound to another artifact");
1195
+ }
1196
+ if (intent.replicaId !== context.actor.replicaId) {
1197
+ throw new EditableArtifactInvalidRequestError(
1198
+ "Mutation intent is bound to another actor replica"
1199
+ );
1200
+ }
1201
+ const intentBytes = request.intentBytes.slice();
1202
+ const actorKey = editableArtifactActorKey(context.actor);
1203
+ const inFlightKey = transactionInFlightKey(
1204
+ context.scope,
1205
+ context.artifactId,
1206
+ actorKey,
1207
+ intent.clientTransactionId
1208
+ );
1209
+ for (; ; ) {
1210
+ const inFlight = this.inFlightTransactions.get(inFlightKey);
1211
+ if (inFlight) {
1212
+ if (inFlight.requestHash === request.requestHash) {
1213
+ const result = await inFlight.promise;
1214
+ return Object.freeze({
1215
+ receipt: copyReceipt(result.receipt),
1216
+ replayed: true
1217
+ });
1218
+ }
1219
+ try {
1220
+ await inFlight.promise;
1221
+ } catch {
1222
+ }
1223
+ continue;
1224
+ }
1225
+ const promise = this.applyTransactionOptimistically({
1226
+ context,
1227
+ actorKey,
1228
+ request,
1229
+ intent,
1230
+ intentBytes,
1231
+ authorizationRevision
1232
+ });
1233
+ this.inFlightTransactions.set(inFlightKey, {
1234
+ requestHash: request.requestHash,
1235
+ promise
1236
+ });
1237
+ try {
1238
+ return await promise;
1239
+ } finally {
1240
+ if (this.inFlightTransactions.get(inFlightKey)?.promise === promise) {
1241
+ this.inFlightTransactions.delete(inFlightKey);
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+ async applyTransactionOptimistically(input) {
1247
+ const { context, actorKey, request, intent, intentBytes } = input;
1248
+ let authorizationRevision = input.authorizationRevision;
1249
+ for (let attempt = 1; attempt <= EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS; attempt += 1) {
1250
+ const basisRead = await this.dependencies.store.readTransactionBasis(
1251
+ context.scope,
1252
+ context.artifactId,
1253
+ {
1254
+ actorKey,
1255
+ clientTransactionId: intent.clientTransactionId,
1256
+ previousLocalTransactionId: intent.previousLocalTransactionId,
1257
+ selectiveUndoOperationIds: intent.selectiveUndoOperationIds
1258
+ }
1259
+ );
1260
+ if (basisRead.kind === "existing") {
1261
+ if (basisRead.receipt.requestHash !== request.requestHash) {
1262
+ throw new EditableArtifactIdempotencyConflictError();
1263
+ }
1264
+ return Object.freeze({
1265
+ receipt: copyReceipt(basisRead.receipt),
1266
+ replayed: true
1267
+ });
1268
+ }
1269
+ const basis = basisRead;
1270
+ const artifact = basis.artifact;
1271
+ if (artifact.authorizationRevision !== authorizationRevision) {
1272
+ authorizationRevision = await this.requirePermission({
1273
+ ...context,
1274
+ permission: "edit"
1275
+ });
1276
+ continue;
1277
+ }
1278
+ if (request.expectedHead && (artifact.headSequence !== request.expectedHead.sequence || artifact.stateHash !== request.expectedHead.stateHash)) {
1279
+ throw new EditableArtifactStaleBaseError();
1280
+ }
1281
+ if (artifact.lifecycle !== "active") {
1282
+ throw new EditableArtifactNotEditableError(artifact.lifecycle);
1283
+ }
1284
+ if (this.dependencies.compaction && shouldCompactKernelState(basis.kernelState)) {
1285
+ await this.compactCurrentHeadWithPermission(context, "edit");
1286
+ continue;
1287
+ }
1288
+ validateIntentCommands(artifact, intent);
1289
+ if (artifact.modality !== "spreadsheet") {
1290
+ if (intent.observedHeadSequence !== artifact.headSequence) {
1291
+ throw new EditableArtifactStaleBaseError();
1292
+ }
1293
+ if (intent.causalBase.length !== 0 || intent.selectiveUndoOperationIds.length !== 0) {
1294
+ throw new EditableArtifactInvalidRequestError(
1295
+ "Serialized artifacts do not accept CRDT causality or selective undo"
1296
+ );
1297
+ }
1298
+ validateSerializedPredecessor(
1299
+ basis.predecessor,
1300
+ context.actor.replicaId,
1301
+ intent,
1302
+ artifact.modality
1303
+ );
1304
+ if (basis.undoTargets.length !== 0 || basis.kernelState.modality !== artifact.modality || basis.kernelState.artifact.modality !== artifact.modality) {
1305
+ throw new Error("Editable artifact store returned a cross-modality serialized basis");
1306
+ }
1307
+ const priorNativeRevision = serializedBasisNativeRevision(basis.kernelState, artifact);
1308
+ const kernelResult2 = await this.dependencies.kernel.applyTransaction({
1309
+ modality: artifact.modality,
1310
+ state: basis.kernelState,
1311
+ actor: context.actor,
1312
+ intent,
1313
+ requestHash: request.requestHash,
1314
+ intentBytes: intentBytes.slice()
1315
+ });
1316
+ const serverTransactionId2 = editableArtifactTransactionId(
1317
+ this.dependencies.ids.next("transaction")
1318
+ );
1319
+ let normalizedKernel2;
1320
+ try {
1321
+ normalizedKernel2 = validateSerializedKernelResult({
1322
+ artifact,
1323
+ intent,
1324
+ intentBytes,
1325
+ requestHash: request.requestHash,
1326
+ serverTransactionId: serverTransactionId2,
1327
+ priorNativeRevision,
1328
+ result: kernelResult2
1329
+ });
1330
+ } catch (error) {
1331
+ if (error instanceof EditableArtifactKernelContractError) throw error;
1332
+ throw new EditableArtifactKernelContractError(
1333
+ `Authoritative kernel returned malformed serialized data: ${error instanceof Error ? error.message : "unknown contract error"}`
1334
+ );
1335
+ }
1336
+ const committedAt2 = canonicalNow(this.dependencies.clock);
1337
+ const sequence = artifact.headSequence + 1;
1338
+ assertPositiveSafeInteger(sequence, "serialized resulting head sequence");
1339
+ const committedTransaction2 = Object.freeze({
1340
+ scope: artifact.scope,
1341
+ artifactId: artifact.id,
1342
+ modality: artifact.modality,
1343
+ serverTransactionId: serverTransactionId2,
1344
+ requestHash: request.requestHash,
1345
+ sequenceStart: sequence,
1346
+ sequenceEnd: sequence,
1347
+ priorStateHash: artifact.stateHash,
1348
+ stateHash: normalizedKernel2.stateHash,
1349
+ commitProtocolVersion: EDITABLE_ARTIFACT_SERIALIZED_COMMIT_VERSION,
1350
+ priorNativeRevision,
1351
+ nativeRevision: normalizedKernel2.nativeRevision,
1352
+ commandCount: normalizedKernel2.commandCount,
1353
+ nativeReceiptBytes: normalizedKernel2.nativeReceiptBytes.slice(),
1354
+ modelSchemaVersion: normalizedKernel2.modelSchemaVersion,
1355
+ kernelVersion: normalizedKernel2.kernelVersion,
1356
+ committedTransactionBytes: normalizedKernel2.committedTransactionBytes.slice(),
1357
+ committedAt: committedAt2
1358
+ });
1359
+ const receipt2 = Object.freeze({
1360
+ receiptId: editableArtifactReceiptId(this.dependencies.ids.next("receipt")),
1361
+ scope: artifact.scope,
1362
+ artifactId: artifact.id,
1363
+ modality: artifact.modality,
1364
+ serverTransactionId: serverTransactionId2,
1365
+ clientTransactionId: intent.clientTransactionId,
1366
+ replicaId: context.actor.replicaId,
1367
+ replicaCounter: intent.replicaCounter,
1368
+ previousLocalTransactionId: intent.previousLocalTransactionId,
1369
+ requestHash: request.requestHash,
1370
+ intentBytes: intentBytes.slice(),
1371
+ actorKey,
1372
+ sequenceStart: sequence,
1373
+ sequenceEnd: sequence,
1374
+ priorStateHash: artifact.stateHash,
1375
+ stateHash: normalizedKernel2.stateHash,
1376
+ intentEnvelopeVersion: intent.envelopeVersion,
1377
+ intentProtocolVersion: intent.protocolVersion,
1378
+ commandProtocolVersion: intent.commandProtocolVersion,
1379
+ kernelVersion: normalizedKernel2.kernelVersion,
1380
+ modelSchemaVersion: normalizedKernel2.modelSchemaVersion,
1381
+ commitProtocolVersion: EDITABLE_ARTIFACT_SERIALIZED_COMMIT_VERSION,
1382
+ priorNativeRevision,
1383
+ nativeRevision: normalizedKernel2.nativeRevision,
1384
+ commandCount: normalizedKernel2.commandCount,
1385
+ committedAt: committedAt2
1386
+ });
1387
+ const outbox2 = transactionOutboxRecord(
1388
+ editableArtifactOutboxId(this.dependencies.ids.next("outbox")),
1389
+ receipt2
1390
+ );
1391
+ const commit2 = await this.dependencies.store.tryCommitAppliedTransaction({
1392
+ scope: context.scope,
1393
+ artifactId: context.artifactId,
1394
+ expectedLifecycle: "active",
1395
+ expectedAuthorizationRevision: authorizationRevision,
1396
+ authorizationActor: context.actor,
1397
+ expectedHeadSequence: artifact.headSequence,
1398
+ actorKey,
1399
+ clientTransactionId: intent.clientTransactionId,
1400
+ requestHash: request.requestHash,
1401
+ expectedPredecessor: basis.predecessor ? {
1402
+ receiptId: basis.predecessor.receiptId,
1403
+ serverTransactionId: basis.predecessor.serverTransactionId,
1404
+ actorKey: basis.predecessor.actorKey,
1405
+ clientTransactionId: basis.predecessor.clientTransactionId,
1406
+ replicaId: basis.predecessor.replicaId,
1407
+ replicaCounter: basis.predecessor.replicaCounter
1408
+ } : null,
1409
+ expectedUnclaimedUndoTargets: Object.freeze([]),
1410
+ serverTransactionId: serverTransactionId2,
1411
+ receipt: receipt2,
1412
+ committedTransaction: committedTransaction2,
1413
+ operations: Object.freeze([]),
1414
+ outbox: outbox2
1415
+ });
1416
+ if (commit2.kind === "committed") {
1417
+ return Object.freeze({
1418
+ receipt: copyReceipt(commit2.receipt),
1419
+ replayed: false
1420
+ });
1421
+ }
1422
+ if (commit2.kind === "replayed") {
1423
+ if (commit2.receipt.requestHash !== request.requestHash) {
1424
+ throw new EditableArtifactIdempotencyConflictError();
1425
+ }
1426
+ return Object.freeze({
1427
+ receipt: copyReceipt(commit2.receipt),
1428
+ replayed: true
1429
+ });
1430
+ }
1431
+ continue;
1432
+ }
1433
+ if (basis.kernelState.modality !== "spreadsheet" || basis.kernelState.artifact.modality !== "spreadsheet") {
1434
+ throw new Error("Editable artifact store returned a cross-modality spreadsheet basis");
1435
+ }
1436
+ if (intent.observedHeadSequence > artifact.headSequence) {
1437
+ throw new EditableArtifactCausalFutureError();
1438
+ }
1439
+ if (!causalFrontierDominates(artifact.causalFrontier, intent.causalBase)) {
1440
+ throw new EditableArtifactCausalFutureError();
1441
+ }
1442
+ let resolvedCausalBase = intent.causalBase;
1443
+ if (intent.previousLocalTransactionId === null) {
1444
+ if (intent.replicaCounter !== 1) {
1445
+ throw new EditableArtifactCausalChainError(
1446
+ "Only a replica's first transaction may omit its local predecessor"
1447
+ );
1448
+ }
1449
+ } else {
1450
+ const predecessor = basis.predecessor;
1451
+ if (!predecessor) {
1452
+ throw new EditableArtifactCausalChainError(
1453
+ "Previous local transaction is not committed yet"
1454
+ );
1455
+ }
1456
+ if (predecessor.modality !== "spreadsheet" || predecessor.replicaId !== context.actor.replicaId || predecessor.replicaCounter + 1 !== intent.replicaCounter) {
1457
+ throw new EditableArtifactCausalChainError(
1458
+ "Previous local transaction does not match this replica counter chain"
1459
+ );
1460
+ }
1461
+ resolvedCausalBase = mergeCausalFrontiers(
1462
+ intent.causalBase,
1463
+ predecessor.resolvedCausalBase,
1464
+ editableArtifactCausalFrontier([
1465
+ {
1466
+ replicaId: predecessor.replicaId,
1467
+ counter: predecessor.replicaCounter
1468
+ }
1469
+ ])
1470
+ );
1471
+ }
1472
+ if (intent.replicaCounter !== causalCounter(artifact.causalFrontier, context.actor.replicaId) + 1) {
1473
+ throw new EditableArtifactCausalChainError(
1474
+ "Replica counter does not immediately advance the authoritative frontier"
1475
+ );
1476
+ }
1477
+ if (!causalFrontierDominates(artifact.causalFrontier, resolvedCausalBase)) {
1478
+ throw new EditableArtifactCausalFutureError();
1479
+ }
1480
+ if (basis.undoTargets.length !== intent.selectiveUndoOperationIds.length) {
1481
+ throw new Error("Editable artifact store returned a malformed transaction undo basis");
1482
+ }
1483
+ const resolvedUndoTargets = [];
1484
+ for (const [index, operationId] of intent.selectiveUndoOperationIds.entries()) {
1485
+ const undoBasis = basis.undoTargets[index];
1486
+ if (!undoBasis || undoBasis.operationId !== operationId) {
1487
+ throw new Error("Editable artifact store returned a malformed transaction undo basis");
1488
+ }
1489
+ const operation = undoBasis.operation;
1490
+ if (!operation) {
1491
+ throw new EditableArtifactUndoTargetError(`Unknown undo target: ${operationId}`);
1492
+ }
1493
+ if (operation.actorKey !== actorKey) {
1494
+ throw new EditableArtifactUndoTargetError(
1495
+ `Selective undo may target only operations authored by the same authority: ${operationId}`
1496
+ );
1497
+ }
1498
+ if (undoBasis.claimedBy) {
1499
+ throw new EditableArtifactUndoTargetError(
1500
+ `Operation was already selectively undone: ${operationId}`
1501
+ );
1502
+ }
1503
+ if (causalCounter(resolvedCausalBase, operation.dot.replicaId) < operation.dot.counter) {
1504
+ throw new EditableArtifactUndoTargetError(
1505
+ `Selective undo target is not present in the transaction's causal base: ${operationId}`
1506
+ );
1507
+ }
1508
+ resolvedUndoTargets.push(operation);
1509
+ }
1510
+ Object.freeze(resolvedUndoTargets);
1511
+ const kernelResult = await this.dependencies.kernel.applyTransaction({
1512
+ modality: "spreadsheet",
1513
+ state: basis.kernelState,
1514
+ actor: context.actor,
1515
+ intent,
1516
+ requestHash: request.requestHash,
1517
+ intentBytes: intentBytes.slice(),
1518
+ resolvedCausalBase,
1519
+ resolvedUndoTargets
1520
+ });
1521
+ let normalizedKernel;
1522
+ try {
1523
+ normalizedKernel = validateKernelResult2(
1524
+ artifact,
1525
+ context.actor,
1526
+ intent,
1527
+ resolvedCausalBase,
1528
+ kernelResult
1529
+ );
1530
+ } catch (error) {
1531
+ if (error instanceof EditableArtifactKernelContractError) throw error;
1532
+ throw new EditableArtifactKernelContractError(
1533
+ `Authoritative kernel returned malformed data: ${error instanceof Error ? error.message : "unknown contract error"}`
1534
+ );
1535
+ }
1536
+ const committedAt = canonicalNow(this.dependencies.clock);
1537
+ const serverTransactionId = normalizedKernel.serverTransactionId;
1538
+ const sequenceStart = artifact.headSequence + 1;
1539
+ const sequenceEnd = artifact.headSequence + normalizedKernel.operationIds.length;
1540
+ assertNonnegativeSafeInteger(sequenceEnd, "resulting head sequence");
1541
+ const operations = normalizedKernel.operationIds.map(
1542
+ (operationId, index) => Object.freeze({
1543
+ scope: artifact.scope,
1544
+ artifactId: artifact.id,
1545
+ serverTransactionId,
1546
+ sequence: sequenceStart + index,
1547
+ actorKey,
1548
+ createdAt: committedAt,
1549
+ operationId,
1550
+ dot: normalizedKernel.dot
1551
+ })
1552
+ );
1553
+ const committedTransaction = Object.freeze({
1554
+ scope: artifact.scope,
1555
+ artifactId: artifact.id,
1556
+ modality: "spreadsheet",
1557
+ serverTransactionId,
1558
+ requestHash: request.requestHash,
1559
+ sequenceStart,
1560
+ sequenceEnd,
1561
+ priorStateHash: normalizedKernel.priorStateHash,
1562
+ stateHash: normalizedKernel.stateHash,
1563
+ dot: normalizedKernel.dot,
1564
+ resolvedCausalBase,
1565
+ resultingCausalFrontier: normalizedKernel.resultingCausalFrontier,
1566
+ operationIds: normalizedKernel.operationIds,
1567
+ operationProtocolVersion: normalizedKernel.operationProtocolVersion,
1568
+ modelSchemaVersion: normalizedKernel.modelSchemaVersion,
1569
+ kernelVersion: normalizedKernel.kernelVersion,
1570
+ committedTransactionBytes: normalizedKernel.committedTransactionBytes.slice(),
1571
+ committedAt
1572
+ });
1573
+ const receipt = Object.freeze({
1574
+ receiptId: editableArtifactReceiptId(this.dependencies.ids.next("receipt")),
1575
+ scope: artifact.scope,
1576
+ artifactId: artifact.id,
1577
+ modality: "spreadsheet",
1578
+ serverTransactionId,
1579
+ clientTransactionId: intent.clientTransactionId,
1580
+ replicaId: context.actor.replicaId,
1581
+ replicaCounter: intent.replicaCounter,
1582
+ previousLocalTransactionId: intent.previousLocalTransactionId,
1583
+ requestHash: request.requestHash,
1584
+ intentBytes: intentBytes.slice(),
1585
+ actorKey,
1586
+ sequenceStart,
1587
+ sequenceEnd,
1588
+ priorStateHash: normalizedKernel.priorStateHash,
1589
+ causalBase: intent.causalBase,
1590
+ resolvedCausalBase,
1591
+ resultingCausalFrontier: normalizedKernel.resultingCausalFrontier,
1592
+ stateHash: normalizedKernel.stateHash,
1593
+ operationCount: operations.length,
1594
+ selectiveUndoOperationIds: intent.selectiveUndoOperationIds,
1595
+ intentEnvelopeVersion: intent.envelopeVersion,
1596
+ intentProtocolVersion: intent.protocolVersion,
1597
+ commandProtocolVersion: intent.commandProtocolVersion,
1598
+ kernelVersion: normalizedKernel.kernelVersion,
1599
+ modelSchemaVersion: normalizedKernel.modelSchemaVersion,
1600
+ operationProtocolVersion: normalizedKernel.operationProtocolVersion,
1601
+ committedAt
1602
+ });
1603
+ const outbox = transactionOutboxRecord(
1604
+ editableArtifactOutboxId(this.dependencies.ids.next("outbox")),
1605
+ receipt
1606
+ );
1607
+ const commit = await this.dependencies.store.tryCommitAppliedTransaction({
1608
+ scope: context.scope,
1609
+ artifactId: context.artifactId,
1610
+ expectedLifecycle: "active",
1611
+ expectedAuthorizationRevision: authorizationRevision,
1612
+ authorizationActor: context.actor,
1613
+ expectedHeadSequence: artifact.headSequence,
1614
+ actorKey,
1615
+ clientTransactionId: intent.clientTransactionId,
1616
+ requestHash: request.requestHash,
1617
+ expectedPredecessor: basis.predecessor ? {
1618
+ receiptId: basis.predecessor.receiptId,
1619
+ serverTransactionId: basis.predecessor.serverTransactionId,
1620
+ actorKey: basis.predecessor.actorKey,
1621
+ clientTransactionId: basis.predecessor.clientTransactionId,
1622
+ replicaId: basis.predecessor.replicaId,
1623
+ replicaCounter: basis.predecessor.replicaCounter
1624
+ } : null,
1625
+ expectedUnclaimedUndoTargets: intent.selectiveUndoOperationIds,
1626
+ serverTransactionId,
1627
+ receipt,
1628
+ committedTransaction,
1629
+ operations,
1630
+ outbox
1631
+ });
1632
+ if (commit.kind === "committed") {
1633
+ return Object.freeze({
1634
+ receipt: copyReceipt(commit.receipt),
1635
+ replayed: false
1636
+ });
1637
+ }
1638
+ if (commit.kind === "replayed") {
1639
+ if (commit.receipt.requestHash !== request.requestHash) {
1640
+ throw new EditableArtifactIdempotencyConflictError();
1641
+ }
1642
+ return Object.freeze({
1643
+ receipt: copyReceipt(commit.receipt),
1644
+ replayed: true
1645
+ });
1646
+ }
1647
+ }
1648
+ throw new EditableArtifactRetryableConflictError();
1649
+ }
1650
+ async compactCurrentHead(input) {
1651
+ return await this.compactCurrentHeadWithPermission(input, "read");
1652
+ }
1653
+ async compactCurrentHeadWithPermission(input, authorizationPermission) {
1654
+ if (!this.dependencies.compaction) {
1655
+ throw new Error("Editable artifact snapshot compaction is not configured");
1656
+ }
1657
+ const context = normalizeBoundaryContext(input);
1658
+ const authorizationRevision = await this.requirePermission({
1659
+ ...context,
1660
+ permission: authorizationPermission
1661
+ });
1662
+ const key = compactionInFlightKey(
1663
+ context.scope,
1664
+ context.artifactId,
1665
+ context.actor,
1666
+ authorizationPermission
1667
+ );
1668
+ const existing = this.inFlightCompactions.get(key);
1669
+ if (existing) return await existing;
1670
+ const promise = this.compactCurrentHeadOnce({
1671
+ ...context,
1672
+ authorizationRevision,
1673
+ authorizationPermission,
1674
+ ...input.signal ? { signal: input.signal } : {}
1675
+ });
1676
+ this.inFlightCompactions.set(key, promise);
1677
+ try {
1678
+ return await promise;
1679
+ } finally {
1680
+ if (this.inFlightCompactions.get(key) === promise) this.inFlightCompactions.delete(key);
1681
+ }
1682
+ }
1683
+ async compactCurrentHeadOnce(input) {
1684
+ const compaction = this.dependencies.compaction;
1685
+ if (!compaction) throw new Error("Editable artifact snapshot compaction is not configured");
1686
+ let authorizationRevision = input.authorizationRevision;
1687
+ for (let attempt = 1; attempt <= EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS; attempt += 1) {
1688
+ if (input.signal?.aborted) {
1689
+ throw new EditableArtifactSnapshotVerificationError("cancelled");
1690
+ }
1691
+ const basis = await this.dependencies.store.readCurrentKernelState(
1692
+ input.scope,
1693
+ input.artifactId,
1694
+ authorizationRevision
1695
+ );
1696
+ if (basis.kind === "authorization_stale") {
1697
+ authorizationRevision = await this.requirePermission({
1698
+ ...input,
1699
+ permission: input.authorizationPermission
1700
+ });
1701
+ continue;
1702
+ }
1703
+ const current = basis.state.snapshot;
1704
+ if (current && current.coveredHeadSequence === basis.state.artifact.headSequence) {
1705
+ return current;
1706
+ }
1707
+ const candidate = await compaction.prepare({
1708
+ scope: input.scope,
1709
+ artifactId: input.artifactId,
1710
+ snapshotId: editableArtifactSnapshotId(this.dependencies.ids.next("snapshot")),
1711
+ state: basis.state,
1712
+ ...input.signal ? { signal: input.signal } : {}
1713
+ });
1714
+ await this.dependencies.snapshotVerifier.verify({ ...input, snapshot: candidate });
1715
+ try {
1716
+ const published = await this.commitVerifiedSnapshotCandidate({
1717
+ context: input,
1718
+ candidate,
1719
+ authorizationRevision,
1720
+ authorizationPermission: input.authorizationPermission,
1721
+ requireCurrentHead: true
1722
+ });
1723
+ return published.snapshot;
1724
+ } catch (error) {
1725
+ if (error instanceof EditableArtifactCompactionRaceError) continue;
1726
+ throw error;
1727
+ }
1728
+ }
1729
+ throw new EditableArtifactRetryableConflictError();
1730
+ }
1731
+ async publishVerifiedSnapshot(input) {
1732
+ const context = normalizeBoundaryContext(input);
1733
+ const candidate = validateSnapshotRequest(input.snapshot);
1734
+ const authorizationRevision = await this.requirePermission({
1735
+ ...context,
1736
+ permission: "manage"
1737
+ });
1738
+ await this.dependencies.snapshotVerifier.verify({
1739
+ ...context,
1740
+ snapshot: candidate
1741
+ });
1742
+ return await this.commitVerifiedSnapshotCandidate({
1743
+ context,
1744
+ candidate,
1745
+ authorizationRevision,
1746
+ authorizationPermission: "manage",
1747
+ requireCurrentHead: false
1748
+ });
1749
+ }
1750
+ async commitVerifiedSnapshotCandidate(input) {
1751
+ const { context, candidate, authorizationPermission } = input;
1752
+ let authorizationRevision = input.authorizationRevision;
1753
+ for (let attempt = 1; attempt <= EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS; attempt += 1) {
1754
+ const outcome = await this.dependencies.store.withSnapshotPublicationLock(
1755
+ context.scope,
1756
+ context.artifactId,
1757
+ async (unitOfWork) => {
1758
+ const artifact = unitOfWork.artifact();
1759
+ if (artifact.modality !== candidate.modality) {
1760
+ throw new EditableArtifactSnapshotConflictError(
1761
+ "Snapshot modality does not match the durable artifact"
1762
+ );
1763
+ }
1764
+ if (input.requireCurrentHead && candidate.coveredHeadSequence !== artifact.headSequence) {
1765
+ throw new EditableArtifactCompactionRaceError(
1766
+ "Compaction candidate no longer covers the current durable head"
1767
+ );
1768
+ }
1769
+ if (artifact.authorizationRevision !== authorizationRevision) {
1770
+ return Object.freeze({ kind: "authorization_stale" });
1771
+ }
1772
+ const existing = await unitOfWork.findSnapshot(candidate.snapshotId);
1773
+ if (existing) {
1774
+ if (!sameSnapshotRequest(existing, candidate)) {
1775
+ throw new EditableArtifactSnapshotConflictError(
1776
+ "Snapshot id was already published with different immutable metadata"
1777
+ );
1778
+ }
1779
+ return Object.freeze({
1780
+ kind: "result",
1781
+ value: Object.freeze({ snapshot: existing, replayed: true })
1782
+ });
1783
+ }
1784
+ const checkpoint = await unitOfWork.checkpoint(candidate.coveredHeadSequence);
1785
+ if (!checkpoint || checkpoint.modality !== candidate.modality || checkpoint.stateHash !== candidate.stateHash || candidate.modality === "spreadsheet" && (checkpoint.modality !== "spreadsheet" || !causalFrontiersEqual(
1786
+ checkpoint.causalFrontier,
1787
+ candidate.coveredCausalFrontier
1788
+ )) || candidate.modality !== "spreadsheet" && (checkpoint.modality === "spreadsheet" || checkpoint.nativeRevision !== candidate.nativeRevision)) {
1789
+ throw new EditableArtifactSnapshotConflictError(
1790
+ "Snapshot coverage does not match an authoritative transaction boundary"
1791
+ );
1792
+ }
1793
+ if (artifact.currentSnapshotId) {
1794
+ const current = await unitOfWork.findSnapshot(artifact.currentSnapshotId);
1795
+ if (!current) {
1796
+ throw new EditableArtifactSnapshotConflictError(
1797
+ "Current snapshot metadata is missing"
1798
+ );
1799
+ }
1800
+ if (candidate.coveredHeadSequence <= current.coveredHeadSequence) {
1801
+ if (input.requireCurrentHead && current.coveredHeadSequence === artifact.headSequence) {
1802
+ return Object.freeze({
1803
+ kind: "result",
1804
+ value: Object.freeze({ snapshot: current, replayed: true })
1805
+ });
1806
+ }
1807
+ throw new EditableArtifactSnapshotConflictError(
1808
+ "Snapshot publication must advance coverage"
1809
+ );
1810
+ }
1811
+ }
1812
+ const publishedAt = canonicalNow(this.dependencies.clock);
1813
+ const snapshot = Object.freeze({
1814
+ ...candidate,
1815
+ scope: artifact.scope,
1816
+ artifactId: artifact.id,
1817
+ publishedAt
1818
+ });
1819
+ const outbox = snapshotOutboxRecord(
1820
+ editableArtifactOutboxId(this.dependencies.ids.next("outbox")),
1821
+ snapshot
1822
+ );
1823
+ const publication = await unitOfWork.commitSnapshot({
1824
+ expectedCurrentSnapshotId: artifact.currentSnapshotId,
1825
+ expectedAuthorizationRevision: authorizationRevision,
1826
+ authorizationActor: context.actor,
1827
+ authorizationPermission,
1828
+ snapshot,
1829
+ outbox
1830
+ });
1831
+ if (publication.kind === "authorization_stale") {
1832
+ return Object.freeze({ kind: "authorization_stale" });
1833
+ }
1834
+ return Object.freeze({
1835
+ kind: "result",
1836
+ value: Object.freeze({ snapshot, replayed: false })
1837
+ });
1838
+ }
1839
+ );
1840
+ if (outcome.kind === "result") return outcome.value;
1841
+ authorizationRevision = await this.requirePermission({
1842
+ ...context,
1843
+ permission: authorizationPermission
1844
+ });
1845
+ }
1846
+ throw new EditableArtifactRetryableConflictError();
1847
+ }
1848
+ async requirePermission(input) {
1849
+ const decision = await this.dependencies.authorization.authorize({
1850
+ scope: input.scope,
1851
+ artifactId: input.artifactId,
1852
+ actor: input.actor,
1853
+ permission: input.permission
1854
+ });
1855
+ if (typeof decision.allowed !== "boolean") {
1856
+ throw new TypeError("Artifact authorization decision is malformed");
1857
+ }
1858
+ assertPositiveSafeInteger(decision.revision, "artifact authorization revision");
1859
+ if (!decision.allowed) {
1860
+ throw new EditableArtifactForbiddenError(input.permission);
1861
+ }
1862
+ return decision.revision;
1863
+ }
1864
+ };
1865
+ function creationInFlightKey(scope, operationKind, actorKey, idempotencyKey) {
1866
+ return JSON.stringify([
1867
+ scope.accountId,
1868
+ scope.workspaceId,
1869
+ operationKind,
1870
+ actorKey,
1871
+ idempotencyKey
1872
+ ]);
1873
+ }
1874
+ function transactionInFlightKey(scope, artifactId, actorKey, clientTransactionId) {
1875
+ return JSON.stringify([
1876
+ scope.accountId,
1877
+ scope.workspaceId,
1878
+ artifactId,
1879
+ actorKey,
1880
+ clientTransactionId
1881
+ ]);
1882
+ }
1883
+ function compactionInFlightKey(scope, artifactId, actor, permission) {
1884
+ return JSON.stringify([
1885
+ scope.accountId,
1886
+ scope.workspaceId,
1887
+ artifactId,
1888
+ editableArtifactActorKey(actor),
1889
+ permission
1890
+ ]);
1891
+ }
1892
+ function shouldCompactKernelState(state) {
1893
+ return state.tailTransactionCount >= EDITABLE_ARTIFACT_COMPACTION_TRANSACTION_THRESHOLD || state.tailByteSize >= EDITABLE_ARTIFACT_COMPACTION_BYTE_THRESHOLD;
1894
+ }
1895
+ var EditableArtifactCompactionRaceError = class extends EditableArtifactSnapshotConflictError {
1896
+ };
1897
+ function normalizeCreateRequest(input) {
1898
+ const record = plainDataRecord(input, "artifact create request");
1899
+ rejectUnknownKeys(record, ["idempotencyKey", "modality", "title"], "artifact create request");
1900
+ const rawIdempotencyKey = dataProperty(record, "idempotencyKey", true);
1901
+ const modality = dataProperty(record, "modality", true);
1902
+ const title = dataProperty(record, "title", true);
1903
+ if (typeof rawIdempotencyKey !== "string" || typeof title !== "string" || modality !== "spreadsheet" && modality !== "presentation" && modality !== "document") {
1904
+ throw new TypeError("Artifact create request fields are malformed");
1905
+ }
1906
+ assertBoundedArtifactTitle(title);
1907
+ return Object.freeze({
1908
+ idempotencyKey: editableArtifactClientTransactionId(rawIdempotencyKey),
1909
+ modality,
1910
+ title
1911
+ });
1912
+ }
1913
+ function normalizeImportRequest(input) {
1914
+ const record = plainDataRecord(input, "artifact import request");
1915
+ rejectUnknownKeys(
1916
+ record,
1917
+ ["idempotencyKey", "modality", "title", "originalImport", "snapshot"],
1918
+ "artifact import request"
1919
+ );
1920
+ const rawIdempotencyKey = dataProperty(record, "idempotencyKey", true);
1921
+ const modality = dataProperty(record, "modality", true);
1922
+ const title = dataProperty(record, "title", true);
1923
+ const rawOriginalImport = dataProperty(record, "originalImport", true);
1924
+ const rawSnapshot = dataProperty(record, "snapshot", true);
1925
+ if (typeof rawIdempotencyKey !== "string" || typeof title !== "string" || modality !== "spreadsheet" && modality !== "presentation" && modality !== "document") {
1926
+ throw new TypeError("Artifact import request fields are malformed");
1927
+ }
1928
+ assertBoundedArtifactTitle(title);
1929
+ const originalImport = normalizeOriginalImport(rawOriginalImport, modality);
1930
+ const snapshotRecord = plainDataRecord(rawSnapshot, "artifact import snapshot");
1931
+ if (Reflect.ownKeys(snapshotRecord).includes("snapshotId") || Reflect.ownKeys(snapshotRecord).includes("verifiedAt")) {
1932
+ throw new TypeError("Artifact import snapshot identity and timestamps are server-assigned");
1933
+ }
1934
+ const candidate = Object.fromEntries(
1935
+ Reflect.ownKeys(snapshotRecord).map((key) => {
1936
+ if (typeof key !== "string") throw new TypeError("Artifact import snapshot has symbols");
1937
+ return [key, dataProperty(snapshotRecord, key, true)];
1938
+ })
1939
+ );
1940
+ const normalized = validateSnapshotRequest({
1941
+ ...candidate,
1942
+ snapshotId: "00000000000000000000000000000001",
1943
+ verifiedAt: "1970-01-01T00:00:00.000Z"
1944
+ });
1945
+ if (normalized.modality !== modality || normalized.coveredHeadSequence !== 0) {
1946
+ throw new TypeError("Artifact import snapshot boundary is malformed");
1947
+ }
1948
+ const {
1949
+ snapshotId: _serverAssignedSnapshotId,
1950
+ verifiedAt: _serverAssignedVerifiedAt,
1951
+ ...snapshot
1952
+ } = normalized;
1953
+ return Object.freeze({
1954
+ idempotencyKey: editableArtifactClientTransactionId(rawIdempotencyKey),
1955
+ modality,
1956
+ title,
1957
+ originalImport,
1958
+ snapshot: Object.freeze(snapshot)
1959
+ });
1960
+ }
1961
+ function normalizeOriginalImport(input, modality) {
1962
+ const record = plainDataRecord(input, "artifact original import");
1963
+ rejectUnknownKeys(
1964
+ record,
1965
+ ["fileId", "blobReference", "byteSize", "contentHash", "mimeType"],
1966
+ "artifact original import"
1967
+ );
1968
+ const fileId = dataProperty(record, "fileId", true);
1969
+ const blobReference = dataProperty(record, "blobReference", true);
1970
+ const byteSize = dataProperty(record, "byteSize", true);
1971
+ const contentHash = dataProperty(record, "contentHash", true);
1972
+ const mimeType = dataProperty(record, "mimeType", true);
1973
+ const expectedMimeType = modality === "spreadsheet" ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : modality === "document" ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/vnd.openxmlformats-officedocument.presentationml.presentation";
1974
+ if (typeof fileId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u.test(fileId) || typeof blobReference !== "string" || typeof byteSize !== "number" || typeof contentHash !== "string" || mimeType !== expectedMimeType) {
1975
+ throw new TypeError("Artifact original import fields are malformed");
1976
+ }
1977
+ assertBoundedOpaqueReference(blobReference, "original import blob reference");
1978
+ assertPositiveSafeInteger(byteSize, "original import byte size");
1979
+ if (byteSize > EDITABLE_ARTIFACT_ORIGINAL_IMPORT_MAX_BYTES) {
1980
+ throw new TypeError("Artifact original import exceeds its byte limit");
1981
+ }
1982
+ return Object.freeze({
1983
+ fileId,
1984
+ blobReference,
1985
+ byteSize,
1986
+ contentHash: editableArtifactContentHash(contentHash),
1987
+ mimeType: expectedMimeType
1988
+ });
1989
+ }
1990
+ function copyReceipt(receipt) {
1991
+ if (receipt.modality !== "spreadsheet") {
1992
+ return Object.freeze({
1993
+ ...receipt,
1994
+ scope: Object.freeze({ ...receipt.scope }),
1995
+ intentBytes: receipt.intentBytes.slice()
1996
+ });
1997
+ }
1998
+ return Object.freeze({
1999
+ ...receipt,
2000
+ scope: Object.freeze({ ...receipt.scope }),
2001
+ intentBytes: receipt.intentBytes.slice(),
2002
+ causalBase: editableArtifactCausalFrontier(receipt.causalBase),
2003
+ resolvedCausalBase: editableArtifactCausalFrontier(receipt.resolvedCausalBase),
2004
+ resultingCausalFrontier: editableArtifactCausalFrontier(receipt.resultingCausalFrontier),
2005
+ selectiveUndoOperationIds: Object.freeze([...receipt.selectiveUndoOperationIds])
2006
+ });
2007
+ }
2008
+ function copyCreateResult(result, replayed = result.replayed) {
2009
+ const artifact = result.artifact.modality === "spreadsheet" ? Object.freeze({
2010
+ ...result.artifact,
2011
+ scope: Object.freeze({ ...result.artifact.scope }),
2012
+ causalFrontier: editableArtifactCausalFrontier(result.artifact.causalFrontier)
2013
+ }) : Object.freeze({
2014
+ ...result.artifact,
2015
+ scope: Object.freeze({ ...result.artifact.scope })
2016
+ });
2017
+ const genesisSnapshot = result.genesisSnapshot.modality === "spreadsheet" ? Object.freeze({
2018
+ ...result.genesisSnapshot,
2019
+ scope: Object.freeze({ ...result.genesisSnapshot.scope }),
2020
+ coveredCausalFrontier: editableArtifactCausalFrontier(
2021
+ result.genesisSnapshot.coveredCausalFrontier
2022
+ )
2023
+ }) : Object.freeze({
2024
+ ...result.genesisSnapshot,
2025
+ scope: Object.freeze({ ...result.genesisSnapshot.scope })
2026
+ });
2027
+ return Object.freeze({
2028
+ artifact,
2029
+ genesisSnapshot,
2030
+ creationReceipt: Object.freeze({
2031
+ ...result.creationReceipt,
2032
+ scope: Object.freeze({ ...result.creationReceipt.scope })
2033
+ }),
2034
+ replayed
2035
+ });
2036
+ }
2037
+ function normalizeBoundaryContext(input) {
2038
+ const scope = editableArtifactScope(input.scope);
2039
+ const artifactId = editableArtifactId(input.artifactId);
2040
+ validateEditableArtifactActor(input.actor);
2041
+ const actor = Object.freeze({ ...input.actor });
2042
+ return Object.freeze({ scope, artifactId, actor });
2043
+ }
2044
+ function normalizeTransactionEnvelope(request) {
2045
+ const record = plainDataRecord(request, "mutation envelope");
2046
+ rejectUnknownKeys(record, ["intentBytes", "requestHash", "expectedHead"], "mutation envelope");
2047
+ const rawIntentBytes = dataProperty(record, "intentBytes", true);
2048
+ const rawRequestHash = dataProperty(record, "requestHash", true);
2049
+ const rawExpectedHead = dataProperty(record, "expectedHead", false);
2050
+ if (!(rawIntentBytes instanceof Uint8Array)) {
2051
+ throw new TypeError("Mutation intent bytes must be a Uint8Array");
2052
+ }
2053
+ if (rawIntentBytes.byteLength < 1 || rawIntentBytes.byteLength > EDITABLE_ARTIFACT_MAX_INTENT_BYTES_PER_TRANSACTION) {
2054
+ throw new TypeError("Mutation intent bytes exceed the bounded envelope size");
2055
+ }
2056
+ if (typeof rawRequestHash !== "string") {
2057
+ throw new TypeError("Mutation request hash must be a string");
2058
+ }
2059
+ let expectedHead;
2060
+ if (rawExpectedHead !== void 0) {
2061
+ const expected = plainDataRecord(rawExpectedHead, "mutation expected head");
2062
+ rejectUnknownKeys(expected, ["sequence", "stateHash"], "mutation expected head");
2063
+ const sequence = dataProperty(expected, "sequence", true);
2064
+ const stateHash = dataProperty(expected, "stateHash", true);
2065
+ if (typeof sequence !== "number" || typeof stateHash !== "string") {
2066
+ throw new TypeError("Mutation expected head fields are malformed");
2067
+ }
2068
+ assertNonnegativeSafeInteger(sequence, "mutation expected head sequence");
2069
+ expectedHead = Object.freeze({
2070
+ sequence,
2071
+ stateHash: editableArtifactStateHash(stateHash)
2072
+ });
2073
+ }
2074
+ return Object.freeze({
2075
+ intentBytes: rawIntentBytes.slice(),
2076
+ requestHash: editableArtifactRequestHash(rawRequestHash),
2077
+ ...expectedHead ? { expectedHead } : {}
2078
+ });
2079
+ }
2080
+ function normalizeMutationIntent(input) {
2081
+ const record = plainDataRecord(input, "decoded mutation intent");
2082
+ rejectUnknownKeys(
2083
+ record,
2084
+ [
2085
+ "envelopeVersion",
2086
+ "protocolVersion",
2087
+ "modelSchemaVersion",
2088
+ "commandProtocolVersion",
2089
+ "artifactId",
2090
+ "clientTransactionId",
2091
+ "replicaId",
2092
+ "replicaCounter",
2093
+ "previousLocalTransactionId",
2094
+ "observedHeadSequence",
2095
+ "causalBase",
2096
+ "selectiveUndoOperationIds",
2097
+ "commandBytes"
2098
+ ],
2099
+ "decoded mutation intent"
2100
+ );
2101
+ const envelopeVersion = dataProperty(record, "envelopeVersion", true);
2102
+ const protocolVersion = dataProperty(record, "protocolVersion", true);
2103
+ const modelSchemaVersion = dataProperty(record, "modelSchemaVersion", true);
2104
+ const commandProtocolVersion = dataProperty(record, "commandProtocolVersion", true);
2105
+ const rawArtifactId = dataProperty(record, "artifactId", true);
2106
+ const rawClientTransactionId = dataProperty(record, "clientTransactionId", true);
2107
+ const rawReplicaId = dataProperty(record, "replicaId", true);
2108
+ const replicaCounter = dataProperty(record, "replicaCounter", true);
2109
+ const rawPreviousLocalTransactionId = dataProperty(record, "previousLocalTransactionId", true);
2110
+ const observedHeadSequence = dataProperty(record, "observedHeadSequence", true);
2111
+ const rawCausalBase = dataProperty(record, "causalBase", true);
2112
+ const rawUndoTargets = dataProperty(record, "selectiveUndoOperationIds", true);
2113
+ const rawCommandBytes = dataProperty(record, "commandBytes", true);
2114
+ if (typeof envelopeVersion !== "number" || typeof protocolVersion !== "number" || typeof modelSchemaVersion !== "number" || typeof commandProtocolVersion !== "number" || typeof rawArtifactId !== "string" || typeof rawClientTransactionId !== "string" || typeof rawReplicaId !== "string" || typeof replicaCounter !== "number" || rawPreviousLocalTransactionId !== null && typeof rawPreviousLocalTransactionId !== "string" || typeof observedHeadSequence !== "number" || !Array.isArray(rawCausalBase) || !Array.isArray(rawUndoTargets) || !(rawCommandBytes instanceof Uint8Array)) {
2115
+ throw new TypeError("Decoded mutation intent fields are malformed");
2116
+ }
2117
+ if (envelopeVersion !== EDITABLE_ARTIFACT_INTENT_VERSION) {
2118
+ throw new TypeError("Mutation intent envelope version is unsupported");
2119
+ }
2120
+ assertPositiveSafeInteger(protocolVersion, "intent protocol version");
2121
+ if (protocolVersion !== EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION) {
2122
+ throw new TypeError("Mutation intent protocol version is unsupported");
2123
+ }
2124
+ assertPositiveSafeInteger(modelSchemaVersion, "intent model schema version");
2125
+ assertPositiveSafeInteger(commandProtocolVersion, "intent command protocol version");
2126
+ const artifactId = editableArtifactId(rawArtifactId);
2127
+ const clientTransactionId = editableArtifactClientTransactionId(rawClientTransactionId);
2128
+ const replicaId = editableArtifactReplicaId(rawReplicaId);
2129
+ assertPositiveSafeInteger(replicaCounter, "replica counter");
2130
+ const previousLocalTransactionId = rawPreviousLocalTransactionId === null ? null : editableArtifactClientTransactionId(rawPreviousLocalTransactionId);
2131
+ if (previousLocalTransactionId === clientTransactionId) {
2132
+ throw new TypeError("A transaction cannot name itself as its local predecessor");
2133
+ }
2134
+ assertNonnegativeSafeInteger(observedHeadSequence, "observed head sequence");
2135
+ const causalBase = editableArtifactCausalFrontier(
2136
+ rawCausalBase
2137
+ );
2138
+ if (rawUndoTargets.length > EDITABLE_ARTIFACT_MAX_UNDO_TARGETS_PER_TRANSACTION) {
2139
+ throw new TypeError("Editable artifact transaction has too many undo targets");
2140
+ }
2141
+ const selectiveUndoOperationIds = rawUndoTargets.map((target) => {
2142
+ if (typeof target !== "string") {
2143
+ throw new TypeError("Selective undo target must be a string");
2144
+ }
2145
+ return editableArtifactOperationId(target);
2146
+ });
2147
+ if (new Set(selectiveUndoOperationIds).size !== selectiveUndoOperationIds.length) {
2148
+ throw new TypeError("Selective undo targets must be unique");
2149
+ }
2150
+ const sortedUndoTargets = [...selectiveUndoOperationIds].sort(compareCodeUnits);
2151
+ if (sortedUndoTargets.some((target, index) => target !== selectiveUndoOperationIds[index])) {
2152
+ throw new TypeError("Selective undo targets must be canonically sorted");
2153
+ }
2154
+ if (rawCommandBytes.byteLength < 1 || rawCommandBytes.byteLength > EDITABLE_ARTIFACT_MAX_COMMAND_BYTES_PER_TRANSACTION) {
2155
+ throw new TypeError("Mutation command bytes exceed the transaction limit");
2156
+ }
2157
+ return Object.freeze({
2158
+ envelopeVersion,
2159
+ protocolVersion,
2160
+ modelSchemaVersion,
2161
+ commandProtocolVersion,
2162
+ artifactId,
2163
+ clientTransactionId,
2164
+ replicaId,
2165
+ replicaCounter,
2166
+ previousLocalTransactionId,
2167
+ observedHeadSequence,
2168
+ causalBase,
2169
+ selectiveUndoOperationIds: Object.freeze(selectiveUndoOperationIds),
2170
+ commandBytes: rawCommandBytes.slice()
2171
+ });
2172
+ }
2173
+ function plainDataRecord(value, label) {
2174
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2175
+ throw new TypeError(`${label} must be a plain object`);
2176
+ }
2177
+ const prototype = Object.getPrototypeOf(value);
2178
+ if (prototype !== Object.prototype && prototype !== null) {
2179
+ throw new TypeError(`${label} must be a plain object`);
2180
+ }
2181
+ if (Object.getOwnPropertySymbols(value).length > 0) {
2182
+ throw new TypeError(`${label} must not contain symbol properties`);
2183
+ }
2184
+ return value;
2185
+ }
2186
+ function rejectUnknownKeys(record, allowed, label) {
2187
+ const allowedSet = new Set(allowed);
2188
+ const unknown = Object.keys(record).find((key) => !allowedSet.has(key));
2189
+ if (unknown) throw new TypeError(`${label} contains unknown property: ${unknown}`);
2190
+ }
2191
+ function dataProperty(record, key, required) {
2192
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
2193
+ if (!descriptor) {
2194
+ if (required) throw new TypeError(`Missing required property: ${key}`);
2195
+ return void 0;
2196
+ }
2197
+ if (!("value" in descriptor)) throw new TypeError(`Accessor property is not allowed: ${key}`);
2198
+ if (descriptor.value === void 0) {
2199
+ throw new TypeError(`Undefined property is not allowed: ${key}`);
2200
+ }
2201
+ return descriptor.value;
2202
+ }
2203
+ function validateIntentCommands(artifact, intent) {
2204
+ try {
2205
+ const descriptor = editableArtifactCodecFor({
2206
+ durableModality: artifact.modality,
2207
+ modelSchemaVersion: intent.modelSchemaVersion,
2208
+ commandProtocolVersion: intent.commandProtocolVersion
2209
+ });
2210
+ descriptor.command.assertCanonical(intent.commandBytes);
2211
+ const decoded = descriptor.command.decode(intent.commandBytes);
2212
+ if (artifact.modality !== "spreadsheet" && commandCount(decoded) < 1) {
2213
+ throw new TypeError("serialized command batch must not be empty");
2214
+ }
2215
+ } catch (error) {
2216
+ throw new EditableArtifactInvalidRequestError(
2217
+ `Mutation commands are incompatible with the durable ${artifact.modality} artifact: ${error instanceof Error ? error.message : "invalid command envelope"}`
2218
+ );
2219
+ }
2220
+ }
2221
+ function commandCount(value) {
2222
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
2223
+ throw new TypeError("command decoder returned an invalid batch");
2224
+ }
2225
+ const descriptor = Object.getOwnPropertyDescriptor(value, "commands");
2226
+ if (!descriptor || !("value" in descriptor) || !Array.isArray(descriptor.value)) {
2227
+ throw new TypeError("command decoder returned an invalid batch");
2228
+ }
2229
+ return descriptor.value.length;
2230
+ }
2231
+ function validateSerializedPredecessor(predecessor, replicaId, intent, modality) {
2232
+ if (intent.previousLocalTransactionId === null) {
2233
+ if (intent.replicaCounter !== 1) {
2234
+ throw new EditableArtifactCausalChainError(
2235
+ "Only a replica's first transaction may omit its local predecessor"
2236
+ );
2237
+ }
2238
+ return;
2239
+ }
2240
+ if (!predecessor) {
2241
+ throw new EditableArtifactCausalChainError("Previous local transaction is not committed yet");
2242
+ }
2243
+ if (predecessor.modality !== modality || predecessor.replicaId !== replicaId || predecessor.replicaCounter + 1 !== intent.replicaCounter) {
2244
+ throw new EditableArtifactCausalChainError(
2245
+ "Previous local transaction does not match this serialized replica chain"
2246
+ );
2247
+ }
2248
+ }
2249
+ function serializedBasisNativeRevision(state, artifact) {
2250
+ if (state.modality !== artifact.modality || state.artifact.id !== artifact.id || state.artifact.stateHash !== artifact.stateHash || state.artifact.headSequence !== artifact.headSequence) {
2251
+ throw new Error("Serialized kernel basis disagrees with its artifact head");
2252
+ }
2253
+ let headSequence = 0;
2254
+ assertNonnegativeSafeInteger(state.baseNativeRevision, "serialized kernel base native revision");
2255
+ let nativeRevision = state.baseNativeRevision;
2256
+ let stateHash;
2257
+ if (state.snapshot) {
2258
+ if (state.snapshot.modality !== artifact.modality || state.snapshot.artifactId !== artifact.id) {
2259
+ throw new Error("Serialized snapshot basis has the wrong modality or identity");
2260
+ }
2261
+ if (state.snapshot.nativeRevision !== state.baseNativeRevision) {
2262
+ throw new Error("Serialized snapshot disagrees with its native revision checkpoint");
2263
+ }
2264
+ headSequence = state.snapshot.coveredHeadSequence;
2265
+ stateHash = state.snapshot.stateHash;
2266
+ } else {
2267
+ stateHash = state.committedTransactionTail[0]?.priorStateHash ?? artifact.stateHash;
2268
+ }
2269
+ for (const committed of state.committedTransactionTail) {
2270
+ if (committed.modality !== artifact.modality || committed.sequenceStart !== headSequence + 1 || committed.sequenceEnd !== committed.sequenceStart || committed.priorNativeRevision !== nativeRevision || committed.priorStateHash !== stateHash) {
2271
+ throw new Error("Serialized transaction tail is not contiguous with its snapshot");
2272
+ }
2273
+ headSequence = committed.sequenceEnd;
2274
+ nativeRevision = committed.nativeRevision;
2275
+ stateHash = committed.stateHash;
2276
+ }
2277
+ if (headSequence !== artifact.headSequence || stateHash !== artifact.stateHash) {
2278
+ throw new Error("Serialized kernel basis does not reconstruct its artifact head");
2279
+ }
2280
+ return nativeRevision;
2281
+ }
2282
+ function validateSerializedKernelResult(input) {
2283
+ const { artifact, intent, result } = input;
2284
+ if (result.modality !== artifact.modality) {
2285
+ throw new EditableArtifactKernelContractError(
2286
+ "Kernel result modality does not match the durable artifact"
2287
+ );
2288
+ }
2289
+ if (!(result.nativeReceiptBytes instanceof Uint8Array)) {
2290
+ throw new EditableArtifactKernelContractError(
2291
+ "Kernel returned an invalid native serialized receipt"
2292
+ );
2293
+ }
2294
+ assertPositiveSafeInteger(result.modelSchemaVersion, "kernel model schema version");
2295
+ if (result.modelSchemaVersion !== intent.modelSchemaVersion) {
2296
+ throw new EditableArtifactKernelContractError(
2297
+ "Kernel result model schema does not match the canonical intent"
2298
+ );
2299
+ }
2300
+ try {
2301
+ assertBoundedKernelVersion(result.kernelVersion);
2302
+ } catch {
2303
+ throw new EditableArtifactKernelContractError(
2304
+ `Kernel version must contain 1-${EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES2} well-formed UTF-8 bytes`
2305
+ );
2306
+ }
2307
+ const stateHash = editableArtifactStateHash(result.resultingStateHash);
2308
+ const committedTransactionBytes = encodeEditableArtifactSerializedCommit({
2309
+ modality: artifact.modality,
2310
+ transactionId: input.serverTransactionId,
2311
+ parentHeadSequence: artifact.headSequence,
2312
+ resultHeadSequence: artifact.headSequence + 1,
2313
+ priorNativeRevision: input.priorNativeRevision,
2314
+ priorStateHash: artifact.stateHash,
2315
+ stateHash,
2316
+ intentBytes: input.intentBytes,
2317
+ nativeReceiptBytes: result.nativeReceiptBytes
2318
+ });
2319
+ const summary = decodeEditableArtifactSerializedCommit2(
2320
+ committedTransactionBytes,
2321
+ artifact.modality
2322
+ );
2323
+ if (summary.requestHash !== input.requestHash) {
2324
+ throw new EditableArtifactKernelContractError(
2325
+ "Serialized commit does not bind the exact verified request hash"
2326
+ );
2327
+ }
2328
+ return Object.freeze({
2329
+ committedTransactionBytes,
2330
+ nativeReceiptBytes: summary.nativeReceiptBytes.slice(),
2331
+ nativeRevision: summary.nativeReceipt.revision,
2332
+ commandCount: summary.nativeReceipt.commandCount,
2333
+ stateHash,
2334
+ kernelVersion: result.kernelVersion,
2335
+ modelSchemaVersion: result.modelSchemaVersion
2336
+ });
2337
+ }
2338
+ function validateKernelResult2(artifact, actor, intent, resolvedCausalBase, result) {
2339
+ if (result.modality !== "spreadsheet") {
2340
+ throw new EditableArtifactKernelContractError(
2341
+ "Kernel result modality does not match the durable spreadsheet"
2342
+ );
2343
+ }
2344
+ if (!(result.committedTransactionBytes instanceof Uint8Array) || result.committedTransactionBytes.byteLength < 1 || result.committedTransactionBytes.byteLength > EDITABLE_ARTIFACT_MAX_COMMITTED_TRANSACTION_BYTES) {
2345
+ throw new EditableArtifactKernelContractError(
2346
+ "Authoritative kernel returned an invalid canonical OGACO envelope"
2347
+ );
2348
+ }
2349
+ const summary = decodeCommittedTransactionSummary(result.committedTransactionBytes);
2350
+ if (summary.operationIds.length < 1 || summary.operationIds.length > EDITABLE_ARTIFACT_MAX_OPERATIONS_PER_TRANSACTION) {
2351
+ throw new EditableArtifactKernelContractError(
2352
+ "Authoritative kernel returned an invalid operation count"
2353
+ );
2354
+ }
2355
+ if (summary.operationProtocolVersion !== EDITABLE_ARTIFACT_OPERATION_PROTOCOL_VERSION) {
2356
+ throw new EditableArtifactKernelContractError(
2357
+ "Authoritative kernel returned an unsupported operation protocol version"
2358
+ );
2359
+ }
2360
+ assertPositiveSafeInteger(result.modelSchemaVersion, "kernel model schema version");
2361
+ if (result.modelSchemaVersion !== intent.modelSchemaVersion) {
2362
+ throw new EditableArtifactKernelContractError(
2363
+ "Kernel result model schema does not match the canonical intent"
2364
+ );
2365
+ }
2366
+ try {
2367
+ assertBoundedKernelVersion(result.kernelVersion);
2368
+ } catch {
2369
+ throw new EditableArtifactKernelContractError(
2370
+ `Kernel version must contain 1-${EDITABLE_ARTIFACT_KERNEL_VERSION_MAX_BYTES2} well-formed UTF-8 bytes`
2371
+ );
2372
+ }
2373
+ const serverTransactionId = editableArtifactTransactionId(summary.transactionId);
2374
+ const priorStateHash = editableArtifactStateHash(summary.priorStateHash);
2375
+ if (priorStateHash !== artifact.stateHash) {
2376
+ throw new EditableArtifactKernelContractError(
2377
+ "Kernel committed transaction prior state does not match its authoritative basis"
2378
+ );
2379
+ }
2380
+ const stateHash = editableArtifactStateHash(summary.stateHash);
2381
+ const normalizedResolvedBase = editableArtifactCausalFrontier(
2382
+ summary.resolvedCausalBase.map((entry) => ({
2383
+ replicaId: editableArtifactReplicaId(entry.replicaId),
2384
+ counter: entry.counter
2385
+ }))
2386
+ );
2387
+ if (!causalFrontiersEqual(normalizedResolvedBase, resolvedCausalBase)) {
2388
+ throw new EditableArtifactKernelContractError(
2389
+ "Kernel committed transaction resolved base does not match server authority"
2390
+ );
2391
+ }
2392
+ const resultingCausalFrontier = editableArtifactCausalFrontier(
2393
+ summary.resultingCausalFrontier.map((entry) => ({
2394
+ replicaId: editableArtifactReplicaId(entry.replicaId),
2395
+ counter: entry.counter
2396
+ }))
2397
+ );
2398
+ const dotFrontier = editableArtifactCausalFrontier([
2399
+ {
2400
+ replicaId: editableArtifactReplicaId(summary.dot.replicaId),
2401
+ counter: summary.dot.counter
2402
+ }
2403
+ ]);
2404
+ const dot = dotFrontier[0];
2405
+ if (dot.replicaId !== actor.replicaId) {
2406
+ throw new EditableArtifactKernelContractError(
2407
+ "Kernel transaction dot is not owned by the submitting replica"
2408
+ );
2409
+ }
2410
+ if (dot.counter !== intent.replicaCounter) {
2411
+ throw new EditableArtifactKernelContractError(
2412
+ "Kernel transaction dot does not match the authored replica counter"
2413
+ );
2414
+ }
2415
+ const seenOperationIds = /* @__PURE__ */ new Set();
2416
+ const operationIds = summary.operationIds.map((rawOperationId) => {
2417
+ const operationId = editableArtifactOperationId(rawOperationId);
2418
+ if (seenOperationIds.has(operationId)) {
2419
+ throw new EditableArtifactKernelContractError("Kernel returned duplicate operation ids");
2420
+ }
2421
+ seenOperationIds.add(operationId);
2422
+ return operationId;
2423
+ });
2424
+ const expectedFrontier = mergeCausalFrontiers(
2425
+ artifact.causalFrontier,
2426
+ editableArtifactCausalFrontier([
2427
+ { replicaId: actor.replicaId, counter: intent.replicaCounter }
2428
+ ])
2429
+ );
2430
+ if (!causalFrontiersEqual(resultingCausalFrontier, expectedFrontier)) {
2431
+ throw new EditableArtifactKernelContractError(
2432
+ "Kernel resulting frontier does not exactly match committed operation dots"
2433
+ );
2434
+ }
2435
+ return Object.freeze({
2436
+ committedTransactionBytes: result.committedTransactionBytes.slice(),
2437
+ serverTransactionId,
2438
+ dot,
2439
+ operationIds: Object.freeze(operationIds),
2440
+ priorStateHash,
2441
+ resultingCausalFrontier,
2442
+ stateHash,
2443
+ kernelVersion: result.kernelVersion,
2444
+ modelSchemaVersion: result.modelSchemaVersion,
2445
+ operationProtocolVersion: summary.operationProtocolVersion
2446
+ });
2447
+ }
2448
+ function transactionOutboxRecord(outboxId, receipt) {
2449
+ const event = receipt.modality === "spreadsheet" ? Object.freeze({
2450
+ kind: "transaction_committed",
2451
+ schemaVersion: 1,
2452
+ scope: receipt.scope,
2453
+ artifactId: receipt.artifactId,
2454
+ modality: receipt.modality,
2455
+ serverTransactionId: receipt.serverTransactionId,
2456
+ sequenceStart: receipt.sequenceStart,
2457
+ sequenceEnd: receipt.sequenceEnd,
2458
+ stateHash: receipt.stateHash,
2459
+ operationProtocolVersion: receipt.operationProtocolVersion,
2460
+ committedAt: receipt.committedAt
2461
+ }) : Object.freeze({
2462
+ kind: "transaction_committed",
2463
+ schemaVersion: 1,
2464
+ scope: receipt.scope,
2465
+ artifactId: receipt.artifactId,
2466
+ modality: receipt.modality,
2467
+ serverTransactionId: receipt.serverTransactionId,
2468
+ sequenceStart: receipt.sequenceStart,
2469
+ sequenceEnd: receipt.sequenceEnd,
2470
+ stateHash: receipt.stateHash,
2471
+ commitProtocolVersion: receipt.commitProtocolVersion,
2472
+ committedAt: receipt.committedAt
2473
+ });
2474
+ return Object.freeze({
2475
+ outboxId,
2476
+ event,
2477
+ state: "pending",
2478
+ attemptCount: 0,
2479
+ leaseOwner: null,
2480
+ leaseExpiresAt: null,
2481
+ nextAttemptAt: receipt.committedAt,
2482
+ lastErrorCode: null,
2483
+ publishedAt: null,
2484
+ deadLetteredAt: null,
2485
+ createdAt: receipt.committedAt
2486
+ });
2487
+ }
2488
+ function validateSnapshotRequest(request) {
2489
+ const record = plainDataRecord(request, "snapshot request");
2490
+ const rawModality = dataProperty(record, "modality", true);
2491
+ if (rawModality !== "spreadsheet" && rawModality !== "document" && rawModality !== "presentation") {
2492
+ throw new TypeError("Snapshot modality is invalid");
2493
+ }
2494
+ rejectUnknownKeys(
2495
+ record,
2496
+ rawModality === "spreadsheet" ? [
2497
+ "modality",
2498
+ "snapshotId",
2499
+ "blobReference",
2500
+ "byteSize",
2501
+ "contentHash",
2502
+ "mimeType",
2503
+ "coveredHeadSequence",
2504
+ "coveredCausalFrontier",
2505
+ "stateHash",
2506
+ "modelSchemaVersion",
2507
+ "operationProtocolVersion",
2508
+ "kernelVersion",
2509
+ "crdtStateVersion",
2510
+ "verifiedAt"
2511
+ ] : [
2512
+ "modality",
2513
+ "snapshotId",
2514
+ "blobReference",
2515
+ "byteSize",
2516
+ "contentHash",
2517
+ "mimeType",
2518
+ "coveredHeadSequence",
2519
+ "stateHash",
2520
+ "modelSchemaVersion",
2521
+ "kernelVersion",
2522
+ "nativeRevision",
2523
+ "verifiedAt"
2524
+ ],
2525
+ "snapshot request"
2526
+ );
2527
+ const rawSnapshotId = dataProperty(record, "snapshotId", true);
2528
+ const rawBlobReference = dataProperty(record, "blobReference", true);
2529
+ const rawByteSize = dataProperty(record, "byteSize", true);
2530
+ const rawContentHash = dataProperty(record, "contentHash", true);
2531
+ const rawMimeType = dataProperty(record, "mimeType", true);
2532
+ const rawCoveredHeadSequence = dataProperty(record, "coveredHeadSequence", true);
2533
+ const rawStateHash = dataProperty(record, "stateHash", true);
2534
+ const rawModelSchemaVersion = dataProperty(record, "modelSchemaVersion", true);
2535
+ const rawKernelVersion = dataProperty(record, "kernelVersion", true);
2536
+ const rawVerifiedAt = dataProperty(record, "verifiedAt", true);
2537
+ if (typeof rawSnapshotId !== "string" || typeof rawBlobReference !== "string" || typeof rawByteSize !== "number" || typeof rawContentHash !== "string" || typeof rawCoveredHeadSequence !== "number" || typeof rawStateHash !== "string" || typeof rawModelSchemaVersion !== "number" || typeof rawKernelVersion !== "string" || typeof rawVerifiedAt !== "string") {
2538
+ throw new TypeError("Snapshot request fields are malformed");
2539
+ }
2540
+ if (rawMimeType !== "application/vnd.opengeni.editable-artifact-snapshot") {
2541
+ throw new TypeError("Snapshot MIME type is unsupported");
2542
+ }
2543
+ const snapshotId = editableArtifactSnapshotId(rawSnapshotId);
2544
+ assertBoundedOpaqueReference(rawBlobReference, "snapshot blob reference");
2545
+ assertPositiveSafeInteger(rawByteSize, "snapshot byte size");
2546
+ const contentHash = editableArtifactContentHash(rawContentHash);
2547
+ assertNonnegativeSafeInteger(rawCoveredHeadSequence, "snapshot covered head sequence");
2548
+ const stateHash = editableArtifactStateHash(rawStateHash);
2549
+ assertPositiveSafeInteger(rawModelSchemaVersion, "snapshot model schema version");
2550
+ assertBoundedKernelVersion(rawKernelVersion);
2551
+ assertIsoTimestamp(rawVerifiedAt, "snapshot verified timestamp");
2552
+ const common = {
2553
+ modality: rawModality,
2554
+ snapshotId,
2555
+ blobReference: rawBlobReference,
2556
+ byteSize: rawByteSize,
2557
+ contentHash,
2558
+ mimeType: "application/vnd.opengeni.editable-artifact-snapshot",
2559
+ coveredHeadSequence: rawCoveredHeadSequence,
2560
+ stateHash,
2561
+ modelSchemaVersion: rawModelSchemaVersion,
2562
+ kernelVersion: rawKernelVersion,
2563
+ verifiedAt: rawVerifiedAt
2564
+ };
2565
+ if (rawModality === "spreadsheet") {
2566
+ const rawCoveredCausalFrontier = dataProperty(record, "coveredCausalFrontier", true);
2567
+ const rawOperationProtocolVersion = dataProperty(record, "operationProtocolVersion", true);
2568
+ const rawCrdtStateVersion = dataProperty(record, "crdtStateVersion", true);
2569
+ if (!Array.isArray(rawCoveredCausalFrontier) || typeof rawOperationProtocolVersion !== "number" || typeof rawCrdtStateVersion !== "number") {
2570
+ throw new TypeError("Spreadsheet snapshot fields are malformed");
2571
+ }
2572
+ assertPositiveSafeInteger(rawOperationProtocolVersion, "snapshot operation protocol version");
2573
+ assertPositiveSafeInteger(rawCrdtStateVersion, "snapshot CRDT state version");
2574
+ return Object.freeze({
2575
+ ...common,
2576
+ modality: "spreadsheet",
2577
+ coveredCausalFrontier: editableArtifactCausalFrontier(
2578
+ rawCoveredCausalFrontier
2579
+ ),
2580
+ operationProtocolVersion: rawOperationProtocolVersion,
2581
+ crdtStateVersion: rawCrdtStateVersion
2582
+ });
2583
+ }
2584
+ const rawNativeRevision = dataProperty(record, "nativeRevision", true);
2585
+ if (typeof rawNativeRevision !== "number") {
2586
+ throw new TypeError("Serialized snapshot native revision is malformed");
2587
+ }
2588
+ assertNonnegativeSafeInteger(rawNativeRevision, "snapshot native revision");
2589
+ return Object.freeze({
2590
+ ...common,
2591
+ modality: rawModality,
2592
+ nativeRevision: rawNativeRevision
2593
+ });
2594
+ }
2595
+ function sameSnapshotRequest(existing, candidate) {
2596
+ if (existing.modality !== candidate.modality || !(existing.snapshotId === candidate.snapshotId && existing.blobReference === candidate.blobReference && existing.byteSize === candidate.byteSize && existing.contentHash === candidate.contentHash && existing.mimeType === candidate.mimeType && existing.coveredHeadSequence === candidate.coveredHeadSequence && existing.stateHash === candidate.stateHash && existing.modelSchemaVersion === candidate.modelSchemaVersion && existing.kernelVersion === candidate.kernelVersion && existing.verifiedAt === candidate.verifiedAt)) {
2597
+ return false;
2598
+ }
2599
+ if (existing.modality === "spreadsheet" && candidate.modality === "spreadsheet") {
2600
+ return causalFrontiersEqual(existing.coveredCausalFrontier, candidate.coveredCausalFrontier) && existing.operationProtocolVersion === candidate.operationProtocolVersion && existing.crdtStateVersion === candidate.crdtStateVersion;
2601
+ }
2602
+ return existing.modality !== "spreadsheet" && candidate.modality !== "spreadsheet" && existing.nativeRevision === candidate.nativeRevision;
2603
+ }
2604
+ function snapshotOutboxRecord(outboxId, snapshot) {
2605
+ const event = snapshot.modality === "spreadsheet" ? Object.freeze({
2606
+ kind: "snapshot_published",
2607
+ schemaVersion: 1,
2608
+ scope: snapshot.scope,
2609
+ artifactId: snapshot.artifactId,
2610
+ modality: snapshot.modality,
2611
+ snapshotId: snapshot.snapshotId,
2612
+ coveredHeadSequence: snapshot.coveredHeadSequence,
2613
+ stateHash: snapshot.stateHash,
2614
+ operationProtocolVersion: snapshot.operationProtocolVersion,
2615
+ publishedAt: snapshot.publishedAt
2616
+ }) : Object.freeze({
2617
+ kind: "snapshot_published",
2618
+ schemaVersion: 1,
2619
+ scope: snapshot.scope,
2620
+ artifactId: snapshot.artifactId,
2621
+ modality: snapshot.modality,
2622
+ snapshotId: snapshot.snapshotId,
2623
+ coveredHeadSequence: snapshot.coveredHeadSequence,
2624
+ stateHash: snapshot.stateHash,
2625
+ publishedAt: snapshot.publishedAt
2626
+ });
2627
+ return Object.freeze({
2628
+ outboxId,
2629
+ event,
2630
+ state: "pending",
2631
+ attemptCount: 0,
2632
+ leaseOwner: null,
2633
+ leaseExpiresAt: null,
2634
+ nextAttemptAt: snapshot.publishedAt,
2635
+ lastErrorCode: null,
2636
+ publishedAt: null,
2637
+ deadLetteredAt: null,
2638
+ createdAt: snapshot.publishedAt
2639
+ });
2640
+ }
2641
+ function canonicalNow(clock) {
2642
+ const now = clock.now();
2643
+ if (Number.isNaN(now.getTime())) throw new TypeError("Artifact clock returned an invalid date");
2644
+ return now.toISOString();
2645
+ }
2646
+
2647
+ export {
2648
+ editableArtifactId,
2649
+ editableArtifactTransactionId,
2650
+ editableArtifactReceiptId,
2651
+ editableArtifactSnapshotId,
2652
+ editableArtifactOutboxId,
2653
+ editableArtifactOperationId,
2654
+ editableArtifactReplicaId,
2655
+ editableArtifactClientTransactionId,
2656
+ editableArtifactRequestHash,
2657
+ editableArtifactStateHash,
2658
+ editableArtifactContentHash,
2659
+ editableArtifactScope,
2660
+ validateEditableArtifactActor,
2661
+ editableArtifactActorKey,
2662
+ editableArtifactCausalFrontier,
2663
+ causalCounter,
2664
+ causalFrontierDominates,
2665
+ causalFrontiersEqual,
2666
+ mergeCausalFrontiers,
2667
+ EDITABLE_ARTIFACT_ORIGINAL_IMPORT_MAX_BYTES,
2668
+ assertNonnegativeSafeInteger,
2669
+ assertPositiveSafeInteger,
2670
+ assertIsoTimestamp,
2671
+ assertBoundedArtifactTitle,
2672
+ assertBoundedOpaqueReference,
2673
+ assertBoundedKernelVersion,
2674
+ compareCodeUnits,
2675
+ EditableArtifactDomainError,
2676
+ EditableArtifactNotFoundError,
2677
+ EditableArtifactInvalidRequestError,
2678
+ EditableArtifactForbiddenError,
2679
+ EditableArtifactNotEditableError,
2680
+ EditableArtifactIdempotencyConflictError,
2681
+ EditableArtifactRequestHashMismatchError,
2682
+ EditableArtifactCausalFutureError,
2683
+ EditableArtifactCausalChainError,
2684
+ EditableArtifactStaleBaseError,
2685
+ EditableArtifactKernelContractError,
2686
+ EditableArtifactRetryableConflictError,
2687
+ EditableArtifactUndoTargetError,
2688
+ EditableArtifactSnapshotConflictError,
2689
+ EditableArtifactOutboxLeaseConflictError,
2690
+ hashEditableArtifactCreateRequest,
2691
+ hashEditableArtifactImportRequest,
2692
+ OgatxEditableArtifactMutationIntentCodec,
2693
+ ogatxEditableArtifactMutationIntentCodec,
2694
+ MAX_EDITABLE_ARTIFACT_SNAPSHOT_BYTES,
2695
+ MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_BYTES,
2696
+ MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_SEGMENTS,
2697
+ MAX_EDITABLE_ARTIFACT_SNAPSHOT_TAIL_SEGMENT_BYTES,
2698
+ EditableArtifactSnapshotVerificationError,
2699
+ ProductionEditableArtifactSnapshotVerifier,
2700
+ EDITABLE_ARTIFACT_MAX_OPERATIONS_PER_TRANSACTION,
2701
+ EDITABLE_ARTIFACT_MAX_UNDO_TARGETS_PER_TRANSACTION,
2702
+ EDITABLE_ARTIFACT_MAX_COMMAND_BYTES_PER_TRANSACTION,
2703
+ EDITABLE_ARTIFACT_MAX_INTENT_BYTES_PER_TRANSACTION,
2704
+ EDITABLE_ARTIFACT_INTENT_PROTOCOL_VERSION,
2705
+ EDITABLE_ARTIFACT_MAX_OPERATION_BYTES_PER_TRANSACTION,
2706
+ EDITABLE_ARTIFACT_MAX_COMMITTED_TRANSACTION_BYTES,
2707
+ EDITABLE_ARTIFACT_OPERATION_PROTOCOL_VERSION,
2708
+ EDITABLE_ARTIFACT_MAX_COMMIT_ATTEMPTS,
2709
+ EDITABLE_ARTIFACT_COMPACTION_TRANSACTION_THRESHOLD,
2710
+ EDITABLE_ARTIFACT_COMPACTION_BYTE_THRESHOLD,
2711
+ EditableArtifactService
2712
+ };
2713
+ //# sourceMappingURL=chunk-JJU6I5XD.js.map