@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.
- package/CHANGELOG.md +24 -0
- package/README.md +15 -0
- package/dist/backend/drizzle/engine/index.cjs +6 -6
- package/dist/backend/drizzle/engine/index.js +1 -1
- package/dist/backend/postgres/index.cjs +15 -15
- package/dist/backend/postgres/index.js +1 -1
- package/dist/backend/postgres/pglite-store.cjs +1 -1
- package/dist/backend/postgres/pglite-store.js +1 -1
- package/dist/backend/postgres/pglite.cjs +3 -3
- package/dist/backend/postgres/pglite.js +2 -2
- package/dist/backend/sqlite/index.cjs +14 -14
- package/dist/backend/sqlite/index.js +1 -1
- package/dist/backend/sqlite/libsql.cjs +6 -6
- package/dist/backend/sqlite/libsql.js +1 -1
- package/dist/backend/sqlite/local-store.cjs +1 -1
- package/dist/backend/sqlite/local-store.js +1 -1
- package/dist/backend/sqlite/local.cjs +3 -3
- package/dist/backend/sqlite/local.js +2 -2
- package/dist/{chunk-TR7QTODF.js → chunk-55DGFZYC.js} +3 -3
- package/dist/{chunk-TR7QTODF.js.map → chunk-55DGFZYC.js.map} +1 -1
- package/dist/{chunk-WB55URNM.js → chunk-6CKSTY2D.js} +7 -3
- package/dist/chunk-6CKSTY2D.js.map +1 -0
- package/dist/{chunk-ZONDBN6H.js → chunk-6DY26DPE.js} +3 -3
- package/dist/{chunk-ZONDBN6H.js.map → chunk-6DY26DPE.js.map} +1 -1
- package/dist/{chunk-YHMXF7OZ.js → chunk-6J3VUPI6.js} +3 -3
- package/dist/{chunk-YHMXF7OZ.js.map → chunk-6J3VUPI6.js.map} +1 -1
- package/dist/{chunk-MN325MG4.cjs → chunk-7MQL7FEN.cjs} +11 -2
- package/dist/chunk-7MQL7FEN.cjs.map +1 -0
- package/dist/{chunk-WWSYGZED.cjs → chunk-E6XL3KMZ.cjs} +6 -6
- package/dist/{chunk-WWSYGZED.cjs.map → chunk-E6XL3KMZ.cjs.map} +1 -1
- package/dist/{chunk-SSDHR2C2.js → chunk-EFSXK3DW.js} +3 -3
- package/dist/{chunk-SSDHR2C2.js.map → chunk-EFSXK3DW.js.map} +1 -1
- package/dist/{chunk-2DJKHZQ4.js → chunk-EORFWD6O.js} +11 -2
- package/dist/chunk-EORFWD6O.js.map +1 -0
- package/dist/{chunk-Y4X6ODVY.cjs → chunk-FS2GVO2T.cjs} +56 -56
- package/dist/{chunk-Y4X6ODVY.cjs.map → chunk-FS2GVO2T.cjs.map} +1 -1
- package/dist/{chunk-K7YACILO.cjs → chunk-GXHEGNI6.cjs} +9 -9
- package/dist/{chunk-K7YACILO.cjs.map → chunk-GXHEGNI6.cjs.map} +1 -1
- package/dist/{chunk-BX42F7IJ.cjs → chunk-SSDLQPMY.cjs} +7 -2
- package/dist/chunk-SSDLQPMY.cjs.map +1 -0
- package/dist/{chunk-ANMCKLZQ.cjs → chunk-ZRIXU33S.cjs} +12 -12
- package/dist/{chunk-ANMCKLZQ.cjs.map → chunk-ZRIXU33S.cjs.map} +1 -1
- package/dist/graph-merge/index.cjs +500 -26
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +384 -2
- package/dist/graph-merge/index.d.ts +384 -2
- package/dist/graph-merge/index.js +485 -15
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/index.cjs +63 -63
- package/dist/index.js +3 -3
- package/dist/{local-store-impl-ERPNYOCL.js → local-store-impl-CCMWTLGY.js} +6 -6
- package/dist/{local-store-impl-ERPNYOCL.js.map → local-store-impl-CCMWTLGY.js.map} +1 -1
- package/dist/{local-store-impl-OUINMCYI.cjs → local-store-impl-GI6S4J6B.cjs} +9 -9
- package/dist/{local-store-impl-OUINMCYI.cjs.map → local-store-impl-GI6S4J6B.cjs.map} +1 -1
- package/dist/{pglite-store-impl-Z3SOOP75.cjs → pglite-store-impl-HAPAJUIO.cjs} +9 -9
- package/dist/{pglite-store-impl-Z3SOOP75.cjs.map → pglite-store-impl-HAPAJUIO.cjs.map} +1 -1
- package/dist/{pglite-store-impl-DUJBQZS5.js → pglite-store-impl-HDLFOZWS.js} +6 -6
- package/dist/{pglite-store-impl-DUJBQZS5.js.map → pglite-store-impl-HDLFOZWS.js.map} +1 -1
- package/dist/schema/index.cjs +27 -27
- package/dist/schema/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-2DJKHZQ4.js.map +0 -1
- package/dist/chunk-BX42F7IJ.cjs.map +0 -1
- package/dist/chunk-MN325MG4.cjs.map +0 -1
- 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-
|
|
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-
|
|
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
|
-
|
|
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
|
-
|
|
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
|