@dbsp/core 3.3.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1740 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __copyProps = (to, from, except, desc) => {
6
+ if (from && typeof from === "object" || typeof from === "function") {
7
+ for (let key of __getOwnPropNames(from))
8
+ if (!__hasOwnProp.call(to, key) && key !== except)
9
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
10
+ }
11
+ return to;
12
+ };
13
+ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
14
+
15
+ // src/transition/canonical-json.ts
16
+ import { createHash } from "crypto";
17
+ var CanonicalJsonError = class extends TypeError {
18
+ constructor(path, detail, options) {
19
+ super(`canonical JSON refuses ${path}: ${detail}`, options);
20
+ this.name = "CanonicalJsonError";
21
+ }
22
+ };
23
+ function canonicalJson(value) {
24
+ return encodeCanonicalJson(value, "$");
25
+ }
26
+ function canonicalJsonDigest(value) {
27
+ return createHash("sha256").update(canonicalJson(value)).digest("hex");
28
+ }
29
+ var MAX_RENDERED_PATH_KEY = 160;
30
+ function memberPath(path, key) {
31
+ const rendered = key.length <= MAX_RENDERED_PATH_KEY ? key : `${key.slice(0, MAX_RENDERED_PATH_KEY)}\u2026<truncated>`;
32
+ return `${path}[${JSON.stringify(rendered)}]`;
33
+ }
34
+ function scalar(value, path) {
35
+ if (value === null) return "null";
36
+ switch (typeof value) {
37
+ case "string":
38
+ case "boolean":
39
+ return JSON.stringify(value);
40
+ case "number":
41
+ if (!Number.isFinite(value))
42
+ throw new CanonicalJsonError(
43
+ path,
44
+ "numbers must be finite JSON numbers"
45
+ );
46
+ return JSON.stringify(value);
47
+ case "undefined":
48
+ throw new CanonicalJsonError(path, "undefined is not a JSON value");
49
+ case "bigint":
50
+ case "function":
51
+ case "symbol":
52
+ throw new CanonicalJsonError(path, `${typeof value} is not a JSON value`);
53
+ case "object":
54
+ return void 0;
55
+ }
56
+ }
57
+ function captureReflection(path, capture) {
58
+ try {
59
+ return capture();
60
+ } catch (error) {
61
+ throw new CanonicalJsonError(path, "reflection failed", { cause: error });
62
+ }
63
+ }
64
+ function encodeCanonicalJson(value, path) {
65
+ const output = [];
66
+ const active = /* @__PURE__ */ new WeakSet();
67
+ const tasks = [{ kind: "value", value, path }];
68
+ while (tasks.length > 0) {
69
+ const task = tasks.pop();
70
+ if (!task) continue;
71
+ if (task.kind === "text") {
72
+ output.push(task.text);
73
+ continue;
74
+ }
75
+ if (task.kind === "close") {
76
+ active.delete(task.value);
77
+ output.push(task.text);
78
+ continue;
79
+ }
80
+ const encoded = scalar(task.value, task.path);
81
+ if (encoded !== void 0) {
82
+ output.push(encoded);
83
+ continue;
84
+ }
85
+ const object = task.value;
86
+ if (active.has(object))
87
+ throw new CanonicalJsonError(
88
+ task.path,
89
+ "cyclic values are not JSON values"
90
+ );
91
+ active.add(object);
92
+ const isArray = captureReflection(task.path, () => Array.isArray(object));
93
+ if (isArray) {
94
+ if (captureReflection(task.path, () => Object.getPrototypeOf(object)) !== Array.prototype)
95
+ throw new CanonicalJsonError(
96
+ task.path,
97
+ "arrays require Array.prototype"
98
+ );
99
+ if (captureReflection(task.path, () => Object.getOwnPropertySymbols(object)).length > 0)
100
+ throw new CanonicalJsonError(
101
+ task.path,
102
+ "symbol-keyed members are not JSON members"
103
+ );
104
+ for (const key of captureReflection(
105
+ task.path,
106
+ () => Object.getOwnPropertyNames(object)
107
+ )) {
108
+ if (key === "length") continue;
109
+ if (!/^(?:0|[1-9][0-9]*)$/u.test(key))
110
+ throw new CanonicalJsonError(
111
+ task.path,
112
+ "array non-index members are not JSON members"
113
+ );
114
+ if (BigInt(key) > 4294967294n)
115
+ throw new CanonicalJsonError(
116
+ task.path,
117
+ "array non-index members are not JSON members"
118
+ );
119
+ }
120
+ output.push("[");
121
+ tasks.push({ kind: "close", value: object, text: "]" });
122
+ const length = captureReflection(
123
+ task.path,
124
+ () => object.length
125
+ );
126
+ for (let index = length - 1; index >= 0; index -= 1) {
127
+ const itemPath = `${task.path}[${index}]`;
128
+ const descriptor = captureReflection(
129
+ itemPath,
130
+ () => Object.getOwnPropertyDescriptor(object, String(index))
131
+ );
132
+ if (!descriptor)
133
+ throw new CanonicalJsonError(
134
+ itemPath,
135
+ "array holes are not JSON values"
136
+ );
137
+ if (!descriptor.enumerable || !("value" in descriptor))
138
+ throw new CanonicalJsonError(
139
+ itemPath,
140
+ "array members must be enumerable data properties"
141
+ );
142
+ if (index < length - 1) tasks.push({ kind: "text", text: "," });
143
+ tasks.push({ kind: "value", value: descriptor.value, path: itemPath });
144
+ }
145
+ continue;
146
+ }
147
+ const prototype = captureReflection(
148
+ task.path,
149
+ () => Object.getPrototypeOf(object)
150
+ );
151
+ if (prototype !== Object.prototype && prototype !== null)
152
+ throw new CanonicalJsonError(
153
+ task.path,
154
+ "only plain objects are JSON objects"
155
+ );
156
+ if (captureReflection(task.path, () => Object.getOwnPropertySymbols(object)).length > 0)
157
+ throw new CanonicalJsonError(
158
+ task.path,
159
+ "symbol-keyed members are not JSON members"
160
+ );
161
+ const entries = Object.entries(
162
+ captureReflection(
163
+ task.path,
164
+ () => Object.getOwnPropertyDescriptors(object)
165
+ )
166
+ );
167
+ for (const [key, descriptor] of entries) {
168
+ const keyPath = memberPath(task.path, key);
169
+ if (!descriptor.enumerable)
170
+ throw new CanonicalJsonError(
171
+ keyPath,
172
+ "non-enumerable members are not JSON members"
173
+ );
174
+ if (!("value" in descriptor))
175
+ throw new CanonicalJsonError(
176
+ keyPath,
177
+ "accessor members are not JSON values"
178
+ );
179
+ if (descriptor.value === void 0)
180
+ throw new CanonicalJsonError(keyPath, "undefined is not a JSON value");
181
+ }
182
+ entries.sort(
183
+ ([left], [right]) => left < right ? -1 : left > right ? 1 : 0
184
+ );
185
+ output.push("{");
186
+ tasks.push({ kind: "close", value: object, text: "}" });
187
+ for (let index = entries.length - 1; index >= 0; index -= 1) {
188
+ const entry = entries[index];
189
+ if (!entry) continue;
190
+ const [key, descriptor] = entry;
191
+ if (index < entries.length - 1) tasks.push({ kind: "text", text: "," });
192
+ tasks.push({
193
+ kind: "value",
194
+ value: descriptor.value,
195
+ path: memberPath(task.path, key)
196
+ });
197
+ tasks.push({ kind: "text", text: `${JSON.stringify(key)}:` });
198
+ }
199
+ }
200
+ return output.join("");
201
+ }
202
+
203
+ // src/transition/durably-loaded-run.ts
204
+ var durablyLoadedRuns = /* @__PURE__ */ new WeakSet();
205
+ function frozenMetadata(run) {
206
+ return Object.freeze({ ...run });
207
+ }
208
+ function mintDurablyLoadedRun(run) {
209
+ const witness = Object.freeze({
210
+ metadata: frozenMetadata(run)
211
+ });
212
+ durablyLoadedRuns.add(witness);
213
+ return witness;
214
+ }
215
+ function isDurablyLoadedRun(value) {
216
+ return value != null && (typeof value === "object" || typeof value === "function") && durablyLoadedRuns.has(value);
217
+ }
218
+
219
+ // src/transition/transition-lessor.ts
220
+ var TRANSITION_LESSOR_BRAND = /* @__PURE__ */ Symbol.for("dbsp.transition.lessor");
221
+ var TRANSITION_LESSOR_PROTOCOL_VERSION = 2;
222
+ var TRANSITION_LESSOR_REJECTION = "transition target must be a core-minted lessor";
223
+ var TRANSITION_LESSOR_REJECTION_DETAIL = "The supplied transition target was refused because it is not a core-minted protocol-v2 lessor carrying a revocation capability.";
224
+ var TransitionLessorContractRejection = class extends Error {
225
+ constructor(detail) {
226
+ super(detail);
227
+ this.name = "TransitionLessorContractRejection";
228
+ }
229
+ };
230
+ async function captureLease(value) {
231
+ const { query, queryPlanOperation, release } = leaseMembers(value);
232
+ if (typeof query !== "function" || typeof release !== "function") {
233
+ const rejection = new TransitionLessorContractRejection(
234
+ "A transition lessor must acquire a lease exposing query() and release(). This acquisition returned an unusable member pair."
235
+ );
236
+ releaseMalformedAcquisition(value, release, rejection);
237
+ throw rejection;
238
+ }
239
+ return {
240
+ raw: value,
241
+ query,
242
+ queryPlanOperation: typeof queryPlanOperation === "function" ? queryPlanOperation : void 0,
243
+ release
244
+ };
245
+ }
246
+ function transitionLessorRejectionDetail(error) {
247
+ try {
248
+ return error instanceof TransitionLessorContractRejection ? error.message : void 0;
249
+ } catch {
250
+ return void 0;
251
+ }
252
+ }
253
+ var planOperationQueries = /* @__PURE__ */ new WeakMap();
254
+ var transitionSessionRevocations = /* @__PURE__ */ new WeakMap();
255
+ var transitionSessionLatches = /* @__PURE__ */ new WeakMap();
256
+ var physicalSessionRevocationLatches = /* @__PURE__ */ new WeakMap();
257
+ var remintedLessorStates = /* @__PURE__ */ new WeakMap();
258
+ function mintTransitionSessionRevocationLatch() {
259
+ return { compromised: false };
260
+ }
261
+ function resolvePhysicalSessionRevocationLatch(physicalSession) {
262
+ const existing = physicalSessionRevocationLatches.get(physicalSession);
263
+ if (existing) return existing;
264
+ const latch = mintTransitionSessionRevocationLatch();
265
+ physicalSessionRevocationLatches.set(physicalSession, latch);
266
+ return latch;
267
+ }
268
+ function mintTransitionSessionRevocationPropagation() {
269
+ return Object.freeze({
270
+ compromise() {
271
+ if (!isObject(this)) return;
272
+ resolvePhysicalSessionRevocationLatch(this).compromised = true;
273
+ },
274
+ isCompromised() {
275
+ return isObject(this) && resolvePhysicalSessionRevocationLatch(this).compromised;
276
+ }
277
+ });
278
+ }
279
+ function mintTransitionSessionRevocation(latch, physicalSession, propagation) {
280
+ return Object.freeze({
281
+ compromise() {
282
+ latch.compromised = true;
283
+ try {
284
+ propagation.compromise.call(physicalSession);
285
+ } catch {
286
+ }
287
+ },
288
+ isCompromised() {
289
+ if (latch.compromised) return true;
290
+ try {
291
+ return propagation.isCompromised.call(physicalSession) === true;
292
+ } catch {
293
+ return false;
294
+ }
295
+ }
296
+ });
297
+ }
298
+ function revocationForTransitionSession(session) {
299
+ const revocation = transitionSessionRevocations.get(session);
300
+ if (!revocation)
301
+ throw new Error(
302
+ "transition session was not minted by the transition lessor"
303
+ );
304
+ return revocation;
305
+ }
306
+ function latchForTransitionSession(session) {
307
+ const latch = transitionSessionLatches.get(session);
308
+ if (!latch)
309
+ throw new Error(
310
+ "transition session was not minted by the transition lessor"
311
+ );
312
+ return latch;
313
+ }
314
+ function mintTransitionSession(revocation, latch, query) {
315
+ const session = Object.freeze({
316
+ query: (sql, params) => query(session, sql, params)
317
+ });
318
+ transitionSessionRevocations.set(session, revocation);
319
+ transitionSessionLatches.set(session, latch);
320
+ return session;
321
+ }
322
+ function transitionSessionIsCompromised(session) {
323
+ return revocationForTransitionSession(session).isCompromised();
324
+ }
325
+ function transitionPhysicalSessionIsCompromised(physicalSession) {
326
+ return physicalSessionRevocationLatches.get(physicalSession)?.compromised === true;
327
+ }
328
+ function compromisedTransitionSessionError() {
329
+ return new Error("transition execution marked its leased client compromised");
330
+ }
331
+ function markTransitionClientCompromised(session) {
332
+ revocationForTransitionSession(session).compromise();
333
+ }
334
+ function planOperationSession(session) {
335
+ const query = planOperationQueries.get(session);
336
+ if (!query) return session;
337
+ const revocation = revocationForTransitionSession(session);
338
+ const latch = latchForTransitionSession(session);
339
+ return mintTransitionSession(
340
+ revocation,
341
+ latch,
342
+ (planSession, sql, params) => {
343
+ if (transitionSessionIsCompromised(planSession)) {
344
+ return Promise.reject(compromisedTransitionSessionError());
345
+ }
346
+ return query(sql, params);
347
+ }
348
+ );
349
+ }
350
+ var exclusiveTargets = /* @__PURE__ */ new WeakMap();
351
+ function createExclusiveTransitionTarget(target, isLive) {
352
+ if (!isTransitionLessor(target)) {
353
+ throw new Error(
354
+ "exclusive transition target requires a core-minted lessor"
355
+ );
356
+ }
357
+ const exclusive = Object.freeze({});
358
+ exclusiveTargets.set(exclusive, { target, isLive });
359
+ return exclusive;
360
+ }
361
+ function isExclusiveTransitionTarget(value) {
362
+ return value != null && (typeof value === "object" || typeof value === "function") && exclusiveTargets.has(value);
363
+ }
364
+ async function acquireExclusiveTransitionLease(target) {
365
+ const state = exclusiveTargets.get(target);
366
+ if (!state?.isLive()) {
367
+ throw new Error(
368
+ "exclusive transition target is only valid while its adapter lock callback is running"
369
+ );
370
+ }
371
+ const lease = await acquireTransitionLease(state.target);
372
+ if (!state.isLive()) {
373
+ await lease.release({
374
+ error: new Error(
375
+ "exclusive transition target ended while acquiring its lease"
376
+ )
377
+ });
378
+ throw new Error(
379
+ "exclusive transition target ended while acquiring its lease"
380
+ );
381
+ }
382
+ return lease;
383
+ }
384
+ function acquireTransitionTargetLease(target) {
385
+ return isTransitionLessor(target) ? acquireTransitionLease(target) : acquireExclusiveTransitionLease(target);
386
+ }
387
+ async function acquireTransitionLease(target) {
388
+ if (!isTransitionLessor(target)) {
389
+ throw new Error(TRANSITION_LESSOR_REJECTION);
390
+ }
391
+ const captured = await captureLease(await target.acquire());
392
+ const { raw, query, release } = captured;
393
+ const physicalSession = raw;
394
+ const reminted = remintedLessorStates.get(target);
395
+ const latch = reminted?.latch ?? resolvePhysicalSessionRevocationLatch(physicalSession);
396
+ if (reminted) {
397
+ physicalSessionRevocationLatches.set(physicalSession, reminted.latch);
398
+ }
399
+ const revocation = reminted?.revocation ?? mintTransitionSessionRevocation(
400
+ latch,
401
+ physicalSession,
402
+ lessorBrand(target).revocation
403
+ );
404
+ let closed = false;
405
+ const session = mintTransitionSession(
406
+ revocation,
407
+ latch,
408
+ (leasedSession, sql, params) => {
409
+ if (closed) {
410
+ return Promise.reject(
411
+ new Error("transition lease was already given back")
412
+ );
413
+ }
414
+ if (transitionSessionIsCompromised(leasedSession)) {
415
+ return Promise.reject(compromisedTransitionSessionError());
416
+ }
417
+ return query.call(raw, sql, params);
418
+ }
419
+ );
420
+ if (captured.queryPlanOperation) {
421
+ planOperationQueries.set(
422
+ session,
423
+ (sql, params) => Promise.resolve(
424
+ captured.queryPlanOperation?.call(captured.raw, sql, params)
425
+ )
426
+ );
427
+ }
428
+ const releaseLease = (failure) => {
429
+ if (closed) {
430
+ return Promise.resolve();
431
+ }
432
+ closed = true;
433
+ try {
434
+ const effectiveFailure = failure ?? (transitionSessionIsCompromised(session) ? {
435
+ error: new Error(
436
+ "transition execution marked its leased client compromised"
437
+ )
438
+ } : void 0);
439
+ const returned = effectiveFailure ? release.call(raw, releaseArgument(effectiveFailure)) : release.call(raw);
440
+ void Promise.resolve(returned).catch(() => void 0);
441
+ } catch {
442
+ }
443
+ return Promise.resolve();
444
+ };
445
+ return Object.freeze({
446
+ session,
447
+ release: releaseLease
448
+ });
449
+ }
450
+ function leaseMembers(value) {
451
+ if (value == null || typeof value !== "object" && typeof value !== "function") {
452
+ return {
453
+ query: void 0,
454
+ queryPlanOperation: void 0,
455
+ release: void 0
456
+ };
457
+ }
458
+ const release = readMember(value, "release");
459
+ return {
460
+ query: readMember(value, "query"),
461
+ queryPlanOperation: readMember(value, "queryPlanOperation"),
462
+ release
463
+ };
464
+ }
465
+ function readMember(value, member) {
466
+ try {
467
+ return value[member];
468
+ } catch {
469
+ return void 0;
470
+ }
471
+ }
472
+ function releaseMalformedAcquisition(value, release, rejection) {
473
+ if (typeof release !== "function") {
474
+ return;
475
+ }
476
+ try {
477
+ void Promise.resolve(release.call(value, rejection)).catch(() => void 0);
478
+ } catch {
479
+ }
480
+ }
481
+ function createTransitionLessor(acquire) {
482
+ return mintTransitionLessor(acquire);
483
+ }
484
+ function remintTransitionLessorFromSession(session, acquire) {
485
+ return mintTransitionLessor(acquire, {
486
+ latch: latchForTransitionSession(session),
487
+ revocation: revocationForTransitionSession(session)
488
+ });
489
+ }
490
+ function mintTransitionLessor(acquire, reminted) {
491
+ const lessor = { acquire };
492
+ Object.defineProperty(lessor, TRANSITION_LESSOR_BRAND, {
493
+ value: Object.freeze({
494
+ protocolVersion: TRANSITION_LESSOR_PROTOCOL_VERSION,
495
+ revocation: reminted?.revocation ?? mintTransitionSessionRevocationPropagation()
496
+ }),
497
+ writable: false,
498
+ configurable: false
499
+ });
500
+ const frozenLessor = Object.freeze(lessor);
501
+ if (reminted) {
502
+ remintedLessorStates.set(frozenLessor, reminted);
503
+ }
504
+ return frozenLessor;
505
+ }
506
+ function isTransitionLessor(value) {
507
+ try {
508
+ if (value == null || typeof value !== "object" && typeof value !== "function" || Array.isArray(value)) {
509
+ return false;
510
+ }
511
+ const brand = value[TRANSITION_LESSOR_BRAND];
512
+ return brand != null && typeof brand === "object" && brand.protocolVersion === TRANSITION_LESSOR_PROTOCOL_VERSION && isTransitionSessionRevocationCapability(
513
+ brand.revocation
514
+ ) && typeof value.acquire === "function";
515
+ } catch {
516
+ return false;
517
+ }
518
+ }
519
+ function lessorBrand(target) {
520
+ const brand = target[TRANSITION_LESSOR_BRAND];
521
+ if (!brand) throw new Error(TRANSITION_LESSOR_REJECTION);
522
+ return brand;
523
+ }
524
+ function isTransitionSessionRevocationCapability(value) {
525
+ return value != null && typeof value === "object" && Object.isFrozen(value) && typeof value.compromise === "function" && typeof value.isCompromised === "function";
526
+ }
527
+ function isObject(value) {
528
+ return value != null && (typeof value === "object" || typeof value === "function");
529
+ }
530
+ var TransitionLeaseFailureError = class extends Error {
531
+ constructor(cause) {
532
+ super("transition lease released after a failure", { cause });
533
+ this.name = "TransitionLeaseFailureError";
534
+ }
535
+ };
536
+ function releaseArgument(failure) {
537
+ return failure.error ? failure.error : new TransitionLeaseFailureError(failure.error);
538
+ }
539
+ function transitionLessorRejectionAssessment(artifact, lifecycle = "planned", detail = TRANSITION_LESSOR_REJECTION_DETAIL) {
540
+ return {
541
+ decision: "blocked",
542
+ assurance: "unproven",
543
+ lifecycle,
544
+ continuation: "human-intervention-required",
545
+ reasons: [
546
+ {
547
+ code: "context-mismatch",
548
+ artifact,
549
+ fact: {
550
+ key: "transition-lessor",
551
+ value: TRANSITION_LESSOR_REJECTION
552
+ },
553
+ detail,
554
+ scope: []
555
+ }
556
+ ]
557
+ };
558
+ }
559
+
560
+ // src/transition/declaration.ts
561
+ import { canonicalResourceParent } from "@dbsp/types";
562
+
563
+ // src/transition/minting.ts
564
+ var minted = /* @__PURE__ */ new WeakSet();
565
+ function isObject2(value) {
566
+ return typeof value === "object" && value !== null || typeof value === "function";
567
+ }
568
+ function childPath(parent, key) {
569
+ if (typeof key === "symbol") {
570
+ return `${parent}[${String(key)}]`;
571
+ }
572
+ if (/^(0|[1-9]\d*)$/.test(key)) {
573
+ return `${parent}[${key}]`;
574
+ }
575
+ if (/^[A-Za-z_$][\w$]*$/.test(key)) {
576
+ return `${parent}.${key}`;
577
+ }
578
+ return `${parent}[${JSON.stringify(key)}]`;
579
+ }
580
+ function kindOf(value) {
581
+ if (typeof value === "function") {
582
+ return "function";
583
+ }
584
+ if (value instanceof Map) {
585
+ return "Map";
586
+ }
587
+ if (value instanceof Set) {
588
+ return "Set";
589
+ }
590
+ if (value instanceof Date) {
591
+ return "Date";
592
+ }
593
+ if (value instanceof RegExp) {
594
+ return "RegExp";
595
+ }
596
+ if (ArrayBuffer.isView(value)) {
597
+ return "typed array";
598
+ }
599
+ if (value instanceof ArrayBuffer) {
600
+ return "ArrayBuffer";
601
+ }
602
+ return value.constructor?.name ?? "object";
603
+ }
604
+ function plainDataError(kind, path) {
605
+ return new Error(
606
+ `a minted plan must be plain data; found ${kind} at ${path}`
607
+ );
608
+ }
609
+ function isVisibleArrayIndex(value, key) {
610
+ const index = Number(key);
611
+ return String(index) === key && Number.isInteger(index) && index >= 0 && index < value.length;
612
+ }
613
+ function assertPlainDataAndFreeze(value, path, seen) {
614
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "undefined") {
615
+ return;
616
+ }
617
+ if (typeof value === "bigint" || typeof value === "symbol") {
618
+ throw plainDataError(typeof value, path);
619
+ }
620
+ if (!isObject2(value)) {
621
+ return;
622
+ }
623
+ if (seen.has(value)) {
624
+ return;
625
+ }
626
+ seen.add(value);
627
+ if (typeof value === "function") {
628
+ throw plainDataError("function", path);
629
+ }
630
+ if (value instanceof Map || value instanceof Set || value instanceof Date || value instanceof RegExp || ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
631
+ throw plainDataError(kindOf(value), path);
632
+ }
633
+ let prototype;
634
+ let keys;
635
+ try {
636
+ prototype = Object.getPrototypeOf(value);
637
+ keys = Reflect.ownKeys(value);
638
+ } catch (error) {
639
+ throw plainDataError(
640
+ error instanceof Error ? `Proxy (${error.message})` : "Proxy",
641
+ path
642
+ );
643
+ }
644
+ const isPlainObject = prototype === Object.prototype;
645
+ const isPlainArray = Array.isArray(value) && prototype === Array.prototype;
646
+ if (!isPlainObject && !isPlainArray) {
647
+ throw plainDataError(kindOf(value), path);
648
+ }
649
+ for (const key of keys) {
650
+ if (typeof key === "symbol") {
651
+ throw plainDataError("symbol key", childPath(path, key));
652
+ }
653
+ let descriptor;
654
+ try {
655
+ descriptor = Object.getOwnPropertyDescriptor(value, key);
656
+ } catch (error) {
657
+ throw plainDataError(
658
+ error instanceof Error ? `Proxy (${error.message})` : "Proxy",
659
+ childPath(path, key)
660
+ );
661
+ }
662
+ if (!descriptor) {
663
+ continue;
664
+ }
665
+ if ("get" in descriptor || "set" in descriptor) {
666
+ throw plainDataError("accessor", childPath(path, key));
667
+ }
668
+ if (isPlainObject && !descriptor.enumerable) {
669
+ throw plainDataError("non-enumerable property", childPath(path, key));
670
+ }
671
+ if (isPlainArray && key !== "length" && !isVisibleArrayIndex(value, key)) {
672
+ throw plainDataError("named array property", childPath(path, key));
673
+ }
674
+ assertPlainDataAndFreeze(descriptor.value, childPath(path, key), seen);
675
+ }
676
+ try {
677
+ Object.freeze(value);
678
+ } catch (error) {
679
+ throw plainDataError(
680
+ error instanceof Error ? `Proxy (${error.message})` : "Proxy",
681
+ path
682
+ );
683
+ }
684
+ }
685
+ function mintInProcessPlan(shape) {
686
+ assertPlainDataAndFreeze(shape, "$", /* @__PURE__ */ new WeakSet());
687
+ minted.add(shape);
688
+ return shape;
689
+ }
690
+ function isMintedInProcessPlan(plan) {
691
+ return isObject2(plan) && minted.has(plan);
692
+ }
693
+
694
+ // src/transition/stable-json.ts
695
+ function stableNumber(value) {
696
+ if (Number.isNaN(value)) {
697
+ return "number:NaN";
698
+ }
699
+ if (Object.is(value, -0)) {
700
+ return "number:-0";
701
+ }
702
+ if (value === Number.POSITIVE_INFINITY) {
703
+ return "number:Infinity";
704
+ }
705
+ if (value === Number.NEGATIVE_INFINITY) {
706
+ return "number:-Infinity";
707
+ }
708
+ return `number:${value}`;
709
+ }
710
+ function objectTag(value) {
711
+ return Object.prototype.toString.call(value);
712
+ }
713
+ function stableJsonInner(value, seen) {
714
+ if (value === null) {
715
+ return "null";
716
+ }
717
+ switch (typeof value) {
718
+ case "undefined":
719
+ return "undefined";
720
+ case "boolean":
721
+ return `boolean:${value ? "true" : "false"}`;
722
+ case "number":
723
+ return stableNumber(value);
724
+ case "bigint":
725
+ return `bigint:${value.toString()}`;
726
+ case "string":
727
+ return `string:${JSON.stringify(value)}`;
728
+ case "symbol":
729
+ return `symbol:${JSON.stringify(String(value.description ?? ""))}`;
730
+ case "function":
731
+ return `function:${JSON.stringify(value.name)}`;
732
+ case "object":
733
+ break;
734
+ }
735
+ if (seen.has(value)) {
736
+ throw new TypeError("stableJson cannot serialize cyclic structures");
737
+ }
738
+ seen.add(value);
739
+ try {
740
+ if (value instanceof Date) {
741
+ const time = value.getTime();
742
+ return Number.isNaN(time) ? "date:Invalid" : `date:${time}`;
743
+ }
744
+ if (value instanceof RegExp) {
745
+ return `regexp:${JSON.stringify(value.source)}/${value.flags}`;
746
+ }
747
+ if (value instanceof Map) {
748
+ const entries2 = [...value.entries()].map(
749
+ ([key, item]) => `[${stableJsonInner(key, seen)},${stableJsonInner(item, seen)}]`
750
+ ).sort();
751
+ return `map:[${entries2.join(",")}]`;
752
+ }
753
+ if (value instanceof Set) {
754
+ const entries2 = [...value.values()].map((item) => stableJsonInner(item, seen)).sort();
755
+ return `set:[${entries2.join(",")}]`;
756
+ }
757
+ if (Array.isArray(value)) {
758
+ const entries2 = [];
759
+ for (let index = 0; index < value.length; index += 1) {
760
+ entries2.push(
761
+ Object.hasOwn(value, index) ? stableJsonInner(value[index], seen) : "array-hole"
762
+ );
763
+ }
764
+ return `array:[${entries2.join(",")}]`;
765
+ }
766
+ const tag = objectTag(value);
767
+ const entries = Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(
768
+ ([key, item]) => `${JSON.stringify(key)}:${stableJsonInner(item, seen)}`
769
+ );
770
+ return `${tag}:{${entries.join(",")}}`;
771
+ } finally {
772
+ seen.delete(value);
773
+ }
774
+ }
775
+ function stableJson(value) {
776
+ return stableJsonInner(value, /* @__PURE__ */ new WeakSet());
777
+ }
778
+
779
+ // src/transition/declaration.ts
780
+ var identityDeclarationNaming = {
781
+ toDatabase: (identifier) => identifier
782
+ };
783
+ function jsonError(path, detail) {
784
+ return new Error(
785
+ `declaration is not canonicalizable JSON at ${path}: ${detail}`
786
+ );
787
+ }
788
+ function childPath2(parent, key) {
789
+ if (typeof key === "number") return `${parent}[${key}]`;
790
+ return /^[A-Za-z_$][\w$]*$/u.test(key) ? `${parent}.${key}` : `${parent}[${JSON.stringify(key)}]`;
791
+ }
792
+ function assertCanonicalizableJson(value, path = "$", seen = /* @__PURE__ */ new WeakSet()) {
793
+ if (value === null || typeof value === "string" || typeof value === "boolean")
794
+ return;
795
+ if (typeof value === "number") {
796
+ if (!Number.isFinite(value)) throw jsonError(path, "number must be finite");
797
+ return;
798
+ }
799
+ if (typeof value !== "object") {
800
+ throw jsonError(path, `found ${typeof value}`);
801
+ }
802
+ if (seen.has(value)) throw jsonError(path, "cyclic value");
803
+ seen.add(value);
804
+ try {
805
+ if (Array.isArray(value)) {
806
+ for (let index = 0; index < value.length; index += 1) {
807
+ if (!Object.hasOwn(value, index))
808
+ throw jsonError(childPath2(path, index), "array hole");
809
+ assertCanonicalizableJson(value[index], childPath2(path, index), seen);
810
+ }
811
+ if (Reflect.ownKeys(value).some(
812
+ (key) => key !== "length" && !(typeof key === "string" && /^(0|[1-9]\d*)$/u.test(key))
813
+ ))
814
+ throw jsonError(path, "array has a non-index property");
815
+ return;
816
+ }
817
+ if (Object.getPrototypeOf(value) !== Object.prototype)
818
+ throw jsonError(
819
+ path,
820
+ `found ${value.constructor?.name ?? "non-plain object"}`
821
+ );
822
+ for (const key of Reflect.ownKeys(value)) {
823
+ if (typeof key !== "string") throw jsonError(path, "symbol key");
824
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
825
+ if (!descriptor || "get" in descriptor || "set" in descriptor)
826
+ throw jsonError(childPath2(path, key), "accessor property");
827
+ if (!descriptor.enumerable)
828
+ throw jsonError(childPath2(path, key), "non-enumerable property");
829
+ assertCanonicalizableJson(descriptor.value, childPath2(path, key), seen);
830
+ }
831
+ } finally {
832
+ seen.delete(value);
833
+ }
834
+ }
835
+ function canonicalPayload(value, path) {
836
+ assertCanonicalizableJson(value, path);
837
+ return JSON.parse(canonicalJson(value));
838
+ }
839
+ function digest(value) {
840
+ return canonicalJsonDigest(value);
841
+ }
842
+ function address(context, kind, name, parent, identity) {
843
+ return {
844
+ engine: context.engine,
845
+ database: context.database,
846
+ ...kind === "extension" ? {} : { schema: context.schema },
847
+ ...parent === void 0 ? {} : { parent: canonicalResourceParent(parent) },
848
+ kind,
849
+ name,
850
+ ...identity === void 0 ? {} : { catalogueIdentity: identity }
851
+ };
852
+ }
853
+ function declaration(context, kind, name, fragment, path, parent) {
854
+ const canonical = canonicalPayload(fragment, path);
855
+ return {
856
+ address: address(context, kind, name, parent),
857
+ fragment: canonical,
858
+ digest: digest(canonical)
859
+ };
860
+ }
861
+ function optional(key, value) {
862
+ return value === void 0 ? {} : { [key]: value };
863
+ }
864
+ function tableAddress(context, name) {
865
+ return address(context, "table", name);
866
+ }
867
+ function declarationSetFromModel(model, context, naming = identityDeclarationNaming) {
868
+ const declarations = [];
869
+ const toDatabase = (identifier) => naming.toDatabase(identifier);
870
+ for (const [tableKey, table] of [...model.tables].sort(
871
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
872
+ )) {
873
+ const tablePath = `schema.tables[${JSON.stringify(tableKey)}]`;
874
+ const tableName = toDatabase(table.name);
875
+ const parent = tableAddress(context, tableName);
876
+ declarations.push(
877
+ declaration(context, "table", tableName, { name: tableName }, tablePath)
878
+ );
879
+ for (const [index, column] of table.columns.entries()) {
880
+ const fragment = {
881
+ name: toDatabase(column.name),
882
+ type: column.type,
883
+ nullable: column.nullable,
884
+ ...optional("js", column.js),
885
+ ...optional("default", column.default),
886
+ ...optional("originalDbType", column.originalDbType),
887
+ ...optional("originalDbTypeSchema", column.originalDbTypeSchema),
888
+ ...optional(
889
+ "originalDbTypeSchemaScope",
890
+ column.originalDbTypeSchemaScope
891
+ ),
892
+ ...optional("unique", column.unique),
893
+ ...optional("uniqueConstraintName", column.uniqueConstraintName),
894
+ ...optional("autoIncrement", column.autoIncrement),
895
+ ...optional("collation", column.collation),
896
+ ...optional("identity", column.identity)
897
+ };
898
+ declarations.push(
899
+ declaration(
900
+ context,
901
+ "column",
902
+ toDatabase(column.name),
903
+ fragment,
904
+ `${tablePath}.columns[${index}]`,
905
+ parent
906
+ )
907
+ );
908
+ }
909
+ for (const [index, item] of table.indexes.entries()) {
910
+ const physicalItem = {
911
+ ...item,
912
+ ...item.name === void 0 ? {} : { name: toDatabase(item.name) },
913
+ columns: item.columns.map(toDatabase),
914
+ ...item.include === void 0 ? {} : { include: item.include.map(toDatabase) },
915
+ ...item.opclass === void 0 ? {} : {
916
+ opclass: Object.fromEntries(
917
+ Object.entries(item.opclass).map(([key, value]) => [
918
+ toDatabase(key),
919
+ value
920
+ ])
921
+ )
922
+ }
923
+ };
924
+ const name = physicalItem.name ?? `idx_${tableName}_${physicalItem.columns.join("_")}`;
925
+ declarations.push(
926
+ declaration(
927
+ context,
928
+ "index",
929
+ name,
930
+ physicalItem,
931
+ `${tablePath}.indexes[${index}]`,
932
+ parent
933
+ )
934
+ );
935
+ }
936
+ if (table.primaryKey !== void 0) {
937
+ const columns = typeof table.primaryKey === "string" ? toDatabase(table.primaryKey) : table.primaryKey.map(toDatabase);
938
+ declarations.push(
939
+ declaration(
940
+ context,
941
+ "constraint",
942
+ `pk_${tableName}`,
943
+ { kind: "primary-key", columns },
944
+ `${tablePath}.primaryKey`,
945
+ parent
946
+ )
947
+ );
948
+ }
949
+ for (const [index, item] of table.foreignKeys.entries()) {
950
+ const physicalItem = {
951
+ ...item,
952
+ columns: item.columns.map(toDatabase),
953
+ references: {
954
+ ...item.references,
955
+ table: toDatabase(item.references.table),
956
+ columns: item.references.columns.map(toDatabase)
957
+ }
958
+ };
959
+ declarations.push(
960
+ declaration(
961
+ context,
962
+ "constraint",
963
+ `fk_${tableName}_${physicalItem.columns.join("_")}`,
964
+ { kind: "foreign-key", ...physicalItem },
965
+ `${tablePath}.foreignKeys[${index}]`,
966
+ parent
967
+ )
968
+ );
969
+ }
970
+ for (const [index, item] of (table.checkConstraints ?? []).entries()) {
971
+ const physicalItem = { ...item, name: toDatabase(item.name) };
972
+ declarations.push(
973
+ declaration(
974
+ context,
975
+ "constraint",
976
+ physicalItem.name,
977
+ { kind: "check", ...physicalItem },
978
+ `${tablePath}.checkConstraints[${index}]`,
979
+ parent
980
+ )
981
+ );
982
+ }
983
+ }
984
+ for (const [name, item] of [...model.enums ?? /* @__PURE__ */ new Map()].sort(
985
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
986
+ )) {
987
+ declarations.push(
988
+ declaration(
989
+ context,
990
+ "enum",
991
+ toDatabase(name),
992
+ { ...item, name: toDatabase(item.name) },
993
+ `schema.enums[${JSON.stringify(name)}]`
994
+ )
995
+ );
996
+ }
997
+ for (const [name, item] of [...model.sequences ?? /* @__PURE__ */ new Map()].sort(
998
+ ([a], [b]) => a < b ? -1 : a > b ? 1 : 0
999
+ )) {
1000
+ declarations.push(
1001
+ declaration(
1002
+ context,
1003
+ "sequence",
1004
+ toDatabase(name),
1005
+ { ...item, name: toDatabase(item.name) },
1006
+ `schema.sequences[${JSON.stringify(name)}]`
1007
+ )
1008
+ );
1009
+ }
1010
+ for (const name of [...model.extensions ?? []].sort()) {
1011
+ declarations.push(
1012
+ declaration(
1013
+ context,
1014
+ "extension",
1015
+ name,
1016
+ { name },
1017
+ `schema.extensions[${JSON.stringify(name)}]`
1018
+ )
1019
+ );
1020
+ }
1021
+ const canonicalDeclarations = declarations.sort((left, right) => {
1022
+ const a = stableJson(left.address);
1023
+ const b = stableJson(right.address);
1024
+ return a < b ? -1 : a > b ? 1 : 0;
1025
+ });
1026
+ const setValue = canonicalPayload(
1027
+ { version: 1, declarations: canonicalDeclarations },
1028
+ "schema.declarations"
1029
+ );
1030
+ return {
1031
+ version: 1,
1032
+ declarations: canonicalDeclarations,
1033
+ digest: digest(setValue)
1034
+ };
1035
+ }
1036
+ function validateDeclarationModel(model) {
1037
+ declarationSetFromModel(model, {
1038
+ engine: "declaration-validation",
1039
+ database: "declaration-validation",
1040
+ schema: "declaration-validation"
1041
+ });
1042
+ }
1043
+ function admitRecordedIdentity(recorded, live) {
1044
+ if (recorded.catalogueIdentity === void 0)
1045
+ return {
1046
+ ok: false,
1047
+ detail: `recorded address ${recorded.kind} ${recorded.name} has no catalogue identity`
1048
+ };
1049
+ if (live.catalogueIdentity === void 0)
1050
+ return {
1051
+ ok: false,
1052
+ detail: `live address ${live.kind} ${live.name} has no catalogue identity`
1053
+ };
1054
+ if (stableJson(recorded.catalogueIdentity) !== stableJson(live.catalogueIdentity))
1055
+ return {
1056
+ ok: false,
1057
+ detail: `identity drift for ${recorded.kind} ${recorded.name}: recorded ${stableJson(recorded.catalogueIdentity)} does not match live ${stableJson(live.catalogueIdentity)}`
1058
+ };
1059
+ return { ok: true };
1060
+ }
1061
+ function bindDeclarationSet(plan, declarations) {
1062
+ return mintInProcessPlan({ ...plan, declarations });
1063
+ }
1064
+
1065
+ // src/transition/lifecycle-interpreter.ts
1066
+ import {
1067
+ LEDGER_EVENT_KINDS,
1068
+ sameLedgerAddress
1069
+ } from "@dbsp/types";
1070
+ var LEDGER_PROJECTION_CODE_VERSION = 1;
1071
+ var LEDGER_LIFECYCLE_GRAMMAR = {
1072
+ intent: {
1073
+ claimKind: "intent",
1074
+ opensFrom: ["unknown", "managed", "absent"],
1075
+ resolvesThrough: [
1076
+ "refused",
1077
+ "executing",
1078
+ "observed",
1079
+ "indeterminate",
1080
+ "resolved"
1081
+ ]
1082
+ },
1083
+ "retire-intent": {
1084
+ claimKind: "retire-intent",
1085
+ opensFrom: ["managed"],
1086
+ resolvesThrough: [
1087
+ "refused",
1088
+ "executing",
1089
+ "absent",
1090
+ "indeterminate",
1091
+ "resolved"
1092
+ ]
1093
+ },
1094
+ "readdress-intent": {
1095
+ claimKind: "readdress-intent",
1096
+ opensFrom: ["unknown", "managed", "absent"],
1097
+ resolvesThrough: [
1098
+ "refused",
1099
+ "indeterminate",
1100
+ "readdressed-to",
1101
+ "readdressed-from"
1102
+ ]
1103
+ },
1104
+ "adopt-intent": {
1105
+ claimKind: "adopt-intent",
1106
+ opensFrom: ["unknown"],
1107
+ resolvesThrough: ["refused", "adopt"]
1108
+ }
1109
+ };
1110
+ var CLAIM_KINDS = /* @__PURE__ */ new Set([
1111
+ "adopt-intent",
1112
+ "intent",
1113
+ "retire-intent",
1114
+ "readdress-intent"
1115
+ ]);
1116
+ function isLedgerEventKind(value) {
1117
+ return LEDGER_EVENT_KINDS.includes(value);
1118
+ }
1119
+ function invalid(detail) {
1120
+ return { kind: "invalid", detail };
1121
+ }
1122
+ function requiresReadBack(event, label) {
1123
+ if (event.observed !== void 0) return void 0;
1124
+ return invalid(`${label} requires its supporting read-back`);
1125
+ }
1126
+ function opensClaim(stableState, event) {
1127
+ if (!CLAIM_KINDS.has(event.eventKind)) {
1128
+ return invalid(`event ${event.eventKind} cannot open a claim`);
1129
+ }
1130
+ const claimKind = event.eventKind;
1131
+ const column = LEDGER_LIFECYCLE_GRAMMAR[claimKind];
1132
+ if (!column.opensFrom.includes(stableState)) {
1133
+ return invalid(`${claimKind} cannot open from stable state ${stableState}`);
1134
+ }
1135
+ if (claimKind === "readdress-intent" && !event.pairId) {
1136
+ return invalid("readdress-intent requires a pair id");
1137
+ }
1138
+ return {
1139
+ kind: "ok",
1140
+ frame: {
1141
+ stableState,
1142
+ openClaim: {
1143
+ event,
1144
+ kind: claimKind,
1145
+ stableStateBeforeClaim: stableState,
1146
+ phase: "claimed"
1147
+ }
1148
+ }
1149
+ };
1150
+ }
1151
+ function resolveStableState(claim, event) {
1152
+ if (claim.kind === "intent")
1153
+ return event.observed === void 0 ? claim.stableStateBeforeClaim : "managed";
1154
+ return event.observed === void 0 ? "absent" : "managed";
1155
+ }
1156
+ function interpretLedgerLifecycle(frame, event) {
1157
+ if (frame.openClaim === void 0 && event.eventKind === "released") {
1158
+ if (frame.stableState !== "managed")
1159
+ return invalid("released requires a managed stable state");
1160
+ return { kind: "ok", frame: { stableState: "unknown" } };
1161
+ }
1162
+ if (frame.openClaim === void 0)
1163
+ return opensClaim(frame.stableState, event);
1164
+ const claim = frame.openClaim;
1165
+ const allowed = LEDGER_LIFECYCLE_GRAMMAR[claim.kind].resolvesThrough;
1166
+ if (!allowed.includes(event.eventKind)) {
1167
+ return invalid(
1168
+ `${event.eventKind} is not in the ${claim.kind} resolution column`
1169
+ );
1170
+ }
1171
+ switch (event.eventKind) {
1172
+ case "refused":
1173
+ if (claim.phase === "indeterminate")
1174
+ return invalid("refused cannot close an indeterminate claim");
1175
+ return { kind: "ok", frame: { stableState: frame.stableState } };
1176
+ case "executing":
1177
+ if (claim.phase !== "claimed")
1178
+ return invalid("executing may follow only a newly opened claim");
1179
+ return {
1180
+ kind: "ok",
1181
+ frame: {
1182
+ stableState: frame.stableState,
1183
+ openClaim: { ...claim, phase: "executing" }
1184
+ }
1185
+ };
1186
+ case "observed": {
1187
+ const readBack = requiresReadBack(event, "observed");
1188
+ if (readBack) return readBack;
1189
+ if (claim.phase === "indeterminate")
1190
+ return invalid("observed cannot close an indeterminate claim");
1191
+ return { kind: "ok", frame: { stableState: "managed" } };
1192
+ }
1193
+ case "absent":
1194
+ if (claim.phase === "indeterminate")
1195
+ return invalid("absent cannot close an indeterminate claim");
1196
+ return { kind: "ok", frame: { stableState: "absent" } };
1197
+ case "indeterminate":
1198
+ if (claim.phase === "indeterminate")
1199
+ return invalid("indeterminate may be appended only once");
1200
+ return {
1201
+ kind: "ok",
1202
+ frame: {
1203
+ stableState: frame.stableState,
1204
+ openClaim: { ...claim, phase: "indeterminate" }
1205
+ }
1206
+ };
1207
+ case "resolved": {
1208
+ if (claim.phase !== "indeterminate")
1209
+ return invalid("resolved closes only an indeterminate claim");
1210
+ return {
1211
+ kind: "ok",
1212
+ frame: { stableState: resolveStableState(claim, event) }
1213
+ };
1214
+ }
1215
+ case "adopt": {
1216
+ const readBack = requiresReadBack(event, "adopt");
1217
+ if (readBack) return readBack;
1218
+ return { kind: "ok", frame: { stableState: "managed" } };
1219
+ }
1220
+ case "readdressed-to":
1221
+ if (claim.stableStateBeforeClaim !== "managed")
1222
+ return invalid("readdressed-to resolves only a managed source");
1223
+ if (event.pairId !== claim.event.pairId)
1224
+ return invalid("readdressed-to pair id does not match its claim");
1225
+ return { kind: "ok", frame: { stableState: "unknown" } };
1226
+ case "readdressed-from": {
1227
+ const readBack = requiresReadBack(event, "readdressed-from");
1228
+ if (readBack) return readBack;
1229
+ if (claim.stableStateBeforeClaim === "managed")
1230
+ return invalid("readdressed-from resolves only an unoccupied target");
1231
+ if (event.pairId !== claim.event.pairId)
1232
+ return invalid("readdressed-from pair id does not match its claim");
1233
+ return { kind: "ok", frame: { stableState: "managed" } };
1234
+ }
1235
+ default:
1236
+ return invalid(`${event.eventKind} cannot resolve a claim`);
1237
+ }
1238
+ }
1239
+ function unprojectable(input, reason) {
1240
+ return {
1241
+ kind: "unprojectable-ledger-chain",
1242
+ ledger: input.ledger,
1243
+ address: input.address,
1244
+ events: input.events,
1245
+ reason,
1246
+ codeVersion: LEDGER_PROJECTION_CODE_VERSION
1247
+ };
1248
+ }
1249
+ function reportState(frame) {
1250
+ if (frame.openClaim === void 0) return { kind: frame.stableState };
1251
+ return frame.openClaim.phase === "indeterminate" ? {
1252
+ kind: "blocked",
1253
+ stableState: frame.stableState,
1254
+ claim: frame.openClaim
1255
+ } : {
1256
+ kind: "pending",
1257
+ stableState: frame.stableState,
1258
+ claim: frame.openClaim
1259
+ };
1260
+ }
1261
+ function projected(input, frame, declaration2, observation) {
1262
+ return {
1263
+ kind: "projected-ledger-chain",
1264
+ ledger: input.ledger,
1265
+ address: input.address,
1266
+ events: input.events,
1267
+ stableState: frame.stableState,
1268
+ ...frame.openClaim === void 0 ? {} : { openClaim: frame.openClaim },
1269
+ reportedState: reportState(frame),
1270
+ ...declaration2 === void 0 ? {} : { declaration: declaration2 },
1271
+ ...observation === void 0 ? {} : { observation }
1272
+ };
1273
+ }
1274
+ function projectLedgerChain(input) {
1275
+ const byId = /* @__PURE__ */ new Map();
1276
+ const children = /* @__PURE__ */ new Map();
1277
+ for (const event of input.events) {
1278
+ if (!sameLedgerAddress(input.address, event.address))
1279
+ return unprojectable(input, {
1280
+ code: "address-mismatch",
1281
+ eventId: event.eventId
1282
+ });
1283
+ if (!isLedgerEventKind(event.eventKind))
1284
+ return unprojectable(input, {
1285
+ code: "unknown-event-kind",
1286
+ eventId: event.eventId,
1287
+ eventKind: event.eventKind
1288
+ });
1289
+ if (byId.has(event.eventId))
1290
+ return unprojectable(input, {
1291
+ code: "duplicate-event-id",
1292
+ eventId: event.eventId
1293
+ });
1294
+ byId.set(event.eventId, event);
1295
+ const siblings = children.get(event.predecessor) ?? [];
1296
+ siblings.push(event);
1297
+ children.set(event.predecessor, siblings);
1298
+ }
1299
+ for (const event of input.events) {
1300
+ if (event.predecessor !== void 0 && !byId.has(event.predecessor)) {
1301
+ return unprojectable(input, {
1302
+ code: "missing-predecessor",
1303
+ eventId: event.eventId,
1304
+ predecessor: event.predecessor
1305
+ });
1306
+ }
1307
+ }
1308
+ for (const [predecessor, siblings] of children) {
1309
+ if (siblings.length > 1)
1310
+ return unprojectable(input, {
1311
+ code: "fork",
1312
+ ...predecessor === void 0 ? {} : { predecessor },
1313
+ eventIds: siblings.map((event) => event.eventId)
1314
+ });
1315
+ }
1316
+ const roots = children.get(void 0) ?? [];
1317
+ if (roots.length > 1)
1318
+ return unprojectable(input, {
1319
+ code: "fork",
1320
+ eventIds: roots.map((event) => event.eventId)
1321
+ });
1322
+ if (input.events.length > 0 && roots.length === 0)
1323
+ return unprojectable(input, {
1324
+ code: "cycle",
1325
+ eventIds: input.events.map((event) => event.eventId)
1326
+ });
1327
+ let frame = { stableState: "unknown" };
1328
+ let declaration2;
1329
+ let observation;
1330
+ let current = roots[0];
1331
+ const visited = /* @__PURE__ */ new Set();
1332
+ while (current !== void 0) {
1333
+ if (visited.has(current.eventId))
1334
+ return unprojectable(input, {
1335
+ code: "cycle",
1336
+ eventIds: [...visited, current.eventId]
1337
+ });
1338
+ visited.add(current.eventId);
1339
+ const step = interpretLedgerLifecycle(frame, current);
1340
+ if (step.kind === "invalid")
1341
+ return unprojectable(input, {
1342
+ code: "invalid-lifecycle-edge",
1343
+ eventId: current.eventId,
1344
+ detail: step.detail
1345
+ });
1346
+ frame = step.frame;
1347
+ if (current.declared !== void 0) declaration2 = current.declared;
1348
+ if (current.observed !== void 0) observation = current.observed;
1349
+ current = children.get(current.eventId)?.[0];
1350
+ }
1351
+ if (visited.size !== input.events.length)
1352
+ return unprojectable(input, {
1353
+ code: "cycle",
1354
+ eventIds: input.events.filter((event) => !visited.has(event.eventId)).map((event) => event.eventId)
1355
+ });
1356
+ return projected(input, frame, declaration2, observation);
1357
+ }
1358
+
1359
+ // src/transition/outcome-protocol.ts
1360
+ import { createHash as createHash2 } from "crypto";
1361
+ import * as transitionTypes from "@dbsp/types";
1362
+ import { refusalFor, sameLedgerAddress as sameLedgerAddress2 } from "@dbsp/types";
1363
+ var tokenRecords = /* @__PURE__ */ new WeakMap();
1364
+ var sameControllerIdentity2 = transitionTypes.sameControllerIdentity ?? ((recorded, current) => recorded.name === current.name && recorded.oid === current.oid);
1365
+ function findUniqueLedgerTerminal(events) {
1366
+ const predecessors = /* @__PURE__ */ new Set();
1367
+ for (const event of events) {
1368
+ if (event.predecessor !== void 0) predecessors.add(event.predecessor);
1369
+ }
1370
+ let terminal;
1371
+ for (const event of events) {
1372
+ if (predecessors.has(event.eventId)) continue;
1373
+ if (terminal !== void 0) return void 0;
1374
+ terminal = event;
1375
+ }
1376
+ return terminal;
1377
+ }
1378
+ function outcomeClaimId(executionId, plannedClaimKey, address2, closureMemberKey) {
1379
+ return `dbsp.transition.outcome.${createHash2("sha256").update(
1380
+ stableJson({ executionId, plannedClaimKey, address: address2, closureMemberKey })
1381
+ ).digest("hex")}`;
1382
+ }
1383
+ function outcomeClaimEventId(claimId, role) {
1384
+ return `dbsp.transition.outcome.${createHash2("sha256").update(stableJson({ claimId, role })).digest("hex")}`;
1385
+ }
1386
+ function refusal(reason) {
1387
+ return { kind: "outcome-protocol-refused", reason };
1388
+ }
1389
+ function bundleRefusal(plan) {
1390
+ const statements = plan.statementBundle.statements;
1391
+ if (plan.claimSpecies === "cascade-covered") {
1392
+ if (statements.length !== 0)
1393
+ return refusal(
1394
+ `cascade-covered claim ${plan.claimId} has a non-empty statement bundle`
1395
+ );
1396
+ return void 0;
1397
+ }
1398
+ if (plan.claimSpecies === "adoption") {
1399
+ if (plan.claimKind !== "adopt-intent" || statements.length !== 0)
1400
+ return refusal(
1401
+ `adoption claim ${plan.claimId} has an invalid statement bundle`
1402
+ );
1403
+ return void 0;
1404
+ }
1405
+ if (statements.length === 0)
1406
+ return refusal(`claim ${plan.claimId} has an empty statement bundle`);
1407
+ for (let index = 0; index < statements.length; index += 1) {
1408
+ const statement = statements[index];
1409
+ if (!statement || statement.ordinal !== index || statement.sql.length === 0)
1410
+ return refusal(
1411
+ `claim ${plan.claimId} has an invalid statement bundle at ordinal ${index}`
1412
+ );
1413
+ }
1414
+ return void 0;
1415
+ }
1416
+ function fixedBundle(bundle) {
1417
+ return Object.freeze({
1418
+ statements: Object.freeze(
1419
+ bundle.statements.map(
1420
+ (statement) => Object.freeze({ ordinal: statement.ordinal, sql: statement.sql })
1421
+ )
1422
+ )
1423
+ });
1424
+ }
1425
+ function mintClaimToken(plan) {
1426
+ const token = Object.freeze({});
1427
+ tokenRecords.set(token, {
1428
+ claimId: plan.claimId,
1429
+ bundle: fixedBundle(plan.statementBundle),
1430
+ consumed: false
1431
+ });
1432
+ return token;
1433
+ }
1434
+ function claimIdForToken(token) {
1435
+ return tokenRecords.get(token)?.claimId;
1436
+ }
1437
+ function consumeClaimToken(token, claimId, statements) {
1438
+ const record = tokenRecords.get(token);
1439
+ if (!record) return refusal("claim token was not minted by claim admission");
1440
+ if (record.claimId !== claimId)
1441
+ return refusal(
1442
+ `claim token belongs to claim ${record.claimId}, not ${claimId}`
1443
+ );
1444
+ if (record.consumed)
1445
+ return refusal(
1446
+ `claim token for claim ${claimId} has already been consumed`
1447
+ );
1448
+ if (statements.length !== record.bundle.statements.length || statements.some((statement, index) => {
1449
+ const expected = record.bundle.statements[index];
1450
+ return expected === void 0 || expected.ordinal !== statement.ordinal || expected.sql !== statement.sql;
1451
+ }))
1452
+ return refusal(`statements are outside claim ${claimId}'s recorded bundle`);
1453
+ record.consumed = true;
1454
+ return record.bundle;
1455
+ }
1456
+ function admitOutcomeClaim(input) {
1457
+ const { plan, projection } = input;
1458
+ const bundleError = bundleRefusal(plan);
1459
+ if (bundleError) return bundleError;
1460
+ if (projection.kind !== "projected-ledger-chain")
1461
+ return refusal(
1462
+ `claim ${plan.claimId} refuses malformed ledger chain: ${projection.reason.code}`
1463
+ );
1464
+ if (!sameLedgerAddress2(plan.address, projection.address))
1465
+ return refusal(
1466
+ `claim ${plan.claimId} address does not match its ledger chain`
1467
+ );
1468
+ if (projection.openClaim !== void 0)
1469
+ return refusal(
1470
+ `claim ${plan.claimId} refuses open claim ${projection.openClaim.event.eventId}`
1471
+ );
1472
+ const column = LEDGER_LIFECYCLE_GRAMMAR[plan.claimKind];
1473
+ if (!column.opensFrom.includes(projection.stableState))
1474
+ return refusal(
1475
+ `${plan.claimKind} cannot open from stable state ${projection.stableState}`
1476
+ );
1477
+ if (projection.stableState === "managed" && plan.requiresVacancy !== true) {
1478
+ const terminal = findUniqueLedgerTerminal(projection.events);
1479
+ if (!terminal)
1480
+ return refusal(
1481
+ `claim ${plan.claimId} refuses managed chain without terminal`
1482
+ );
1483
+ const currentController = input.currentController;
1484
+ const controllerOid = terminal.controllerOid;
1485
+ if (currentController) {
1486
+ if (!controllerOid)
1487
+ return refusal(
1488
+ `claim ${plan.claimId} refuses managed controller without recorded OID`
1489
+ );
1490
+ if (!sameControllerIdentity2(
1491
+ { name: terminal.controller, oid: controllerOid },
1492
+ currentController
1493
+ ))
1494
+ return refusal(
1495
+ `claim ${plan.claimId} refuses managed-by-other controller ${terminal.controller}`
1496
+ );
1497
+ } else
1498
+ return refusal(
1499
+ `claim ${plan.claimId} refuses unreadable current controller identity`
1500
+ );
1501
+ if (!input.liveAddress)
1502
+ return refusal(
1503
+ `claim ${plan.claimId} refuses missing live identity admission`
1504
+ );
1505
+ const recorded = terminal.catalogueIdentity ? { ...plan.address, catalogueIdentity: terminal.catalogueIdentity } : plan.address;
1506
+ const identity = admitRecordedIdentity(recorded, input.liveAddress);
1507
+ if (!identity.ok) return refusal(identity.detail);
1508
+ }
1509
+ return {
1510
+ kind: "admitted-outcome-claim",
1511
+ plan,
1512
+ stableStateBeforeClaim: projection.stableState,
1513
+ token: mintClaimToken(plan)
1514
+ };
1515
+ }
1516
+ function recoveryPending(input, reason, reasonCode) {
1517
+ return {
1518
+ kind: "outcome-recovery-pending",
1519
+ address: input.projection.address,
1520
+ reason,
1521
+ ...reasonCode === void 0 ? {} : { reasonCode }
1522
+ };
1523
+ }
1524
+ function appendRecovery(input, readBack, eventKind, reason) {
1525
+ const projection = input.projection;
1526
+ if (projection.kind !== "projected-ledger-chain")
1527
+ return {
1528
+ kind: "outcome-recovery-malformed-chain",
1529
+ address: projection.address,
1530
+ reason: projection.reason.code
1531
+ };
1532
+ const claim = projection.openClaim;
1533
+ const predecessor = findUniqueLedgerTerminal(projection.events);
1534
+ if (!claim || !predecessor)
1535
+ return recoveryPending(
1536
+ input,
1537
+ "recovery could not identify the open claim terminal"
1538
+ );
1539
+ return {
1540
+ kind: "outcome-recovery-append",
1541
+ address: projection.address,
1542
+ resolution: {
1543
+ eventKind,
1544
+ predecessor: predecessor.eventId,
1545
+ rootClaimId: claim.event.eventId,
1546
+ reason,
1547
+ readBack,
1548
+ ...eventKind === "refused" ? {
1549
+ refusal: refusalFor("ERR-11", {
1550
+ address: projection.address,
1551
+ state: projection.stableState
1552
+ })
1553
+ } : {}
1554
+ }
1555
+ };
1556
+ }
1557
+ async function classifyOutcomeRecovery(input) {
1558
+ const projection = input.projection;
1559
+ if (projection.kind !== "projected-ledger-chain")
1560
+ return {
1561
+ kind: "outcome-recovery-malformed-chain",
1562
+ address: projection.address,
1563
+ reason: projection.reason.code
1564
+ };
1565
+ if (projection.openClaim === void 0)
1566
+ return {
1567
+ kind: "outcome-recovery-no-open-claim",
1568
+ address: projection.address
1569
+ };
1570
+ const claim = projection.openClaim;
1571
+ if (claim.phase === "indeterminate") {
1572
+ if (!input.resolveIndeterminate)
1573
+ return {
1574
+ kind: "outcome-recovery-blocked",
1575
+ address: projection.address,
1576
+ reason: "indeterminate claim remains blocked until resolved with a read-back"
1577
+ };
1578
+ const evidence = input.indeterminateEvidence;
1579
+ if (evidence === void 0 || evidence.claimId !== claim.event.eventId || evidence.executionId !== claim.event.executionId || evidence.plannedClaimKey !== claim.event.plannedClaimKey || evidence.persistedBundleDigest !== evidence.admittedBundleDigest || evidence.planDigest !== evidence.externalDdlExclusion.planDigest || !sameLedgerAddress2(
1580
+ evidence.externalDdlExclusion.address,
1581
+ projection.address
1582
+ ))
1583
+ return {
1584
+ kind: "outcome-recovery-blocked",
1585
+ address: projection.address,
1586
+ reason: "indeterminate claim refuses a missing or mismatched admission envelope"
1587
+ };
1588
+ }
1589
+ const readBack = await input.catalogue(projection.address);
1590
+ if (readBack.kind === "catalogue-unavailable")
1591
+ return recoveryPending(input, readBack.reason, "catalogue-unavailable");
1592
+ if (claim.phase === "indeterminate") {
1593
+ if (readBack.effect !== "applied")
1594
+ return {
1595
+ kind: "outcome-recovery-blocked",
1596
+ address: projection.address,
1597
+ reason: "indeterminate claim requires an operation-specific applied read-back"
1598
+ };
1599
+ if (claim.kind === "intent") {
1600
+ if (readBack.kind === "present")
1601
+ return appendRecovery(
1602
+ input,
1603
+ readBack,
1604
+ "resolved",
1605
+ "resolved intent is supported by the catalogue read-back"
1606
+ );
1607
+ if (claim.stableStateBeforeClaim !== "managed")
1608
+ return appendRecovery(
1609
+ input,
1610
+ readBack,
1611
+ "resolved",
1612
+ "resolved intent absence is supported by the catalogue read-back"
1613
+ );
1614
+ return {
1615
+ kind: "outcome-recovery-blocked",
1616
+ address: projection.address,
1617
+ reason: "catalogue absence cannot resolve an indeterminate modify intent to managed"
1618
+ };
1619
+ }
1620
+ if (claim.kind === "retire-intent")
1621
+ return appendRecovery(
1622
+ input,
1623
+ readBack,
1624
+ "resolved",
1625
+ readBack.kind === "absent" ? "resolved retirement absence is supported by the catalogue read-back" : "resolved retirement is supported by the catalogue read-back"
1626
+ );
1627
+ return {
1628
+ kind: "outcome-recovery-blocked",
1629
+ address: projection.address,
1630
+ reason: `indeterminate ${claim.kind} has no resolved grammar column`
1631
+ };
1632
+ }
1633
+ if (claim.phase === "claimed")
1634
+ return appendRecovery(
1635
+ input,
1636
+ readBack,
1637
+ "refused",
1638
+ "recovery read live state before refusing a claim that never reached executing"
1639
+ );
1640
+ if (claim.kind === "intent") {
1641
+ if (readBack.kind === "absent")
1642
+ return appendRecovery(
1643
+ input,
1644
+ readBack,
1645
+ "refused",
1646
+ "recovery read-back proves no effect after executing"
1647
+ );
1648
+ if (readBack.effect === "no-effect")
1649
+ return appendRecovery(
1650
+ input,
1651
+ readBack,
1652
+ "refused",
1653
+ "recovery operation read-back proves no effect after executing"
1654
+ );
1655
+ if (readBack.effect === "unverifiable")
1656
+ return recoveryPending(
1657
+ input,
1658
+ "recovery operation read-back cannot verify effect after executing"
1659
+ );
1660
+ if (claim.stableStateBeforeClaim === "managed" && readBack.effect !== "applied")
1661
+ return recoveryPending(
1662
+ input,
1663
+ "recovery executing managed modification requires an operation-specific applied read-back"
1664
+ );
1665
+ if (claim.stableStateBeforeClaim !== "managed" && !input.acceptedExternalDdlExclusion)
1666
+ return appendRecovery(
1667
+ input,
1668
+ readBack,
1669
+ "indeterminate",
1670
+ "create read-back requires claim-bound external-ddl-exclusion and operation postcondition evidence"
1671
+ );
1672
+ return appendRecovery(
1673
+ input,
1674
+ readBack,
1675
+ "observed",
1676
+ "recovery observed the live catalogue read-back after executing"
1677
+ );
1678
+ }
1679
+ if (claim.kind === "retire-intent") {
1680
+ if (readBack.kind === "absent")
1681
+ return appendRecovery(
1682
+ input,
1683
+ readBack,
1684
+ "absent",
1685
+ "recovery observed retirement absence after executing"
1686
+ );
1687
+ return recoveryPending(
1688
+ input,
1689
+ "retirement remains unverifiable because the catalogue object is present"
1690
+ );
1691
+ }
1692
+ return recoveryPending(
1693
+ input,
1694
+ `executing ${claim.kind} is not a recoverable lifecycle edge`
1695
+ );
1696
+ }
1697
+
1698
+ export {
1699
+ __reExport,
1700
+ CanonicalJsonError,
1701
+ canonicalJson,
1702
+ canonicalJsonDigest,
1703
+ stableJson,
1704
+ mintDurablyLoadedRun,
1705
+ isDurablyLoadedRun,
1706
+ mintInProcessPlan,
1707
+ isMintedInProcessPlan,
1708
+ TRANSITION_LESSOR_REJECTION,
1709
+ transitionLessorRejectionDetail,
1710
+ transitionPhysicalSessionIsCompromised,
1711
+ markTransitionClientCompromised,
1712
+ planOperationSession,
1713
+ createExclusiveTransitionTarget,
1714
+ isExclusiveTransitionTarget,
1715
+ acquireExclusiveTransitionLease,
1716
+ acquireTransitionTargetLease,
1717
+ acquireTransitionLease,
1718
+ createTransitionLessor,
1719
+ remintTransitionLessorFromSession,
1720
+ isTransitionLessor,
1721
+ transitionLessorRejectionAssessment,
1722
+ assertCanonicalizableJson,
1723
+ declarationSetFromModel,
1724
+ validateDeclarationModel,
1725
+ admitRecordedIdentity,
1726
+ bindDeclarationSet,
1727
+ LEDGER_PROJECTION_CODE_VERSION,
1728
+ LEDGER_LIFECYCLE_GRAMMAR,
1729
+ interpretLedgerLifecycle,
1730
+ projectLedgerChain,
1731
+ findUniqueLedgerTerminal,
1732
+ outcomeClaimId,
1733
+ outcomeClaimEventId,
1734
+ mintClaimToken,
1735
+ claimIdForToken,
1736
+ consumeClaimToken,
1737
+ admitOutcomeClaim,
1738
+ classifyOutcomeRecovery
1739
+ };
1740
+ //# sourceMappingURL=chunk-AQN7HBYD.js.map