@dbsp/core 3.3.0 → 3.4.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.
package/dist/index.js CHANGED
@@ -3022,9 +3022,6 @@ function generateJoinReasoning(relation, joinType, _hasFilter) {
3022
3022
  return `Relation ${relation.source}.${relation.name} is optional without filter - using LEFT JOIN to preserve parent rows without matches`;
3023
3023
  }
3024
3024
 
3025
- // src/transition/applier.ts
3026
- import { createHash as createHash3, randomUUID } from "crypto";
3027
-
3028
3025
  // src/transition/context-match.ts
3029
3026
  import { createHash } from "crypto";
3030
3027
 
@@ -3101,7 +3098,7 @@ function stableJsonInner(value, seen) {
3101
3098
  return `array:[${entries2.join(",")}]`;
3102
3099
  }
3103
3100
  const tag = objectTag(value);
3104
- const entries = Object.entries(value).sort(([left], [right]) => left.localeCompare(right)).map(
3101
+ const entries = Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(
3105
3102
  ([key, item]) => `${JSON.stringify(key)}:${stableJsonInner(item, seen)}`
3106
3103
  );
3107
3104
  return `${tag}:{${entries.join(",")}}`;
@@ -3308,6 +3305,10 @@ function plainDataError(kind, path) {
3308
3305
  `a minted plan must be plain data; found ${kind} at ${path}`
3309
3306
  );
3310
3307
  }
3308
+ function isVisibleArrayIndex(value, key) {
3309
+ const index = Number(key);
3310
+ return String(index) === key && Number.isInteger(index) && index >= 0 && index < value.length;
3311
+ }
3311
3312
  function assertPlainDataAndFreeze(value, path, seen) {
3312
3313
  if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "undefined") {
3313
3314
  return;
@@ -3363,6 +3364,12 @@ function assertPlainDataAndFreeze(value, path, seen) {
3363
3364
  if ("get" in descriptor || "set" in descriptor) {
3364
3365
  throw plainDataError("accessor", childPath(path, key));
3365
3366
  }
3367
+ if (isPlainObject && !descriptor.enumerable) {
3368
+ throw plainDataError("non-enumerable property", childPath(path, key));
3369
+ }
3370
+ if (isPlainArray && key !== "length" && !isVisibleArrayIndex(value, key)) {
3371
+ throw plainDataError("named array property", childPath(path, key));
3372
+ }
3366
3373
  assertPlainDataAndFreeze(descriptor.value, childPath(path, key), seen);
3367
3374
  }
3368
3375
  try {
@@ -3902,8 +3909,11 @@ function assumptionAccepted(assumption, policy) {
3902
3909
  });
3903
3910
  }
3904
3911
 
3905
- // src/transition/resume.ts
3912
+ // src/transition/plan-digest.ts
3906
3913
  import { createHash as createHash2 } from "crypto";
3914
+ function transitionPlanDigest(plan2) {
3915
+ return createHash2("sha256").update(stableJson(plan2)).digest("hex");
3916
+ }
3907
3917
 
3908
3918
  // src/transition/transition-lessor.ts
3909
3919
  var TRANSITION_LESSOR_BRAND = /* @__PURE__ */ Symbol.for("dbsp.transition.lessor");
@@ -4696,8 +4706,24 @@ var RESUMER_ARTIFACT = {
4696
4706
  id: semanticArtifactId("dbsp.core.transition.resume"),
4697
4707
  version: "0.1.0"
4698
4708
  };
