@nicia-ai/typegraph 0.66.1 → 0.67.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +15 -0
  3. package/dist/backend/drizzle/engine/index.cjs +6 -6
  4. package/dist/backend/drizzle/engine/index.js +1 -1
  5. package/dist/backend/postgres/index.cjs +15 -15
  6. package/dist/backend/postgres/index.js +1 -1
  7. package/dist/backend/postgres/pglite-store.cjs +1 -1
  8. package/dist/backend/postgres/pglite-store.js +1 -1
  9. package/dist/backend/postgres/pglite.cjs +3 -3
  10. package/dist/backend/postgres/pglite.js +2 -2
  11. package/dist/backend/sqlite/index.cjs +14 -14
  12. package/dist/backend/sqlite/index.js +1 -1
  13. package/dist/backend/sqlite/libsql.cjs +6 -6
  14. package/dist/backend/sqlite/libsql.js +1 -1
  15. package/dist/backend/sqlite/local-store.cjs +1 -1
  16. package/dist/backend/sqlite/local-store.js +1 -1
  17. package/dist/backend/sqlite/local.cjs +3 -3
  18. package/dist/backend/sqlite/local.js +2 -2
  19. package/dist/{chunk-TR7QTODF.js → chunk-55DGFZYC.js} +3 -3
  20. package/dist/{chunk-TR7QTODF.js.map → chunk-55DGFZYC.js.map} +1 -1
  21. package/dist/{chunk-WB55URNM.js → chunk-6CKSTY2D.js} +7 -3
  22. package/dist/chunk-6CKSTY2D.js.map +1 -0
  23. package/dist/{chunk-ZONDBN6H.js → chunk-6DY26DPE.js} +3 -3
  24. package/dist/{chunk-ZONDBN6H.js.map → chunk-6DY26DPE.js.map} +1 -1
  25. package/dist/{chunk-YHMXF7OZ.js → chunk-6J3VUPI6.js} +3 -3
  26. package/dist/{chunk-YHMXF7OZ.js.map → chunk-6J3VUPI6.js.map} +1 -1
  27. package/dist/{chunk-MN325MG4.cjs → chunk-7MQL7FEN.cjs} +11 -2
  28. package/dist/chunk-7MQL7FEN.cjs.map +1 -0
  29. package/dist/{chunk-WWSYGZED.cjs → chunk-E6XL3KMZ.cjs} +6 -6
  30. package/dist/{chunk-WWSYGZED.cjs.map → chunk-E6XL3KMZ.cjs.map} +1 -1
  31. package/dist/{chunk-SSDHR2C2.js → chunk-EFSXK3DW.js} +3 -3
  32. package/dist/{chunk-SSDHR2C2.js.map → chunk-EFSXK3DW.js.map} +1 -1
  33. package/dist/{chunk-2DJKHZQ4.js → chunk-EORFWD6O.js} +11 -2
  34. package/dist/chunk-EORFWD6O.js.map +1 -0
  35. package/dist/{chunk-Y4X6ODVY.cjs → chunk-FS2GVO2T.cjs} +56 -56
  36. package/dist/{chunk-Y4X6ODVY.cjs.map → chunk-FS2GVO2T.cjs.map} +1 -1
  37. package/dist/{chunk-K7YACILO.cjs → chunk-GXHEGNI6.cjs} +9 -9
  38. package/dist/{chunk-K7YACILO.cjs.map → chunk-GXHEGNI6.cjs.map} +1 -1
  39. package/dist/{chunk-BX42F7IJ.cjs → chunk-SSDLQPMY.cjs} +7 -2
  40. package/dist/chunk-SSDLQPMY.cjs.map +1 -0
  41. package/dist/{chunk-ANMCKLZQ.cjs → chunk-ZRIXU33S.cjs} +12 -12
  42. package/dist/{chunk-ANMCKLZQ.cjs.map → chunk-ZRIXU33S.cjs.map} +1 -1
  43. package/dist/graph-merge/index.cjs +500 -26
  44. package/dist/graph-merge/index.cjs.map +1 -1
  45. package/dist/graph-merge/index.d.cts +384 -2
  46. package/dist/graph-merge/index.d.ts +384 -2
  47. package/dist/graph-merge/index.js +485 -15
  48. package/dist/graph-merge/index.js.map +1 -1
  49. package/dist/index.cjs +63 -63
  50. package/dist/index.js +3 -3
  51. package/dist/{local-store-impl-ERPNYOCL.js → local-store-impl-CCMWTLGY.js} +6 -6
  52. package/dist/{local-store-impl-ERPNYOCL.js.map → local-store-impl-CCMWTLGY.js.map} +1 -1
  53. package/dist/{local-store-impl-OUINMCYI.cjs → local-store-impl-GI6S4J6B.cjs} +9 -9
  54. package/dist/{local-store-impl-OUINMCYI.cjs.map → local-store-impl-GI6S4J6B.cjs.map} +1 -1
  55. package/dist/{pglite-store-impl-Z3SOOP75.cjs → pglite-store-impl-HAPAJUIO.cjs} +9 -9
  56. package/dist/{pglite-store-impl-Z3SOOP75.cjs.map → pglite-store-impl-HAPAJUIO.cjs.map} +1 -1
  57. package/dist/{pglite-store-impl-DUJBQZS5.js → pglite-store-impl-HDLFOZWS.js} +6 -6
  58. package/dist/{pglite-store-impl-DUJBQZS5.js.map → pglite-store-impl-HDLFOZWS.js.map} +1 -1
  59. package/dist/schema/index.cjs +27 -27
  60. package/dist/schema/index.js +2 -2
  61. package/package.json +1 -1
  62. package/dist/chunk-2DJKHZQ4.js.map +0 -1
  63. package/dist/chunk-BX42F7IJ.cjs.map +0 -1
  64. package/dist/chunk-MN325MG4.cjs.map +0 -1
  65. package/dist/chunk-WB55URNM.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { NODE_TEMPORAL_READ_NAMES, CURRENT_ONLY_READ_NAMES, createStoreWithSchema, createStore, IDENTITY_READ_NAMES, EDGE_TEMPORAL_READ_NAMES } from '../chunk-TR7QTODF.js';