4699
- function digest(value) {
4700
- return createHash2("sha256").update(stableJson(value)).digest("hex");
4709
+ function deepFreeze(value) {
4710
+ if (value !== null && typeof value === "object") {
4711
+ for (const child of Object.values(value)) {
4712
+ deepFreeze(child);
4713
+ }
4714
+ Object.freeze(value);
4715
+ }
4716
+ return value;
4717
+ }
4718
+ function snapshotLoadedRun(value) {
4719
+ const serialized = JSON.stringify(value);
4720
+ if (serialized === void 0) {
4721
+ throw new Error("loaded transition journal is not JSON serializable");
4722
+ }
4723
+ return deepFreeze(JSON.parse(serialized));
4724
+ }
4725
+ function errorDetail(error) {
4726
+ return error instanceof Error ? error.message : "unknown error";
4701
4727
  }
4702
4728
  function planEvidenceContext(plan2) {
4703
4729
  return plan2.observations.find(
@@ -4728,7 +4754,7 @@ function completedAssessment() {
4728
4754
  ]
4729
4755
  };
4730
4756
  }
4731
- function contextMismatch(detail) {
4757
+ function contextMismatch(detail, journals = [], observations = []) {
4732
4758
  return {
4733
4759
  assessment: assessment(
4734
4760
  {
@@ -4738,11 +4764,11 @@ function contextMismatch(detail) {
4738
4764
  scope: [],
4739
4765
  detail
4740
4766
  },
4741
- "planned",
4767
+ "outcome-unknown",
4742
4768
  "replan-required"
4743
4769
  ),
4744
- journals: [],
4745
- observations: []
4770
+ journals,
4771
+ observations
4746
4772
  };
4747
4773
  }
4748
4774
  function unacceptedStepAssumption(plan2, step, assumption) {
@@ -5116,7 +5142,7 @@ async function withObservedStep(registry, step, target, baseContext, phase, use)
5116
5142
  failure = error;
5117
5143
  observation = {
5118
5144
  ok: false,
5119
- detail: error instanceof Error ? error.message : "observation failed"
5145
+ detail: errorDetail(error)
5120
5146
  };
5121
5147
  }
5122
5148
  try {
@@ -5144,27 +5170,49 @@ function validatePolicyForStep(plan2, step, policy) {
5144
5170
  }
5145
5171
  return void 0;
5146
5172
  }
5147
- async function resumeTransitionRun(registry, input) {
5148
- const loaded = await input.loadCurrent(input.runId);
5149
- if (loaded.run.runId !== input.runId) {
5173
+ async function resumeTransitionRunInternal(registry, input, runId, completed, observations) {
5174
+ let supplied;
5175
+ try {
5176
+ supplied = await input.loadCurrent(runId);
5177
+ } catch (error) {
5178
+ return contextMismatch(
5179
+ `transition run ${runId} could not be loaded: ${errorDetail(error)}`
5180
+ );
5181
+ }
5182
+ let loaded;
5183
+ try {
5184
+ loaded = snapshotLoadedRun(supplied);
5185
+ } catch (error) {
5186
+ return contextMismatch(
5187
+ `loaded journal could not be snapshotted safely: ${errorDetail(error)}`
5188
+ );
5189
+ }
5190
+ if (loaded.run.runId !== runId) {
5150
5191
  return contextMismatch(
5151
5192
  "loaded journal run id does not match requested run id"
5152
5193
  );
5153
5194
  }
5154
- if (loaded.run.planDigest !== digest(loaded.plan)) {
5155
- return contextMismatch("loaded plan digest does not match run metadata");
5195
+ let diagnostic;
5196
+ try {
5197
+ if (loaded.run.planDigest !== transitionPlanDigest(loaded.plan)) {
5198
+ return contextMismatch("loaded plan digest does not match run metadata");
5199
+ }
5200
+ diagnostic = validateTransitionRelationalInvariants({
5201
+ kind: "plan",
5202
+ plan: loaded.plan
5203
+ });
5204
+ } catch (error) {
5205
+ return contextMismatch(
5206
+ `loaded plan could not be validated: ${errorDetail(error)}`
5207
+ );
5156
5208
  }
5157
- const diagnostic = validateTransitionRelationalInvariants({
5158
- kind: "plan",
5159
- plan: loaded.plan
5160
- });
5161
5209
  if (!diagnostic.ok) {
5162
5210
  return contextMismatch(
5163
5211
  `loaded plan failed invariant validation: ${diagnostic.detail}`
5164
5212
  );
5165
5213
  }
5166
- const evidenceContext = planEvidenceContext(loaded.plan);
5167
- if (!evidenceContext) {
5214
+ const evidenceContext2 = planEvidenceContext(loaded.plan);
5215
+ if (!evidenceContext2) {
5168
5216
  return contextMismatch("loaded plan contains no durable evidence context");
5169
5217
  }
5170
5218
  const referencedAssumptions = /* @__PURE__ */ new Set();
@@ -5204,7 +5252,14 @@ async function resumeTransitionRun(registry, input) {
5204
5252
  observations: []
5205
5253
  };
5206
5254
  }
5207
- const baseContext = await input.readContext(input.target, loaded.run);
5255
+ let baseContext;
5256
+ try {
5257
+ baseContext = await input.readContext(input.target, loaded.run);
5258
+ } catch (error) {
5259
+ return contextMismatch(
5260
+ `transition run ${runId} target context could not be read: ${errorDetail(error)}`
5261
+ );
5262
+ }
5208
5263
  const runContextMatch = matchRunObservationContext({
5209
5264
  run: loaded.run,
5210
5265
  actual: baseContext
@@ -5214,7 +5269,7 @@ async function resumeTransitionRun(registry, input) {
5214
5269
  }
5215
5270
  const evidenceIdentityMatch = matchObservationContextIdentity({
5216
5271
  expected: baseContext,
5217
- actual: evidenceContext,
5272
+ actual: evidenceContext2,
5218
5273
  label: "loaded plan evidence context"
5219
5274
  });
5220
5275
  if (!evidenceIdentityMatch.ok) {
@@ -5228,24 +5283,35 @@ async function resumeTransitionRun(registry, input) {
5228
5283
  if (!effectsResolution.ok) {
5229
5284
  return contextMismatch(effectsResolution.detail);
5230
5285
  }
5231
- operationEffectsByRef.set(
5232
- step.operation.ref,
5233
- effectsResolution.semantics.effectsOf(step.operation, baseContext)
5286
+ try {
5287
+ operationEffectsByRef.set(
5288
+ step.operation.ref,
5289
+ effectsResolution.semantics.effectsOf(step.operation, baseContext)
5290
+ );
5291
+ } catch (error) {
5292
+ return contextMismatch(
5293
+ `operation ${step.operation.ref} effects could not be resolved: ${errorDetail(error)}`
5294
+ );
5295
+ }
5296
+ }
5297
+ let semanticDiagnostic;
5298
+ try {
5299
+ semanticDiagnostic = validateTransitionRelationalInvariants({
5300
+ kind: "plan",
5301
+ plan: loaded.plan,
5302
+ operationEffectsByRef
5303
+ });
5304
+ } catch (error) {
5305
+ return contextMismatch(
5306
+ `loaded plan could not be semantically validated: ${errorDetail(error)}`
5234
5307
  );
5235
5308
  }
5236
- const semanticDiagnostic = validateTransitionRelationalInvariants({
5237
- kind: "plan",
5238
- plan: loaded.plan,
5239
- operationEffectsByRef
5240
- });
5241
5309
  if (!semanticDiagnostic.ok) {
5242
5310
  return contextMismatch(
5243
5311
  `loaded plan failed semantic invariant validation: ${semanticDiagnostic.detail}`
5244
5312
  );
5245
5313
  }
5246
5314
  const orderedSteps = orderedPlanSteps(loaded.plan, stepById);
5247
- const completed = [];
5248
- const observations = [];
5249
5315
  for (const step of orderedSteps) {
5250
5316
  const events = grouped.get(step.stepId);
5251
5317
  if (!events?.intent && !events?.observed) {
@@ -5429,6 +5495,64 @@ async function resumeTransitionRun(registry, input) {
5429
5495
  observations
5430
5496
  };
5431
5497
  }
5498
+ async function resumeTransitionRun(registry, input) {
5499
+ const completed = [];
5500
+ const observations = [];
5501
+ const runId = input.runId;
5502
+ try {
5503
+ return await resumeTransitionRunInternal(
5504
+ registry,
5505
+ input,
5506
+ runId,
5507
+ completed,
5508
+ observations
5509
+ );
5510
+ } catch (error) {
5511
+ return contextMismatch(
5512
+ `transition run ${runId} could not be resumed: ${errorDetail(error)}`,
5513
+ completed,
5514
+ observations
5515
+ );
5516
+ }
5517
+ }
5518
+
5519
+ // src/transition/run-metadata.ts
5520
+ import { randomUUID } from "crypto";
5521
+ var TRANSITION_PLANNER_ARTIFACT = {
5522
+ id: semanticArtifactId("dbsp.core.transition.planner"),
5523
+ version: "0.1.0"
5524
+ };
5525
+ function evidenceContext(observations) {
5526
+ const evidence = observations.filter(
5527
+ (observation) => observation.role === "evidence"
5528
+ );
5529
+ const first = evidence[0]?.context;
5530
+ if (!first) {
5531
+ throw new Error(
5532
+ "internal error: minted proven plan has no evidence observation context"
5533
+ );
5534
+ }
5535
+ const expected = stableJson(first);
5536
+ for (const observation of evidence.slice(1)) {
5537
+ if (stableJson(observation.context) !== expected) {
5538
+ throw new Error(
5539
+ "internal error: minted proven plan evidence observations do not share one context"
5540
+ );
5541
+ }
5542
+ }
5543
+ return first;
5544
+ }
5545
+ function createTransitionRunMetadata(plan2) {
5546
+ const context = evidenceContext(plan2.observations);
5547
+ return {
5548
+ runId: `dbsp-${randomUUID()}`,
5549
+ planDigest: transitionPlanDigest(plan2),
5550
+ targetContextDigest: observationContextDigest(context),
5551
+ databaseId: context.databaseId,
5552
+ coreVersion: TRANSITION_PLANNER_ARTIFACT.version,
5553
+ startedAt: (/* @__PURE__ */ new Date()).toISOString()
5554
+ };
5555
+ }
5432
5556
 
5433
5557
  // src/transition/applier.ts
5434
5558
  var APPLIER_ARTIFACT = {
@@ -5452,6 +5576,13 @@ function completedAssessment2(proven) {
5452
5576
  continuation: "none"
5453
5577
  };
5454
5578
  }
5579
+ function transitionLessorRejectionResult() {
5580
+ return {
5581
+ assessment: transitionLessorRejectionAssessment(APPLIER_ARTIFACT),
5582
+ journals: [],
5583
+ observations: []
5584
+ };
5585
+ }
5455
5586
  function primaryClaimFromPlan(plan2) {
5456
5587
  return plan2.claims[0]?.id ?? plan2.steps.flatMap((step) => [...step.requiredClaims])[0] ?? claimId("dbsp.transition.claim.plan");
5457
5588
  }
@@ -5479,9 +5610,6 @@ function evidenceIds2(observations) {
5479
5610
  function recordedAt2() {
5480
5611
  return (/* @__PURE__ */ new Date()).toISOString();
5481
5612
  }
5482
- function digest2(value) {
5483
- return createHash3("sha256").update(stableJson(value)).digest("hex");
5484
- }
5485
5613
  function lockTimeoutMs(runtime, step, context) {
5486
5614
  const effects = runtime.effectsOf(step.operation, context);
5487
5615
  const waits = effects.effects.locks.flatMap(
@@ -5489,35 +5617,9 @@ function lockTimeoutMs(runtime, step, context) {
5489
5617
  );
5490
5618
  return waits.length > 0 ? Math.max(...waits) : 5e3;
5491
5619
  }
5492
- function contextFromPlanObservations(observations) {
5493
- const evidence = observations.filter(
5494
- (observation) => observation.role === "evidence"
5495
- );
5496
- const first = evidence[0]?.context;
5497
- if (!first) {
5498
- return void 0;
5499
- }
5500
- const expected = stableJson(first);
5501
- for (const observation of evidence.slice(1)) {
5502
- if (stableJson(observation.context) !== expected) {
5503
- return void 0;
5504
- }
5505
- }
5506
- return first;
5507
- }
5508
5620
  function fingerprintMatches2(expected, actual) {
5509
5621
  return expected.digest === actual.digest;
5510
5622
  }
5511
- function transitionRunMetadata(plan2, context) {
5512
- return {
5513
- runId: `dbsp-${randomUUID()}`,
5514
- planDigest: digest2(plan2),
5515
- targetContextDigest: digest2(context),
5516
- databaseId: context.databaseId,
5517
- coreVersion: APPLIER_ARTIFACT.version,
5518
- startedAt: recordedAt2()
5519
- };
5520
- }
5521
5623
  function intentRecord(step, run) {
5522
5624
  return {
5523
5625
  runId: run.runId,
@@ -5635,6 +5737,22 @@ function invalidPlanResult(detail) {
5635
5737
  observations: []
5636
5738
  };
5637
5739
  }
5740
+ function persistenceFailureResult(run, error) {
5741
+ return {
5742
+ assessment: assessment2({
5743
+ code: "persistence-failed",
5744
+ artifact: APPLIER_ARTIFACT,
5745
+ fact: {
5746
+ key: "transition-run-id",
5747
+ value: run.runId
5748
+ },
5749
+ detail: `transition-run persistence is indeterminate; load run ${run.runId} before retrying: ${errorDetail2(error)}`,
5750
+ scope: []
5751
+ }),
5752
+ journals: [],
5753
+ observations: []
5754
+ };
5755
+ }
5638
5756
  function unknownJournal(intent) {
5639
5757
  return {
5640
5758
  intent,
@@ -5689,11 +5807,11 @@ var CommitOutcomeUncertainError = class extends Error {
5689
5807
  this.originalError = error;
5690
5808
  }
5691
5809
  };
5692
- function errorDetail(error) {
5810
+ function errorDetail2(error) {
5693
5811
  return error instanceof Error ? error.message : "unknown error";
5694
5812
  }
5695
5813
  function withJournalWriteWarning(assessmentValue, error) {
5696
- const detail = `observed journal write failed after outcome was decided: ${errorDetail(error)}`;
5814
+ const detail = `observed journal write failed after outcome was decided: ${errorDetail2(error)}`;
5697
5815
  return {
5698
5816
  ...assessmentValue,
5699
5817
  reasons: assessmentValue.reasons.map(
@@ -5734,7 +5852,7 @@ function durableEvidence(observations) {
5734
5852
  (observation) => observation.role === "evidence"
5735
5853
  );
5736
5854
  }
5737
- function createApplier(registry) {
5855
+ function createApplier(registry, persister) {
5738
5856
  return {
5739
5857
  artifact: APPLIER_ARTIFACT,
5740
5858
  async apply(proven, policy, target) {
@@ -5742,7 +5860,16 @@ function createApplier(registry) {
5742
5860
  if (!isMintedInProcessPlan(plan2)) {
5743
5861
  return invalidPlanResult(UNMINTED_PLAN_DETAIL);
5744
5862
  }
5745
- if (plan2.steps.length === 0 || plan2.segments.length === 0) {
5863
+ if (plan2.segments.length === 0) {
5864
+ if (!isTransitionLessor(target)) {
5865
+ return transitionLessorRejectionResult();
5866
+ }
5867
+ return uncomposablePlanResult(
5868
+ plan2,
5869
+ "plan contains no executable steps"
5870
+ );
5871
+ }
5872
+ if (plan2.steps.length === 0) {
5746
5873
  return uncomposablePlanResult(
5747
5874
  plan2,
5748
5875
  "plan contains no executable steps"
@@ -5758,10 +5885,12 @@ function createApplier(registry) {
5758
5885
  );
5759
5886
  }
5760
5887
  const reportedAssessment = derivedApplicableAssessment(plan2);
5761
- const proofContext = contextFromPlanObservations(plan2.observations);
5888
+ const proofContext = plan2.observations.find(
5889
+ (observation) => observation.role === "evidence"
5890
+ )?.context;
5762
5891
  if (!proofContext) {
5763
5892
  throw new Error(
5764
- "internal error: minted proven plan evidence observations do not share one context"
5893
+ "internal error: minted proven plan has no evidence observation context"
5765
5894
  );
5766
5895
  }
5767
5896
  const assumptionById = new Map(
@@ -5782,7 +5911,7 @@ function createApplier(registry) {
5782
5911
  return unacceptedAssumptionResult(plan2, assumption);
5783
5912
  }
5784
5913
  }
5785
- const run = transitionRunMetadata(plan2, proofContext);
5914
+ const run = createTransitionRunMetadata(plan2);
5786
5915
  const operationEffectsByRef = /* @__PURE__ */ new Map();
5787
5916
  for (const step of plan2.steps) {
5788
5917
  const operationResolution = registry.resolveOperation(step.operation);
@@ -5852,8 +5981,8 @@ function createApplier(registry) {
5852
5981
  journalWriteFailure ?? params.result
5853
5982
  );
5854
5983
  };
5855
- let runtimeContext = proofContext;
5856
5984
  const stepById = new Map(plan2.steps.map((step) => [step.stepId, step]));
5985
+ const preflightSegments = [];
5857
5986
  for (const segment of plan2.segments) {
5858
5987
  const segmentSteps = segment.stepIds.map(
5859
5988
  (stepId2) => stepById.get(stepId2)
@@ -5979,6 +6108,29 @@ function createApplier(registry) {
5979
6108
  )
5980
6109
  );
5981
6110
  }
6111
+ preflightSegments.push({
6112
+ segment,
6113
+ resolvedSteps,
6114
+ first,
6115
+ transactional,
6116
+ segmentCoordinator
6117
+ });
6118
+ }
6119
+ if (!isTransitionLessor(target)) {
6120
+ return resultWithCommittedJournals(transitionLessorRejectionResult());
6121
+ }
6122
+ try {
6123
+ await persister.persist(run, plan2);
6124
+ } catch (error) {
6125
+ return persistenceFailureResult(run, error);
6126
+ }
6127
+ let runtimeContext = proofContext;
6128
+ for (const {
6129
+ resolvedSteps,
6130
+ first,
6131
+ transactional,
6132
+ segmentCoordinator
6133
+ } of preflightSegments) {
5982
6134
  let client;
5983
6135
  let lease;
5984
6136
  let committed = false;
@@ -5989,13 +6141,6 @@ function createApplier(registry) {
5989
6141
  let activeOperationAttempted = false;
5990
6142
  let activeNonRollbackableOperationExecuted = false;
5991
6143
  const completedInSegment = [];
5992
- if (!isTransitionLessor(target)) {
5993
- return resultWithCommittedJournals({
5994
- assessment: transitionLessorRejectionAssessment(APPLIER_ARTIFACT),
5995
- journals: [],
5996
- observations: []
5997
- });
5998
- }
5999
6144
  try {
6000
6145
  lease = await acquireTransitionLease(target);
6001
6146
  client = { opaqueClient: lease.session };
@@ -6478,7 +6623,7 @@ function createApplier(registry) {
6478
6623
  operationKind: entry.step.operation.operationKind,
6479
6624
  operationRef: entry.step.operation.ref,
6480
6625
  scope: [],
6481
- detail: `after-commit expectedAfter observation failed after commit: ${errorDetail(error)}`
6626
+ detail: `after-commit expectedAfter observation failed after commit: ${errorDetail2(error)}`
6482
6627
  },
6483
6628
  "outcome-unknown",
6484
6629
  "human-intervention-required"
@@ -6563,7 +6708,7 @@ function createApplier(registry) {
6563
6708
  operationKind: active.step.operation.operationKind,
6564
6709
  operationRef: active.step.operation.ref,
6565
6710
  scope: [],
6566
- detail: `commit outcome uncertain after commit failure: ${errorDetail(error.originalError)}`
6711
+ detail: `commit outcome uncertain after commit failure: ${errorDetail2(error.originalError)}`
6567
6712
  },
6568
6713
  "outcome-unknown",
6569
6714
  "human-intervention-required"
@@ -9125,6 +9270,106 @@ function createEvidenceView(params) {
9125
9270
  return new CoreEvidenceView(params.evidence, params.context, params.requests);
9126
9271
  }
9127
9272
 
9273
+ // src/transition/execution-contract.ts
9274
+ import { EXECUTION_CONTRACT_VERSION } from "@dbsp/types";
9275
+ function canonicalRequirements(requirements) {
9276
+ const ordered = [...requirements].sort((left, right) => {
9277
+ const leftJson = stableJson(left);
9278
+ const rightJson = stableJson(right);
9279
+ return leftJson < rightJson ? -1 : leftJson > rightJson ? 1 : 0;
9280
+ });
9281
+ return ordered.filter(
9282
+ (requirement, index) => index === 0 || stableJson(requirement) !== stableJson(ordered[index - 1])
9283
+ );
9284
+ }
9285
+ function compareNamespaceIdentity(left, right) {
9286
+ return left.name < right.name ? -1 : left.name > right.name ? 1 : left.oid < right.oid ? -1 : left.oid > right.oid ? 1 : 0;
9287
+ }
9288
+ function canonicalNamespaces(namespaces) {
9289
+ const ordered = [...namespaces].sort(compareNamespaceIdentity);
9290
+ return ordered.filter(
9291
+ (namespace, index) => index === 0 || compareNamespaceIdentity(namespace, ordered[index - 1] ?? namespace) !== 0
9292
+ );
9293
+ }
9294
+ function createExecutionContract(requirements) {
9295
+ return {
9296
+ version: EXECUTION_CONTRACT_VERSION,
9297
+ requirements: canonicalRequirements(requirements)
9298
+ };
9299
+ }
9300
+ function nonEmpty(value) {
9301
+ return typeof value === "string" && value.length > 0;
9302
+ }
9303
+ function validRequirement(requirement) {
9304
+ if (requirement === null || typeof requirement !== "object" || Array.isArray(requirement))
9305
+ return false;
9306
+ const value = requirement;
9307
+ if (!nonEmpty(value.kind) || !["must-match", "set-and-verify", "must-satisfy", "provenance"].includes(
9308
+ value.mode
9309
+ ))
9310
+ return false;
9311
+ switch (value.kind) {
9312
+ case "postgresql.physical-target": {
9313
+ if (!(value.mode === "must-match" && nonEmpty(value.systemIdentifier) && nonEmpty(value.databaseOid) && Array.isArray(value.namespaces) && value.namespaces.length > 0 && value.namespaces.every(
9314
+ (item) => item !== null && typeof item === "object" && nonEmpty(item.name) && nonEmpty(item.oid)
9315
+ )))
9316
+ return false;
9317
+ const namespaces = value.namespaces;
9318
+ return stableJson(namespaces) === stableJson(canonicalNamespaces(namespaces)) && new Set(namespaces.map((namespace) => namespace.name)).size === namespaces.length;
9319
+ }
9320
+ case "postgresql.engine-version":
9321
+ return value.mode === "must-satisfy" && nonEmpty(value.stepId) && (value.minServerVersionNum === void 0 || Number.isInteger(value.minServerVersionNum)) && (value.maxServerVersionNum === void 0 || Number.isInteger(value.maxServerVersionNum));
9322
+ case "postgresql.authority":
9323
+ return value.mode === "must-satisfy" && ["schema-usage", "table-alter", "type-alter"].includes(
9324
+ value.action
9325
+ ) && nonEmpty(value.schema) && (value.action === "schema-usage" || nonEmpty(value.object));
9326
+ case "postgresql.session-setting":
9327
+ return (value.mode === "set-and-verify" || value.mode === "provenance") && [
9328
+ "standard_conforming_strings",
9329
+ "search_path",
9330
+ "client_encoding",
9331
+ "TimeZone"
9332
+ ].includes(value.setting) && nonEmpty(value.value) && (value.mode !== "set-and-verify" || value.setting === "standard_conforming_strings" && value.value === "on" || value.setting === "client_encoding" && value.value === "UTF8");
9333
+ default:
9334
+ return false;
9335
+ }
9336
+ }
9337
+ function validateExecutionContract(value) {
9338
+ if (value === null || typeof value !== "object" || Array.isArray(value))
9339
+ return {
9340
+ ok: false,
9341
+ detail: "plan is missing an execution contract; re-plan before execution"
9342
+ };
9343
+ const contract = value;
9344
+ if (contract.version !== EXECUTION_CONTRACT_VERSION)
9345
+ return {
9346
+ ok: false,
9347
+ detail: `execution contract version ${String(contract.version)} is unsupported`
9348
+ };
9349
+ if (!Array.isArray(contract.requirements) || contract.requirements.length === 0)
9350
+ return { ok: false, detail: "execution contract has no requirements" };
9351
+ if (!contract.requirements.every(validRequirement))
9352
+ return {
9353
+ ok: false,
9354
+ detail: "execution contract contains an unknown requirement kind or mode"
9355
+ };
9356
+ const canonical = canonicalRequirements(contract.requirements);
9357
+ if (stableJson(canonical) !== stableJson(contract.requirements))
9358
+ return {
9359
+ ok: false,
9360
+ detail: "execution contract requirements are not canonically ordered"
9361
+ };
9362
+ return { ok: true, contract };
9363
+ }
9364
+ function bindExecutionContract(plan2, contract) {
9365
+ const validation = validateExecutionContract(contract);
9366
+ if (!validation.ok) throw new Error(validation.detail);
9367
+ return mintInProcessPlan({
9368
+ ...plan2,
9369
+ executionContract: validation.contract
9370
+ });
9371
+ }
9372
+
9128
9373
  // src/transition/composer.ts
9129
9374
  function sameResource4(left, right) {
9130
9375
  return stableJson(left) === stableJson(right);
@@ -9821,6 +10066,14 @@ var ObservationContextMismatchError = class extends Error {
9821
10066
  this.name = "ObservationContextMismatchError";
9822
10067
  }
9823
10068
  };
10069
+ function verifyCapturedTargetIdentity(expected, actual) {
10070
+ if (expected.postgresqlTargetIdentity === void 0) return;
10071
+ if (stableJson(expected.postgresqlTargetIdentity) !== stableJson(actual.postgresqlTargetIdentity)) {
10072
+ throw new ObservationContextMismatchError(
10073
+ "proof observation target identity does not match the target captured before evidence collection"
10074
+ );
10075
+ }
10076
+ }
9824
10077
  function validateIssuedObservation(observation, context) {
9825
10078
  const contextMatch = matchLiveObservationContext({
9826
10079
  expected: context,
@@ -10552,6 +10805,7 @@ async function proveTransitions(registry, compare, target, context, initialState
10552
10805
  requiredObservations
10553
10806
  ) : context
10554
10807
  );
10808
+ verifyCapturedTargetIdentity(context, candidateContext);
10555
10809
  if (!sharedProofContext) {
10556
10810
  sharedProofContext = candidateContext;
10557
10811
  } else {
@@ -10686,6 +10940,7 @@ async function proveTransitions(registry, compare, target, context, initialState
10686
10940
  lease = await checkoutProofClient(target);
10687
10941
  client = lease.session;
10688
10942
  proofContext = issuer.readContext ? await issuer.readContext(client, context, requiredObservations) : context;
10943
+ verifyCapturedTargetIdentity(context, proofContext);
10689
10944
  proofContext = registry.contextWithDerivedCapabilities(proofContext);
10690
10945
  const supportMismatch = ruleSupportMismatch(rule, proofContext);
10691
10946
  if (supportMismatch) {
@@ -11224,6 +11479,7 @@ async function retryUnknownRecognition(registry, compare, target, context) {
11224
11479
  lease = await checkoutProofClient(target);
11225
11480
  client = lease.session;
11226
11481
  let proofContext2 = issuer.readContext ? await issuer.readContext(client, context, recognitionRequests) : context;
11482
+ verifyCapturedTargetIdentity(context, proofContext2);
11227
11483
  proofContext2 = registry.contextWithDerivedCapabilities(proofContext2);
11228
11484
  const supportMismatch2 = ruleSupportMismatch(rule, proofContext2);
11229
11485
  if (supportMismatch2) {
@@ -11856,10 +12112,10 @@ function candidateProgressKey(entry) {
11856
12112
  function completedJournalRefs(journals) {
11857
12113
  return journals.filter((journal) => journal.outcome === "completed").map((journal) => journal.intent.operation.ref);
11858
12114
  }
11859
- function createStagedTransitionOrchestrator(registry) {
12115
+ function createStagedTransitionOrchestrator(registry, persister) {
11860
12116
  const comparator = createComparator(registry);
11861
12117
  const prover = createProver(registry);
11862
- const applier = createApplier(registry);
12118
+ const applier = createApplier(registry, persister);
11863
12119
  return {
11864
12120
  async applyStagedTransition(input) {
11865
12121
  const journals = [];
@@ -12003,8 +12259,41 @@ function createStagedTransitionOrchestrator(registry) {
12003
12259
 
12004
12260
  // src/adapter.ts
12005
12261
  function supportsExecution(adapter) {
12262
+ const connectionProvider = adapter;
12263
+ if (typeof connectionProvider.getPoolInstance === "function") {
12264
+ try {
12265
+ connectionProvider.getPoolInstance();
12266
+ } catch {
12267
+ return false;
12268
+ }
12269
+ }
12006
12270
  return typeof adapter.execute === "function" && typeof adapter.executeOne === "function" && typeof adapter.executeOneOrThrow === "function";
12007
12271
  }
12272
+ function assertConnectionAvailable(adapter, operation) {
12273
+ const availability = adapter.connectionAvailability;
12274
+ if (availability?.status === "unavailable") {
12275
+ throw new ExecutionError({
12276
+ operation,
12277
+ reason: availability.reason,
12278
+ fix: availability.fix
12279
+ });
12280
+ }
12281
+ }
12282
+ function executeCompiledQuery(adapter, query, operation) {
12283
+ assertConnectionAvailable(adapter, operation);
12284
+ return adapter.execute(query);
12285
+ }
12286
+ function executeCompiledQueryWithMeta(adapter, query, operation) {
12287
+ assertConnectionAvailable(adapter, operation);
12288
+ if (typeof adapter.executeWithMeta !== "function") {
12289
+ throw new ExecutionError({
12290
+ operation,
12291
+ reason: "this adapter does not implement executeWithMeta",
12292
+ fix: "Use an adapter that implements executeWithMeta."
12293
+ });
12294
+ }
12295
+ return adapter.executeWithMeta(query);
12296
+ }
12008
12297
  function supportsStreaming(adapter) {
12009
12298
  return adapter.capabilities?.supportsStreaming === true && "stream" in adapter && typeof adapter.stream === "function";
12010
12299
  }
@@ -12913,7 +13202,7 @@ var CteQueryBuilder = class {
12913
13202
  const intent = this.buildIntent();
12914
13203
  const compileOptions = this.compileOptions();
12915
13204
  const compiled = adapter.compileCteQuery(intent, compileOptions);
12916
- return adapter.execute(compiled);
13205
+ return executeCompiledQuery(adapter, compiled, "withCte().all()");
12917
13206
  }
12918
13207
  /**
12919
13208
  * Alias for all().
@@ -14729,9 +15018,10 @@ var MutationBuilderBase = class {
14729
15018
  async affectedRows() {
14730
15019
  const adapter = this.requireAdapter(this.operationName);
14731
15020
  const intent = this.buildIntent();
15021
+ const operation = `${this.operationName}.affectedRows()`;
14732
15022
  if (typeof adapter.executeWithMeta !== "function") {
14733
15023
  throw new ExecutionError({
14734
- operation: `${this.operationName}.affectedRows`,
15024
+ operation,
14735
15025
  reason: "this adapter does not support affectedRows(); it does not implement executeWithMeta",
14736
15026
  fix: "Use an adapter that implements executeWithMeta, or call execute() when row count metadata is not required."
14737
15027
  });
@@ -14743,18 +15033,18 @@ var MutationBuilderBase = class {
14743
15033
  onHookError: this.onHookError,
14744
15034
  schemaName: this.schemaName,
14745
15035
  inTransaction: this.inTransaction,
14746
- prepare: (preparedIntent) => this.prepareMutationExecution(adapter, preparedIntent)
15036
+ prepare: (preparedIntent) => this.prepareMutationExecution(adapter, preparedIntent, operation)
14747
15037
  });
14748
15038
  if (execution.affectedRows === void 0) {
14749
15039
  throw new ExecutionError({
14750
- operation: `${this.operationName}.affectedRows`,
15040
+ operation,
14751
15041
  reason: "this adapter does not support affectedRows(); it did not return executeWithMeta metadata",
14752
15042
  fix: "Use an adapter that implements executeWithMeta and returns rowCount metadata."
14753
15043
  });
14754
15044
  }
14755
15045
  return execution.affectedRows;
14756
15046
  }
14757
- prepareMutationExecution(adapter, intent) {
15047
+ prepareMutationExecution(adapter, intent, operation = `${this.operationName}()`) {
14758
15048
  const compileOptions = {
14759
15049
  model: this.model,
14760
15050
  ...this.schemaName ? { schemaName: this.schemaName } : {}
@@ -14766,13 +15056,21 @@ var MutationBuilderBase = class {
14766
15056
  parameters: compiled.parameters,
14767
15057
  execute: async () => {
14768
15058
  if (typeof adapter.executeWithMeta === "function") {
14769
- const result2 = await adapter.executeWithMeta(compiled);
15059
+ const result2 = await executeCompiledQueryWithMeta(
15060
+ adapter,
15061
+ compiled,
15062
+ operation
15063
+ );
14770
15064
  return {
14771
15065
  result: result2.rows,
14772
15066
  affectedRows: result2.rowCount
14773
15067
  };
14774
15068
  }
14775
- const result = await adapter.execute(compiled);
15069
+ const result = await executeCompiledQuery(
15070
+ adapter,
15071
+ compiled,
15072
+ operation
15073
+ );
14776
15074
  return {
14777
15075
  result
14778
15076
  };
@@ -14786,13 +15084,17 @@ var MutationBuilderBase = class {
14786
15084
  parameters: compiled.parameters,
14787
15085
  execute: async () => {
14788
15086
  if (typeof adapter.executeWithMeta === "function") {
14789
- const result = await adapter.executeWithMeta(compiled);
15087
+ const result = await executeCompiledQueryWithMeta(
15088
+ adapter,
15089
+ compiled,
15090
+ operation
15091
+ );
14790
15092
  return {
14791
15093
  result: void 0,
14792
15094
  affectedRows: result.rowCount
14793
15095
  };
14794
15096
  }
14795
- await adapter.execute(compiled);
15097
+ await executeCompiledQuery(adapter, compiled, operation);
14796
15098
  return {
14797
15099
  result: void 0
14798
15100
  };
@@ -16629,7 +16931,11 @@ var NqlBuilderImpl = class {
16629
16931
  parameters: compiled.parameters,
16630
16932
  execute: async () => {
16631
16933
  if (typeof adapter.executeWithMeta === "function") {
16632
- const metadata = await adapter.executeWithMeta(compiled);
16934
+ const metadata = await executeCompiledQueryWithMeta(
16935
+ adapter,
16936
+ compiled,
16937
+ "nql().all()"
16938
+ );
16633
16939
  const hookRows2 = metadata.rows;
16634
16940
  const rawRows2 = snapshotMutationRows(hookRows2);
16635
16941
  return {
@@ -16641,7 +16947,11 @@ var NqlBuilderImpl = class {
16641
16947
  affectedRows: metadata.rowCount
16642
16948
  };
16643
16949
  }
16644
- const hookRows = await adapter.execute(compiled);
16950
+ const hookRows = await executeCompiledQuery(
16951
+ adapter,
16952
+ compiled,
16953
+ "nql().all()"
16954
+ );
16645
16955
  const rawRows = snapshotMutationRows(hookRows);
16646
16956
  return {
16647
16957
  result: {
@@ -16663,6 +16973,7 @@ var NqlBuilderImpl = class {
16663
16973
  });
16664
16974
  }
16665
16975
  async executeNqlProgramSequence(compiledIntent, adapter) {
16976
+ assertConnectionAvailable(adapter, "nql().all()");
16666
16977
  if (!supportsTransactions(adapter)) {
16667
16978
  throw new ExecutionError({
16668
16979
  operation: "nql",
@@ -16926,6 +17237,7 @@ var NqlBuilderImpl = class {
16926
17237
  if (hasExecutableNqlProgramSequence(compiledIntent.bundle)) {
16927
17238
  return this.executeNqlProgramSequence(compiledIntent, adapter);
16928
17239
  }
17240
+ assertConnectionAvailable(adapter, "nql().all()");
16929
17241
  if (compiledIntent.kind === "mutation") {
16930
17242
  return (await this.runMutationStatement(
16931
17243
  adapter,
@@ -16948,7 +17260,7 @@ var NqlBuilderImpl = class {
16948
17260
  ),
16949
17261
  this.nqlBundleCompileOptions()
16950
17262
  );
16951
- const rows = await adapter.execute(compiled2);
17263
+ const rows = await executeCompiledQuery(adapter, compiled2, "nql().all()");
16952
17264
  if (bindingFinalPlanHasJsonAggIncludes(planReport2)) {
16953
17265
  hydrateJsonAggIncludes(
16954
17266
  rows,
@@ -16961,7 +17273,7 @@ var NqlBuilderImpl = class {
16961
17273
  const planReport = this.planInternal();
16962
17274
  const finalBundle = this.createFinalNqlStatementBundle(compiledIntent);
16963
17275
  const compiled = hasNqlBindings(finalBundle) ? adapter.compile(finalBundle, this.nqlBundleCompileOptions()) : adapter.compile(planReport);
16964
- return adapter.execute(compiled);
17276
+ return executeCompiledQuery(adapter, compiled, "nql().all()");
16965
17277
  }
16966
17278
  async run() {
16967
17279
  await this.all();
@@ -17461,7 +17773,11 @@ var ResultHydrator = class {
17461
17773
  parentIds,
17462
17774
  compileOptions
17463
17775
  );
17464
- const childResults = await adapter.execute(includeQuery);
17776
+ const childResults = await executeCompiledQuery(
17777
+ adapter,
17778
+ includeQuery,
17779
+ "all()"
17780
+ );
17465
17781
  const childrenByParentId = /* @__PURE__ */ new Map();
17466
17782
  for (const child of childResults) {
17467
17783
  const parentId = this.extractKeyValue(
@@ -17617,7 +17933,11 @@ var ResultHydrator = class {
17617
17933
  this.model,
17618
17934
  compileOptions
17619
17935
  );
17620
- const recursiveRows = await adapter.execute(compiledRecursive);
17936
+ const recursiveRows = await executeCompiledQuery(
17937
+ adapter,
17938
+ compiledRecursive,
17939
+ "all()"
17940
+ );
17621
17941
  this.mergeRecursiveResults(
17622
17942
  results,
17623
17943
  recursiveRows,
@@ -17857,8 +18177,11 @@ var SetOperationBuilderImpl = class _SetOperationBuilderImpl {
17857
18177
  }
17858
18178
  async all() {
17859
18179
  const adapter = this.requireAdapter();
17860
- const compiled = adapter.compileSetOperation(this.intent, this.model);
17861
- return adapter.execute(compiled);
18180
+ const compiled = adapter.compileSetOperation(
18181
+ this.intent,
18182
+ this.model
18183
+ );
18184
+ return executeCompiledQuery(adapter, compiled, "set operation all()");
17862
18185
  }
17863
18186
  async first() {
17864
18187
  const rows = await this.all();
@@ -17938,6 +18261,7 @@ function stream(builder, options) {
17938
18261
  return this;
17939
18262
  },
17940
18263
  async next() {
18264
+ assertConnectionAvailable(adapter, "stream()");
17941
18265
  if (!supportsStreaming(adapter)) {
17942
18266
  throw createUnsupportedStreamingError();
17943
18267
  }
@@ -18535,8 +18859,10 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
18535
18859
  planReport,
18536
18860
  compileOptions
18537
18861
  );
18538
- const mainResults = await adapter.execute(
18539
- compiledWithIncludes.main
18862
+ const mainResults = await executeCompiledQuery(
18863
+ adapter,
18864
+ compiledWithIncludes.main,
18865
+ "all()"
18540
18866
  );
18541
18867
  const hydrator = new ResultHydrator(
18542
18868
  this.ctx.model,
@@ -18709,7 +19035,7 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
18709
19035
  compileOptions.schemaName = this.ctx.schemaName;
18710
19036
  }
18711
19037
  const compiled = adapter.compile(planReport, compileOptions);
18712
- const rows = await adapter.execute(compiled);
19038
+ const rows = await executeCompiledQuery(adapter, compiled, "exists()");
18713
19039
  return rows.length > 0 && rows[0].exists === true;
18714
19040
  }
18715
19041
  async existsWithHooks(adapter, store) {
@@ -18765,7 +19091,7 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
18765
19091
  compileOptions.schemaName = this.ctx.schemaName;
18766
19092
  }
18767
19093
  const compiled = adapter.compile(planReport, compileOptions);
18768
- const rows = await adapter.execute(compiled);
19094
+ const rows = await executeCompiledQuery(adapter, compiled, "exists()");
18769
19095
  const result = rows.length > 0 && rows[0].exists === true;
18770
19096
  const afterCtx = {
18771
19097
  table: this.from,
@@ -19049,8 +19375,10 @@ var QueryBuilderImpl = class _QueryBuilderImpl {
19049
19375
  );
19050
19376
  let mainResults;
19051
19377
  try {
19052
- mainResults = await adapter.execute(
19053
- compiledWithIncludes.main
19378
+ mainResults = await executeCompiledQuery(
19379
+ adapter,
19380
+ compiledWithIncludes.main,
19381
+ "all()"
19054
19382
  );
19055
19383
  } catch (error) {
19056
19384
  if (store.onError.length > 0) {
@@ -19468,7 +19796,7 @@ var RawCteQueryBuilder = class {
19468
19796
  ...this.model !== void 0 && { model: this.model }
19469
19797
  } : void 0;
19470
19798
  const compiled = adapter.compileCteQuery(intent, compileOptions);
19471
- return adapter.execute(compiled);
19799
+ return executeCompiledQuery(adapter, compiled, "recursive().all()");
19472
19800
  }
19473
19801
  /**
19474
19802
  * Alias for `all()`.
@@ -20055,6 +20383,11 @@ function createOrmInstance(model, strictMode, relationHints, adapter, schemaName
20055
20383
  )
20056
20384
  );
20057
20385
  }
20386
+ try {
20387
+ assertConnectionAvailable(adapter, "transaction()");
20388
+ } catch (error) {
20389
+ return Promise.reject(error);
20390
+ }
20058
20391
  if (!supportsTransactions(adapter)) {
20059
20392
  return Promise.reject(createUnsupportedTransactionError());
20060
20393
  }
@@ -20097,6 +20430,11 @@ function createOrmInstance(model, strictMode, relationHints, adapter, schemaName
20097
20430
  )
20098
20431
  );
20099
20432
  }
20433
+ try {
20434
+ assertConnectionAvailable(adapter, "withPinnedConnection()");
20435
+ } catch (error) {
20436
+ return Promise.reject(error);
20437
+ }
20100
20438
  if (!supportsPinnedConnections(adapter)) {
20101
20439
  return Promise.reject(createUnsupportedPinnedConnectionError());
20102
20440
  }
@@ -21793,6 +22131,7 @@ export {
21793
22131
  array,
21794
22132
  arrayAgg,
21795
22133
  assertCapability,
22134
+ assertConnectionAvailable,
21796
22135
  assertContains,
21797
22136
  assertDbColumnExists,
21798
22137
  assertDbOutput,
@@ -21818,8 +22157,10 @@ export {
21818
22157
  assertSQLTable,
21819
22158
  assertSuccess,
21820
22159
  assertTypeSupported,
22160
+ assumptionAccepted,
21821
22161
  assumptionId,
21822
22162
  batchValues,
22163
+ bindExecutionContract,
21823
22164
  capitalize,
21824
22165
  caseWhen,
21825
22166
  cast,
@@ -21833,11 +22174,13 @@ export {
21833
22174
  composeBeforeMutationHooks,
21834
22175
  composeBeforeQueryHooks,
21835
22176
  composeOnErrorHooks,
22177
+ concludeEvidenceForObligation,
21836
22178
  countDistinctRelationPathsByName,
21837
22179
  createApplier,
21838
22180
  createComparator,
21839
22181
  createDialectCapabilities,
21840
22182
  createEvidenceView,
22183
+ createExecutionContract,
21841
22184
  createHookManager,
21842
22185
  createNqlTag,
21843
22186
  createOrm,
@@ -21848,6 +22191,7 @@ export {
21848
22191
  createRecursiveMetadata,
21849
22192
  createStagedTransitionOrchestrator,
21850
22193
  createTransitionLessor,
22194
+ createTransitionRunMetadata,
21851
22195
  decapitalize,
21852
22196
  defaultIndexName,
21853
22197
  defaultLogger,
@@ -21956,6 +22300,7 @@ export {
21956
22300
  notExists,
21957
22301
  nqlRaw,
21958
22302
  objectToWhereIntent,
22303
+ observationContextDigest,
21959
22304
  op,
21960
22305
  or,
21961
22306
  outerRef,
@@ -22012,8 +22357,10 @@ export {
22012
22357
  supportsStreaming,
22013
22358
  supportsTransactions,
22014
22359
  textScore,
22360
+ transitionPlanDigest,
22015
22361
  unary,
22016
22362
  validateAssertionBlocks,
22363
+ validateExecutionContract,
22017
22364
  validateRecursiveInclude,
22018
22365
  validateRecursiveShape,
22019
22366
  validateSchemaIndexOptions,