1
+ import { NODE_TEMPORAL_READ_NAMES, CURRENT_ONLY_READ_NAMES, createStoreWithSchema, createStore, IDENTITY_READ_NAMES, EDGE_TEMPORAL_READ_NAMES } from '../chunk-55DGFZYC.js';
2
2
  import '../chunk-OF63ZHSD.js';
3
3
  import { requireLineage } from '../chunk-NSOGBYYS.js';
4
4
  import '../chunk-XELE7ZOX.js';
@@ -9,7 +9,7 @@ import '../chunk-6ECNYQDW.js';
9
9
  import '../chunk-CDKNGCE7.js';
10
10
  import '../chunk-N535PWMA.js';
11
11
  import '../chunk-UFKYMQJ5.js';
12
- import { computeSchemaHash, serializeSchema } from '../chunk-WB55URNM.js';
12
+ import { computeSchemaHash, serializeSchema, getGraphDefinitionHash } from '../chunk-6CKSTY2D.js';
13
13
  import '../chunk-RFMSZ7CF.js';
14
14
  import { defineNode } from '../chunk-Q2SPDS3B.js';
15
15
  import { normalizeIdentityPair, identityReferenceKey, identityValidityWindowsOverlap, identityAssertionSemanticKey } from '../chunk-SYXTWPX6.js';
@@ -2083,6 +2083,14 @@ async function lineageDeltaSinceAnchor(baseStore, base) {
2083
2083
 
2084
2084
  // src/graph-merge/working-copy.ts
2085
2085
  var CLONE_IMPORT_BATCH_SIZE = 1e3;
2086
+ async function assertWorkingCopyMatchesBase(workingCopy, base) {
2087
+ const workingCopyVersion = await computeBaseVersion(workingCopy);
2088
+ if (workingCopyVersion === base) return;
2089
+ throw new BranchError(
2090
+ "Working copy does not match its base: computeBaseVersion disagrees between the host-created store and the base store it was forked from. The host may have forked a different revision or the source may have advanced while the fork was being allocated.",
2091
+ { details: { workingCopyVersion, baseVersion: base } }
2092
+ );
2093
+ }
2086
2094
  function cloneWorkingCopyStrategy(makeBackend) {
2087
2095
  return cloneWorkingCopyWithGraphStrategy(
2088
2096
  makeBackend,
@@ -2235,13 +2243,7 @@ function forkedWorkingCopyStrategy(options) {
2235
2243
  );
2236
2244
  }
2237
2245
  const forkStore = createStore(baseStore.graph, backend, forkOptions);
2238
- const forkVersion = await computeBaseVersion(forkStore);
2239
- if (forkVersion !== base) {
2240
- throw new BranchError(
2241
- "Fork does not match its base: computeBaseVersion disagrees between the forked store and the base store it was forked from. This fence proves base-token equality (schema plus a revision anchor, or a live-content fingerprint) at the instant the fork was taken, not byte-for-byte physical identity \u2014 a working-copy fork is trusted to provide that.",
2242
- { details: { forkVersion, baseVersion: base } }
2243
- );
2244
- }
2246
+ await assertWorkingCopyMatchesBase(forkStore, base);
2245
2247
  return forkStore;
2246
2248
  } catch (error) {
2247
2249
  try {
@@ -2304,11 +2306,7 @@ async function branch(baseStore, makeBackend, options, strategy) {
2304
2306
  }
2305
2307
  async function captureBranchForkState(store) {
2306
2308
  try {
2307
- const schemaRow = await storeBackend(store).getActiveSchema(store.graphId);
2308
- const schemaAnchor = schemaRow === void 0 ? void 0 : { version: schemaRow.version, hash: schemaRow.schema_hash };
2309
- const lineage = resolveLineage(store);
2310
- const forkRevision = lineage === void 0 ? void 0 : await lineage.revision(storeBackend(store));
2311
- return { schemaAnchor, forkRevision };
2309
+ return await readBranchForkState(store);
2312
2310
  } catch (error) {
2313
2311
  try {
2314
2312
  await storeBackend(store).close();
@@ -2317,6 +2315,13 @@ async function captureBranchForkState(store) {
2317
2315
  throw error;
2318
2316
  }
2319
2317
  }
2318
+ async function readBranchForkState(store) {
2319
+ const schemaRow = await storeBackend(store).getActiveSchema(store.graphId);
2320
+ const schemaAnchor = schemaRow === void 0 ? void 0 : { version: schemaRow.version, hash: schemaRow.schema_hash };
2321
+ const lineage = resolveLineage(store);
2322
+ const forkRevision = lineage === void 0 ? void 0 : await lineage.revision(storeBackend(store));
2323
+ return { schemaAnchor, forkRevision };
2324
+ }
2320
2325
 
2321
2326
  // src/graph-merge/evolution-target.ts
2322
2327
  function evolutionPlanningTarget(store, plan) {
@@ -10262,6 +10267,471 @@ function asReviewError(error) {
10262
10267
  );
10263
10268
  }
10264
10269
 
10270
+ // src/graph-merge/durable-branch.ts
10271
+ async function branchDurable(baseStore, strategy, options) {
10272
+ let base;
10273
+ let id;
10274
+ try {
10275
+ base = await computeBaseVersion(baseStore);
10276
+ id = options?.id ?? asBranchId(generateId());
10277
+ } catch (error) {
10278
+ return err(
10279
+ new BranchError(
10280
+ "Failed to stamp the durable branch base version for the base store.",
10281
+ { cause: error }
10282
+ )
10283
+ );
10284
+ }
10285
+ let created;
10286
+ try {
10287
+ created = await strategy.create(baseStore, base, id);
10288
+ } catch (error) {
10289
+ return err(
10290
+ new BranchError(
10291
+ "Failed to create durable working-copy branch of base store",
10292
+ { cause: error }
10293
+ )
10294
+ );
10295
+ }
10296
+ let forkState;
10297
+ let definitionHash;
10298
+ try {
10299
+ await assertDurableWorkingCopyMatchesBase(baseStore, created.store, base);
10300
+ forkState = await readBranchForkState(created.store);
10301
+ definitionHash = await getGraphDefinitionHash(created.store.graph);
10302
+ } catch (error) {
10303
+ return err(await abandonAllocation(strategy, created, id, error));
10304
+ }
10305
+ const origin = {
10306
+ graphId: created.store.graphId,
10307
+ definitionHash,
10308
+ branchId: id,
10309
+ base,
10310
+ schemaAnchor: forkState.schemaAnchor,
10311
+ forkRevision: forkState.forkRevision
10312
+ };
10313
+ try {
10314
+ await strategy.seal(created.descriptor, origin);
10315
+ } catch (error) {
10316
+ return err(await abandonAllocation(strategy, created, id, error));
10317
+ }
10318
+ const branch2 = {
10319
+ id,
10320
+ base,
10321
+ store: created.store,
10322
+ close: coalescedDurableClose(created.store, created.access),
10323
+ ...forkState.schemaAnchor === void 0 ? { schemaAnchor: void 0 } : { schemaAnchor: forkState.schemaAnchor },
10324
+ ...forkState.forkRevision === void 0 ? {} : { forkRevision: forkState.forkRevision }
10325
+ };
10326
+ const descriptor = {
10327
+ kind: strategy.type,
10328
+ version: strategy.version,
10329
+ graphId: created.store.graphId,
10330
+ definitionHash,
10331
+ branchId: id,
10332
+ base,
10333
+ store: created.descriptor,
10334
+ ...forkState.schemaAnchor === void 0 ? { schemaAnchor: void 0 } : { schemaAnchor: forkState.schemaAnchor },
10335
+ ...forkState.forkRevision === void 0 ? {} : { forkRevision: forkState.forkRevision }
10336
+ };
10337
+ return ok({ branch: branch2, descriptor });
10338
+ }
10339
+ async function assertDurableWorkingCopyMatchesBase(baseStore, workingCopy, base) {
10340
+ const sourceVersionBeforeDiff = await computeBaseVersion(baseStore);
10341
+ if (sourceVersionBeforeDiff !== base) {
10342
+ throw new BranchError(
10343
+ "Base store changed while the durable working copy was being allocated.",
10344
+ {
10345
+ details: {
10346
+ baseVersion: base,
10347
+ liveBaseVersion: sourceVersionBeforeDiff
10348
+ }
10349
+ }
10350
+ );
10351
+ }
10352
+ if (workingCopy.graphId !== baseStore.graphId) {
10353
+ throw new BranchError(
10354
+ "Durable working copy belongs to a different graph than its base store.",
10355
+ {
10356
+ details: {
10357
+ expectedGraphId: baseStore.graphId,
10358
+ receivedGraphId: workingCopy.graphId
10359
+ }
10360
+ }
10361
+ );
10362
+ }
10363
+ const workingCopyVersion = await computeBaseVersion(workingCopy);
10364
+ if (workingCopyVersion === base) return;
10365
+ if (schemaComponentOf(workingCopyVersion) !== schemaComponentOf(base)) {
10366
+ throw new BranchError(
10367
+ "Durable working copy schema does not match its stamped base schema.",
10368
+ {
10369
+ details: {
10370
+ baseSchema: schemaComponentOf(base),
10371
+ workingCopySchema: schemaComponentOf(workingCopyVersion)
10372
+ }
10373
+ }
10374
+ );
10375
+ }
10376
+ const diff = await diffAgainstBase(baseStore, workingCopy, false);
10377
+ const sourceVersionAfterDiff = await computeBaseVersion(baseStore);
10378
+ if (sourceVersionAfterDiff !== base) {
10379
+ throw new BranchError(
10380
+ "Base store changed while the durable working copy was being verified.",
10381
+ {
10382
+ details: {
10383
+ baseVersion: base,
10384
+ liveBaseVersion: sourceVersionAfterDiff
10385
+ }
10386
+ }
10387
+ );
10388
+ }
10389
+ const changed = diff.nodes.new.length > 0 || diff.nodes.modified.length > 0 || diff.nodes.deleted.length > 0 || diff.nodes.windowed.length > 0 || diff.edges.new.length > 0 || diff.edges.modified.length > 0 || diff.edges.deleted.length > 0 || diff.edges.windowed.length > 0 || diff.identity.new.length > 0 || diff.identity.retracted.length > 0;
10390
+ if (!changed) return;
10391
+ throw new BranchError(
10392
+ "Durable working copy does not match its base: the stamped graph state differs.",
10393
+ {
10394
+ details: {
10395
+ baseVersion: base,
10396
+ workingCopyVersion,
10397
+ changedNodes: diff.nodes.new.length + diff.nodes.modified.length + diff.nodes.deleted.length + diff.nodes.windowed.length,
10398
+ changedEdges: diff.edges.new.length + diff.edges.modified.length + diff.edges.deleted.length + diff.edges.windowed.length,
10399
+ changedIdentityAssertions: diff.identity.new.length + diff.identity.retracted.length
10400
+ }
10401
+ }
10402
+ );
10403
+ }
10404
+ async function reopenDurableBranch(graph, descriptor, strategy) {
10405
+ const refusal = durableDescriptorRefusal(descriptor, strategy);
10406
+ if (refusal !== void 0) return err(refusal);
10407
+ if (descriptor.graphId !== graph.id) {
10408
+ return err(
10409
+ new BranchError(
10410
+ `Durable branch descriptor belongs to graph "${descriptor.graphId}", not "${graph.id}".`,
10411
+ {
10412
+ details: { descriptorGraphId: descriptor.graphId, graphId: graph.id }
10413
+ }
10414
+ )
10415
+ );
10416
+ }
10417
+ let reopened;
10418
+ try {
10419
+ reopened = await strategy.reopen(graph, descriptor.store);
10420
+ } catch (error) {
10421
+ return err(
10422
+ new BranchError(
10423
+ `Failed to reopen durable working copy for branch "${descriptor.branchId}": ${describeCause(error)}`,
10424
+ {
10425
+ cause: error,
10426
+ details: { branchId: descriptor.branchId, graphId: graph.id },
10427
+ suggestion: "Confirm the persistent working copy still exists and is reachable by the strategy, then retry."
10428
+ }
10429
+ )
10430
+ );
10431
+ }
10432
+ const { access, store, origin } = reopened;
10433
+ try {
10434
+ if (store.graphId !== graph.id) {
10435
+ throw new BranchError(
10436
+ `Reopened working copy belongs to graph "${store.graphId}", not "${graph.id}".`,
10437
+ {
10438
+ details: {
10439
+ branchId: descriptor.branchId,
10440
+ reopenedGraphId: store.graphId,
10441
+ graphId: graph.id
10442
+ }
10443
+ }
10444
+ );
10445
+ }
10446
+ const descriptorOrigin = durableOriginOfDescriptor(descriptor);
10447
+ if (!durableOriginsEqual(descriptorOrigin, origin)) {
10448
+ throw new BranchError(
10449
+ `Durable branch descriptor does not match the working copy the host attested for its store locator: the descriptor's TypeGraph fences disagree with the origin recorded at fork. This is a tampered, relabeled, or wrong-branch descriptor.`,
10450
+ {
10451
+ details: {
10452
+ branchId: descriptor.branchId,
10453
+ descriptorOrigin,
10454
+ attestedOrigin: origin
10455
+ }
10456
+ }
10457
+ );
10458
+ }
10459
+ await assertGraphMatchesAttestedOrigin(graph, origin);
10460
+ return ok(rebuildBranch(store, access, descriptor));
10461
+ } catch (error) {
10462
+ await closeDurableQuietly(store, access);
10463
+ return err(
10464
+ error instanceof BranchError ? error : new BranchError(
10465
+ `Failed to reattach durable working copy for branch "${descriptor.branchId}": ${describeCause(error)}`,
10466
+ { cause: error, details: { branchId: descriptor.branchId } }
10467
+ )
10468
+ );
10469
+ }
10470
+ }
10471
+ async function destroyDurableBranch(descriptor, strategy) {
10472
+ const refusal = durableDescriptorRefusal(descriptor, strategy);
10473
+ if (refusal !== void 0) return err(refusal);
10474
+ try {
10475
+ await strategy.destroy(
10476
+ descriptor.store,
10477
+ durableOriginOfDescriptor(descriptor)
10478
+ );
10479
+ return ok(void 0);
10480
+ } catch (error) {
10481
+ return err(
10482
+ new BranchError(
10483
+ `Failed to destroy durable working copy for branch "${descriptor.branchId}": ${describeCause(error)}`,
10484
+ { cause: error, details: { branchId: descriptor.branchId } }
10485
+ )
10486
+ );
10487
+ }
10488
+ }
10489
+ async function abandonAllocation(strategy, created, branchId, cause) {
10490
+ await closeDurableQuietly(created.store, created.access);
10491
+ let aborted = false;
10492
+ try {
10493
+ await strategy.abort(created.descriptor);
10494
+ aborted = true;
10495
+ } catch {
10496
+ }
10497
+ return new BranchError(
10498
+ aborted ? `Failed to finish durable working-copy branch "${branchId}" after the host allocated it; the opened store was closed and the persistent allocation was aborted.` : `Failed to finish durable working-copy branch "${branchId}" after the host allocated it; the opened store was closed, but the host could NOT abort the persistent allocation, which may survive as an orphan.`,
10499
+ {
10500
+ cause,
10501
+ details: {
10502
+ branchId,
10503
+ strategyType: strategy.type,
10504
+ allocationAborted: aborted
10505
+ },
10506
+ ...aborted ? {} : {
10507
+ suggestion: "The strategy's abort failed, so the persistent allocation for `details.branchId` may still exist. Inspect or remove it through the strategy's operator tooling."
10508
+ }
10509
+ }
10510
+ );
10511
+ }
10512
+ function durableDescriptorRefusal(descriptor, strategy) {
10513
+ if (typeof descriptor !== "object" || descriptor === null || Array.isArray(descriptor)) {
10514
+ return new BranchError("Durable branch descriptor must be a JSON object.", {
10515
+ details: { strategyType: strategy.type }
10516
+ });
10517
+ }
10518
+ const record = descriptor;
10519
+ if (record["kind"] !== strategy.type) {
10520
+ return new BranchError(
10521
+ `Durable branch descriptor belongs to strategy "${String(record["kind"])}", not "${strategy.type}".`,
10522
+ {
10523
+ details: {
10524
+ descriptorKind: record["kind"],
10525
+ strategyType: strategy.type
10526
+ }
10527
+ }
10528
+ );
10529
+ }
10530
+ if (record["version"] !== strategy.version) {
10531
+ return new BranchError(
10532
+ `Durable branch descriptor version ${String(record["version"])} is not supported by strategy "${strategy.type}" (expected ${strategy.version}).`,
10533
+ {
10534
+ details: {
10535
+ descriptorVersion: record["version"],
10536
+ strategyVersion: strategy.version
10537
+ }
10538
+ }
10539
+ );
10540
+ }
10541
+ for (const key of [
10542
+ "graphId",
10543
+ "definitionHash",
10544
+ "branchId",
10545
+ "base"
10546
+ ]) {
10547
+ const value = record[key];
10548
+ if (typeof value !== "string" || value.length === 0) {
10549
+ return new BranchError(
10550
+ `Durable branch descriptor is malformed: "${key}" must be a non-empty string.`,
10551
+ { details: { key, strategyType: strategy.type } }
10552
+ );
10553
+ }
10554
+ }
10555
+ if (!("store" in record)) {
10556
+ return new BranchError(
10557
+ "Durable branch descriptor is malformed: no strategy store locator.",
10558
+ { details: { strategyType: strategy.type } }
10559
+ );
10560
+ }
10561
+ if (record["schemaAnchor"] !== void 0) {
10562
+ const anchor = record["schemaAnchor"];
10563
+ if (typeof anchor !== "object" || anchor === null || typeof anchor["version"] !== "number" || typeof anchor["hash"] !== "string") {
10564
+ return new BranchError(
10565
+ "Durable branch descriptor is malformed: schemaAnchor must be { version: number; hash: string }.",
10566
+ { details: { strategyType: strategy.type } }
10567
+ );
10568
+ }
10569
+ }
10570
+ if (record["forkRevision"] !== void 0 && typeof record["forkRevision"] !== "string") {
10571
+ return new BranchError(
10572
+ "Durable branch descriptor is malformed: forkRevision must be a string.",
10573
+ { details: { strategyType: strategy.type } }
10574
+ );
10575
+ }
10576
+ return void 0;
10577
+ }
10578
+ function durableOriginOfDescriptor(descriptor) {
10579
+ return {
10580
+ graphId: descriptor.graphId,
10581
+ definitionHash: descriptor.definitionHash,
10582
+ branchId: descriptor.branchId,
10583
+ base: descriptor.base,
10584
+ schemaAnchor: descriptor.schemaAnchor,
10585
+ forkRevision: descriptor.forkRevision
10586
+ };
10587
+ }
10588
+ function durableOriginsEqual(descriptor, attested) {
10589
+ return descriptor.graphId === attested.graphId && descriptor.definitionHash === attested.definitionHash && descriptor.branchId === attested.branchId && descriptor.base === attested.base && schemaAnchorsEqual(descriptor.schemaAnchor, attested.schemaAnchor) && descriptor.forkRevision === attested.forkRevision;
10590
+ }
10591
+ function schemaAnchorsEqual(left, right) {
10592
+ if (left === void 0 || right === void 0) return left === right;
10593
+ return left.version === right.version && left.hash === right.hash;
10594
+ }
10595
+ async function assertGraphMatchesAttestedOrigin(graph, origin) {
10596
+ if (graph.id !== origin.graphId) {
10597
+ throw new BranchError(
10598
+ `The supplied graph "${graph.id}" is not the graph the durable working copy was forked from ("${origin.graphId}").`,
10599
+ {
10600
+ details: {
10601
+ graphId: graph.id,
10602
+ attestedGraphId: origin.graphId
10603
+ }
10604
+ }
10605
+ );
10606
+ }
10607
+ const graphHash = await getGraphDefinitionHash(graph);
10608
+ if (graphHash !== origin.definitionHash) {
10609
+ throw new BranchError(
10610
+ `The supplied graph definition is incompatible with the durable working copy's host-attested fork-time definition: it hashes differently, so it is a different graph definition even though it reuses graph id "${graph.id}". This identity check is attested independently of any committed schema anchor, so it also protects an unmanaged working copy.`,
10611
+ {
10612
+ details: {
10613
+ graphId: graph.id,
10614
+ attestedDefinitionHash: origin.definitionHash,
10615
+ graphHash
10616
+ }
10617
+ }
10618
+ );
10619
+ }
10620
+ }
10621
+ function rebuildBranch(store, access, descriptor) {
10622
+ return {
10623
+ id: descriptor.branchId,
10624
+ base: descriptor.base,
10625
+ store,
10626
+ close: coalescedDurableClose(store, access),
10627
+ schemaAnchor: descriptor.schemaAnchor,
10628
+ ...descriptor.forkRevision === void 0 ? {} : { forkRevision: descriptor.forkRevision }
10629
+ };
10630
+ }
10631
+ function coalescedDurableClose(store, access) {
10632
+ const closeBackend = coalescedWorkingCopyClose(store);
10633
+ let complete = false;
10634
+ let accessReleased = false;
10635
+ let inFlight;
10636
+ return async () => {
10637
+ if (complete) return;
10638
+ inFlight ??= (async () => {
10639
+ await closeBackend();
10640
+ if (access.kind === "exclusive" && !accessReleased) {
10641
+ await access.release();
10642
+ accessReleased = true;
10643
+ }
10644
+ complete = true;
10645
+ })().finally(() => {
10646
+ inFlight = void 0;
10647
+ });
10648
+ await inFlight;
10649
+ };
10650
+ }
10651
+ async function closeDurableQuietly(store, access) {
10652
+ try {
10653
+ await coalescedDurableClose(store, access)();
10654
+ } catch {
10655
+ }
10656
+ }
10657
+
10658
+ // src/graph-merge/durable-merge.ts
10659
+ async function applyDurableMergePlan(args) {
10660
+ const { branch: branch2, descriptor, plan, strategy, target } = args;
10661
+ const options = args.options ?? {};
10662
+ const refusal = durableDescriptorRefusal(descriptor, strategy);
10663
+ if (refusal !== void 0) {
10664
+ return err(
10665
+ new MergeError("Durable merge descriptor validation failed.", {
10666
+ cause: refusal
10667
+ })
10668
+ );
10669
+ }
10670
+ let artifact;
10671
+ const descriptorOrigin = durableOriginOfDescriptor(descriptor);
10672
+ try {
10673
+ artifact = await validateMergePlanForTarget(target, plan);
10674
+ const branchOrigin = {
10675
+ graphId: branch2.store.graphId,
10676
+ definitionHash: await getGraphDefinitionHash(branch2.store.graph),
10677
+ branchId: branch2.id,
10678
+ base: branch2.base,
10679
+ schemaAnchor: branch2.schemaAnchor,
10680
+ forkRevision: branch2.forkRevision
10681
+ };
10682
+ if (!durableOriginsEqual(branchOrigin, descriptorOrigin)) {
10683
+ throw new MergeError(
10684
+ "The durable branch handle does not match the descriptor supplied for native merge.",
10685
+ {
10686
+ details: {
10687
+ branchOrigin,
10688
+ descriptorOrigin
10689
+ }
10690
+ }
10691
+ );
10692
+ }
10693
+ } catch (error) {
10694
+ return err(
10695
+ error instanceof MergeError ? error : new MergeError(
10696
+ `Durable merge validation failed: ${describeCause(error)}`,
10697
+ { cause: error }
10698
+ )
10699
+ );
10700
+ }
10701
+ const usePortableApply = () => applyMergePlan(target, artifact, options);
10702
+ const hasCallbacks = options.beforeApply !== void 0 || options.afterApply !== void 0;
10703
+ if (strategy.merge === void 0 || hasCallbacks || artifact.provenance.persist) {
10704
+ return usePortableApply();
10705
+ }
10706
+ let nativeResult;
10707
+ try {
10708
+ nativeResult = await strategy.merge({
10709
+ target,
10710
+ branch: branch2,
10711
+ descriptor: descriptor.store,
10712
+ expectedOrigin: descriptorOrigin,
10713
+ plan: artifact
10714
+ });
10715
+ } catch (error) {
10716
+ return err(
10717
+ new MergeError(
10718
+ `Host-native durable merge failed: ${describeCause(error)}`,
10719
+ {
10720
+ cause: error,
10721
+ suggestion: "Inspect the host-native merge state before retrying. TypeGraph does not run the portable fallback after an uncertain or failed native attempt because the host may have applied a partial change."
10722
+ }
10723
+ )
10724
+ );
10725
+ }
10726
+ if (nativeResult.outcome === "unsupported") return usePortableApply();
10727
+ return ok(
10728
+ reportFromArtifact(artifact, nativeResult.merged, [
10729
+ ...artifact.review.warnings,
10730
+ ...nativeResult.warnings ?? []
10731
+ ])
10732
+ );
10733
+ }
10734
+
10265
10735
  // src/graph-merge/evolution-branch.ts
10266
10736
  async function branchForEvolution(store, plan, makeBackend, options) {
10267
10737
  try {
@@ -10279,6 +10749,6 @@ async function branchForEvolution(store, plan, makeBackend, options) {
10279
10749
  }
10280
10750
  }
10281
10751
 
10282
- export { BaseVersionMismatchError, BranchError, CANDIDATE_WRITE_SET_FORMAT_VERSION, CandidateSourceError, CandidateWriteSetError, CandidateWriteSetSchema, CandidateWriteSetTargetSchema, IdentityMergeConflictError, InvalidMergeOptionsError, InvalidMergePlanError, MERGE_ERROR_CODES, MERGE_OPTION_DEFAULTS, MERGE_PLAN_DIGEST_ALGORITHM, MERGE_PLAN_FORMAT_VERSION, MERGE_REVIEW_FORMAT_VERSION, MatchEvidenceError, MergeConflictError, MergeConstraintConflictError, MergeError, MergePlanCapabilityError, MergePlanDigestMismatchError, MergePlanOriginMismatchError, MergePlanSchemaMismatchError, MergePlanTargetMismatchError, MergePlanningStaleError, MergeReviewError, SimilarityUnavailableError, StaleMergePlanError, UnsupportedMergePlanVersionError, VALIDITY_END_TARGET_PRECEDENCE, applyMergePlan, applyMergePlanInTransaction, asBaseVersion, asBranchId, branch, branchForEvolution, captureCandidateWriteSetTarget, captureCandidateWriteSetTargetForEvolution, cloneWorkingCopyStrategy, computeBaseVersion, forkedWorkingCopyStrategy, ingestionBranch, merge, mergeIncremental, normalizeMergeOptions, openProvenanceStore, persistProvenanceRecords, planCandidateWriteSet, planCandidateWriteSetForEvolution, planCandidateWriteSetReview, planMerge, planMergeForEvolution, planMergeIncremental, provenanceGraphId, readProvenance, revalidateCandidateWriteSetReview };
10752
+ export { BaseVersionMismatchError, BranchError, CANDIDATE_WRITE_SET_FORMAT_VERSION, CandidateSourceError, CandidateWriteSetError, CandidateWriteSetSchema, CandidateWriteSetTargetSchema, IdentityMergeConflictError, InvalidMergeOptionsError, InvalidMergePlanError, MERGE_ERROR_CODES, MERGE_OPTION_DEFAULTS, MERGE_PLAN_DIGEST_ALGORITHM, MERGE_PLAN_FORMAT_VERSION, MERGE_REVIEW_FORMAT_VERSION, MatchEvidenceError, MergeConflictError, MergeConstraintConflictError, MergeError, MergePlanCapabilityError, MergePlanDigestMismatchError, MergePlanOriginMismatchError, MergePlanSchemaMismatchError, MergePlanTargetMismatchError, MergePlanningStaleError, MergeReviewError, SimilarityUnavailableError, StaleMergePlanError, UnsupportedMergePlanVersionError, VALIDITY_END_TARGET_PRECEDENCE, applyDurableMergePlan, applyMergePlan, applyMergePlanInTransaction, asBaseVersion, asBranchId, branch, branchDurable, branchForEvolution, captureCandidateWriteSetTarget, captureCandidateWriteSetTargetForEvolution, cloneWorkingCopyStrategy, computeBaseVersion, destroyDurableBranch, forkedWorkingCopyStrategy, ingestionBranch, merge, mergeIncremental, normalizeMergeOptions, openProvenanceStore, persistProvenanceRecords, planCandidateWriteSet, planCandidateWriteSetForEvolution, planCandidateWriteSetReview, planMerge, planMergeForEvolution, planMergeIncremental, provenanceGraphId, readProvenance, reopenDurableBranch, revalidateCandidateWriteSetReview };
10283
10753
  //# sourceMappingURL=index.js.map
10284
10754
  //# sourceMappingURL=index.js.map