@nicia-ai/typegraph 0.31.0 → 0.32.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/backend/postgres/index.cjs +16 -16
- package/dist/backend/postgres/index.d.cts +5 -5
- package/dist/backend/postgres/index.d.ts +5 -5
- package/dist/backend/postgres/index.js +6 -6
- package/dist/backend/postgres/pglite.cjs +9 -9
- package/dist/backend/postgres/pglite.d.cts +2 -2
- package/dist/backend/postgres/pglite.d.ts +2 -2
- package/dist/backend/postgres/pglite.js +6 -6
- package/dist/backend/sqlite/index.cjs +14 -14
- package/dist/backend/sqlite/index.d.cts +6 -6
- package/dist/backend/sqlite/index.d.ts +6 -6
- package/dist/backend/sqlite/index.js +5 -5
- package/dist/backend/sqlite/libsql.cjs +10 -10
- package/dist/backend/sqlite/libsql.d.cts +2 -2
- package/dist/backend/sqlite/libsql.d.ts +2 -2
- package/dist/backend/sqlite/libsql.js +6 -6
- package/dist/backend/sqlite/local.cjs +10 -10
- package/dist/backend/sqlite/local.d.cts +2 -2
- package/dist/backend/sqlite/local.d.ts +2 -2
- package/dist/backend/sqlite/local.js +6 -6
- package/dist/{chunk-XKJQQQVR.js → chunk-2BVHPOIG.js} +3 -3
- package/dist/{chunk-XKJQQQVR.js.map → chunk-2BVHPOIG.js.map} +1 -1
- package/dist/{chunk-RZ4DH4E7.cjs → chunk-2LAT5BIK.cjs} +662 -217
- package/dist/chunk-2LAT5BIK.cjs.map +1 -0
- package/dist/{chunk-CIINW6PS.cjs → chunk-3HQDMOBK.cjs} +64 -64
- package/dist/{chunk-CIINW6PS.cjs.map → chunk-3HQDMOBK.cjs.map} +1 -1
- package/dist/{chunk-6SA7UIQJ.cjs → chunk-3QRBXYT5.cjs} +6 -6
- package/dist/{chunk-6SA7UIQJ.cjs.map → chunk-3QRBXYT5.cjs.map} +1 -1
- package/dist/{chunk-4A4GUZTQ.js → chunk-7WW3WL6E.js} +5 -5
- package/dist/{chunk-4A4GUZTQ.js.map → chunk-7WW3WL6E.js.map} +1 -1
- package/dist/{chunk-2HZGWCB4.js → chunk-C4C4Q7A6.js} +3 -3
- package/dist/chunk-C4C4Q7A6.js.map +1 -0
- package/dist/{chunk-SFFJ2IKN.cjs → chunk-CAMMC3E4.cjs} +58 -58
- package/dist/{chunk-SFFJ2IKN.cjs.map → chunk-CAMMC3E4.cjs.map} +1 -1
- package/dist/{chunk-O4VUR6SI.js → chunk-CEF2QOTO.js} +3 -3
- package/dist/{chunk-O4VUR6SI.js.map → chunk-CEF2QOTO.js.map} +1 -1
- package/dist/{chunk-V4IQYOQ4.cjs → chunk-CFNT3ITG.cjs} +3 -3
- package/dist/chunk-CFNT3ITG.cjs.map +1 -0
- package/dist/{chunk-LJLMDDCW.js → chunk-CWFPJ3RC.js} +3 -3
- package/dist/{chunk-LJLMDDCW.js.map → chunk-CWFPJ3RC.js.map} +1 -1
- package/dist/{chunk-7JGLPAFV.cjs → chunk-EEYV6UOE.cjs} +16 -16
- package/dist/chunk-EEYV6UOE.cjs.map +1 -0
- package/dist/{chunk-ASYQDPPU.cjs → chunk-FDN43KXV.cjs} +19 -19
- package/dist/{chunk-ASYQDPPU.cjs.map → chunk-FDN43KXV.cjs.map} +1 -1
- package/dist/{chunk-THX25XW6.js → chunk-HFBRAOF7.js} +27 -7
- package/dist/chunk-HFBRAOF7.js.map +1 -0
- package/dist/{chunk-54JVNOAQ.cjs → chunk-JKRKIEVA.cjs} +5 -5
- package/dist/{chunk-54JVNOAQ.cjs.map → chunk-JKRKIEVA.cjs.map} +1 -1
- package/dist/{chunk-5DTPBFX5.cjs → chunk-LAZGHOZW.cjs} +10 -10
- package/dist/{chunk-5DTPBFX5.cjs.map → chunk-LAZGHOZW.cjs.map} +1 -1
- package/dist/{chunk-N2MBPTAF.cjs → chunk-LM2IC4ZJ.cjs} +14 -14
- package/dist/{chunk-N2MBPTAF.cjs.map → chunk-LM2IC4ZJ.cjs.map} +1 -1
- package/dist/{chunk-STP7QVUB.js → chunk-NDM5PXEH.js} +5 -5
- package/dist/{chunk-STP7QVUB.js.map → chunk-NDM5PXEH.js.map} +1 -1
- package/dist/{chunk-2ZY4K43M.js → chunk-PIHXFF73.js} +3 -3
- package/dist/{chunk-2ZY4K43M.js.map → chunk-PIHXFF73.js.map} +1 -1
- package/dist/{chunk-ATTXBMAX.js → chunk-PRMKPFN5.js} +3 -3
- package/dist/{chunk-ATTXBMAX.js.map → chunk-PRMKPFN5.js.map} +1 -1
- package/dist/{chunk-3JBJEYWE.cjs → chunk-SB65GJNY.cjs} +32 -12
- package/dist/chunk-SB65GJNY.cjs.map +1 -0
- package/dist/{chunk-VZX2KSY3.js → chunk-TUA3MEJJ.js} +5 -5
- package/dist/{chunk-VZX2KSY3.js.map → chunk-TUA3MEJJ.js.map} +1 -1
- package/dist/{chunk-MAQJYNJR.js → chunk-W4ZX2GCP.js} +14 -16
- package/dist/chunk-W4ZX2GCP.js.map +1 -0
- package/dist/{chunk-UIFEEPME.js → chunk-WNORITK6.js} +593 -149
- package/dist/chunk-WNORITK6.js.map +1 -0
- package/dist/{chunk-VMEWZU2N.cjs → chunk-WWVV44DY.cjs} +11 -11
- package/dist/{chunk-VMEWZU2N.cjs.map → chunk-WWVV44DY.cjs.map} +1 -1
- package/dist/{chunk-UJ4V3YUQ.js → chunk-XKNAK27R.js} +3 -3
- package/dist/{chunk-UJ4V3YUQ.js.map → chunk-XKNAK27R.js.map} +1 -1
- package/dist/{chunk-GKFWM3DS.js → chunk-XQFAJVYW.js} +3 -3
- package/dist/{chunk-GKFWM3DS.js.map → chunk-XQFAJVYW.js.map} +1 -1
- package/dist/{chunk-FLOI7QOT.cjs → chunk-YL4F3CMD.cjs} +59 -59
- package/dist/{chunk-FLOI7QOT.cjs.map → chunk-YL4F3CMD.cjs.map} +1 -1
- package/dist/{chunk-5CN7B4CZ.cjs → chunk-YW7GHMF4.cjs} +17 -17
- package/dist/{chunk-5CN7B4CZ.cjs.map → chunk-YW7GHMF4.cjs.map} +1 -1
- package/dist/{ddl-BJcPrB4L.d.ts → ddl-C8e8zDAE.d.ts} +3 -3
- package/dist/{ddl-CPZ7mB7X.d.cts → ddl-CwyG4HoQ.d.cts} +3 -3
- package/dist/graph-merge/index.cjs +193 -49
- package/dist/graph-merge/index.cjs.map +1 -1
- package/dist/graph-merge/index.d.cts +3 -3
- package/dist/graph-merge/index.d.ts +3 -3
- package/dist/graph-merge/index.js +180 -36
- package/dist/graph-merge/index.js.map +1 -1
- package/dist/index.cjs +89 -85
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +12 -12
- package/dist/indexes/index.cjs +10 -10
- package/dist/indexes/index.d.cts +4 -4
- package/dist/indexes/index.d.ts +4 -4
- package/dist/indexes/index.js +3 -3
- package/dist/interchange/index.cjs +15 -15
- package/dist/interchange/index.d.cts +7 -7
- package/dist/interchange/index.d.ts +7 -7
- package/dist/interchange/index.js +2 -2
- package/dist/{manager-By-WEd8M.d.ts → manager-8htmuGr-.d.ts} +1 -1
- package/dist/{manager-Np7VVSlE.d.cts → manager-aVgxbGt-.d.cts} +1 -1
- package/dist/{postgres-BdIb7tS_.d.ts → postgres-Dhom7iiX.d.ts} +1 -1
- package/dist/{postgres-4J2tmauh.d.cts → postgres-jgXmD45C.d.cts} +1 -1
- package/dist/profiler/index.d.cts +5 -5
- package/dist/profiler/index.d.ts +5 -5
- package/dist/schema/index.cjs +26 -26
- package/dist/schema/index.d.cts +4 -4
- package/dist/schema/index.d.ts +4 -4
- package/dist/schema/index.js +3 -3
- package/dist/{sqlite-aM_PAz-O.d.ts → sqlite-BfLsCEqO.d.ts} +1 -1
- package/dist/{sqlite-Gatk_iw_.d.cts → sqlite-DcQ_BCUW.d.cts} +1 -1
- package/dist/{store-DuCbdqsh.d.cts → store-view-lTcb_cnp.d.cts} +639 -311
- package/dist/{store-MloRPwb9.d.ts → store-view-pBOUrQte.d.ts} +639 -311
- package/dist/{types-DoByHGyU.d.cts → types-5RQwzWXx.d.cts} +1 -1
- package/dist/{types-K3vpaJAL.d.ts → types-DGDiXAYc.d.ts} +1 -1
- package/dist/{types-BKQljXom.d.ts → types-Dk8r09KY.d.ts} +1 -1
- package/dist/{types-C1CYxWrL.d.cts → types-Dvmn-eal.d.cts} +1 -1
- package/dist/{where-Dt49bvo6.d.ts → where-CHZFELX0.d.ts} +1 -1
- package/dist/{where-DydZjGOb.d.cts → where-Cx_glEEB.d.cts} +1 -1
- package/package.json +2 -2
- package/dist/chunk-2HZGWCB4.js.map +0 -1
- package/dist/chunk-3JBJEYWE.cjs.map +0 -1
- package/dist/chunk-7JGLPAFV.cjs.map +0 -1
- package/dist/chunk-MAQJYNJR.js.map +0 -1
- package/dist/chunk-RZ4DH4E7.cjs.map +0 -1
- package/dist/chunk-THX25XW6.js.map +0 -1
- package/dist/chunk-UIFEEPME.js.map +0 -1
- package/dist/chunk-V4IQYOQ4.cjs.map +0 -1
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { G as GraphDef, t as NodeKinds, u as GetNodeType } from '../manager-
|
|
2
|
-
import { S as Store, N as Node, U as UniqueIntrospection } from '../store-
|
|
1
|
+
import { G as GraphDef, t as NodeKinds, u as GetNodeType } from '../manager-aVgxbGt-.cjs';
|
|
2
|
+
import { S as Store, N as Node, U as UniqueIntrospection } from '../store-view-lTcb_cnp.cjs';
|
|
3
3
|
import { d as NodeId, N as NodeType, e as EdgeId, J as JsonValue, f as KindAnnotations, g as EdgeType, D as DeleteBehavior, T as TemporalMode, h as GraphExtensionVersion, i as ExtensionNodeDef, j as ExtensionEdgeDef, k as ExtensionOntologyRelation, l as ExtensionIndex } from '../extension-types-D1k0inWU.cjs';
|
|
4
4
|
import { T as TypeGraphError } from '../index-C8sSTErx.cjs';
|
|
5
5
|
import { R as Result } from '../result-DWo6mUVz.cjs';
|
|
6
6
|
export { i as isErr, a as isOk, u as unwrap } from '../result-DWo6mUVz.cjs';
|
|
7
|
-
import { G as GraphBackend, M as MetaEdge, I as IndexDeclaration } from '../types-
|
|
7
|
+
import { G as GraphBackend, M as MetaEdge, I as IndexDeclaration } from '../types-Dvmn-eal.cjs';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import 'drizzle-orm';
|
|
10
10
|
import '../materialize-indexes-DEYkU-wr.cjs';
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { G as GraphDef, t as NodeKinds, u as GetNodeType } from '../manager-
|
|
2
|
-
import { S as Store, N as Node, U as UniqueIntrospection } from '../store-
|
|
1
|
+
import { G as GraphDef, t as NodeKinds, u as GetNodeType } from '../manager-8htmuGr-.js';
|
|
2
|
+
import { S as Store, N as Node, U as UniqueIntrospection } from '../store-view-pBOUrQte.js';
|
|
3
3
|
import { d as NodeId, N as NodeType, e as EdgeId, J as JsonValue, f as KindAnnotations, g as EdgeType, D as DeleteBehavior, T as TemporalMode, h as GraphExtensionVersion, i as ExtensionNodeDef, j as ExtensionEdgeDef, k as ExtensionOntologyRelation, l as ExtensionIndex } from '../extension-types-D1k0inWU.js';
|
|
4
4
|
import { T as TypeGraphError } from '../index-BEK0VtNB.js';
|
|
5
5
|
import { R as Result } from '../result-DWo6mUVz.js';
|
|
6
6
|
export { i as isErr, a as isOk, u as unwrap } from '../result-DWo6mUVz.js';
|
|
7
|
-
import { G as GraphBackend, M as MetaEdge, I as IndexDeclaration } from '../types-
|
|
7
|
+
import { G as GraphBackend, M as MetaEdge, I as IndexDeclaration } from '../types-Dk8r09KY.js';
|
|
8
8
|
import { z } from 'zod';
|
|
9
9
|
import 'drizzle-orm';
|
|
10
10
|
import '../materialize-indexes-CAGhqPHQ.js';
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { createStoreWithSchema, generateId, computeUniqueKey } from '../chunk-
|
|
1
|
+
import { createStoreWithSchema, generateId, computeUniqueKey } from '../chunk-WNORITK6.js';
|
|
2
2
|
import '../chunk-3EUKJEQ3.js';
|
|
3
|
-
import { exportGraph, importGraph } from '../chunk-
|
|
4
|
-
import { defineNode, computeSchemaHash, serializeSchema } from '../chunk-
|
|
5
|
-
import { getNodeKinds, getEdgeKinds, defineGraph } from '../chunk-
|
|
6
|
-
import '../chunk-
|
|
3
|
+
import { exportGraph, importGraph } from '../chunk-HFBRAOF7.js';
|
|
4
|
+
import { defineNode, computeSchemaHash, serializeSchema } from '../chunk-2BVHPOIG.js';
|
|
5
|
+
import { getNodeKinds, getEdgeKinds, defineGraph } from '../chunk-W4ZX2GCP.js';
|
|
6
|
+
import '../chunk-PIHXFF73.js';
|
|
7
7
|
import '../chunk-IJORSAC3.js';
|
|
8
8
|
import '../chunk-MXE2U3EX.js';
|
|
9
9
|
import '../chunk-454I6GDF.js';
|
|
@@ -11,7 +11,7 @@ import { ok, err, isErr, computeTransitiveClosure, isReachable } from '../chunk-
|
|
|
11
11
|
export { isErr, isOk, unwrap } from '../chunk-YCV72E6L.js';
|
|
12
12
|
import '../chunk-SAJSMR2B.js';
|
|
13
13
|
import { sortedReplacer } from '../chunk-M6OC455F.js';
|
|
14
|
-
import '../chunk-
|
|
14
|
+
import '../chunk-C4C4Q7A6.js';
|
|
15
15
|
import { sha256Hex } from '../chunk-2YMGNYXY.js';
|
|
16
16
|
import { TypeGraphError } from '../chunk-TQOYM4LN.js';
|
|
17
17
|
import '../chunk-K5P5OZXS.js';
|
|
@@ -317,10 +317,7 @@ function byDigestEntry(left, right) {
|
|
|
317
317
|
return byKind === 0 ? compareStrings(left.id, right.id) : byKind;
|
|
318
318
|
}
|
|
319
319
|
var CONTENT_FINGERPRINT_BYTES = 16;
|
|
320
|
-
async function
|
|
321
|
-
const backend = store.backend;
|
|
322
|
-
const graphId = store.graphId;
|
|
323
|
-
const graph = store.graph;
|
|
320
|
+
async function computeContentComponent(backend, graphId, graph) {
|
|
324
321
|
const nodeKinds = getNodeKinds(graph);
|
|
325
322
|
const edgeKinds = getEdgeKinds(graph);
|
|
326
323
|
const nodeDigest = [];
|
|
@@ -368,12 +365,16 @@ async function computeContentFingerprint(store) {
|
|
|
368
365
|
async function computeBaseVersion(store) {
|
|
369
366
|
const [schemaComponent, contentComponent] = await Promise.all([
|
|
370
367
|
computeSchemaComponent(store),
|
|
371
|
-
|
|
368
|
+
computeContentComponent(store.backend, store.graphId, store.graph)
|
|
372
369
|
]);
|
|
373
370
|
return asBaseVersion(
|
|
374
371
|
`${schemaComponent}${TOKEN_SEPARATOR}${contentComponent}`
|
|
375
372
|
);
|
|
376
373
|
}
|
|
374
|
+
function contentComponentOf(version) {
|
|
375
|
+
const separatorIndex = version.indexOf(TOKEN_SEPARATOR);
|
|
376
|
+
return separatorIndex === -1 ? version : version.slice(separatorIndex + 1);
|
|
377
|
+
}
|
|
377
378
|
|
|
378
379
|
// src/graph-merge/errors.ts
|
|
379
380
|
var MERGE_ERROR_CODES = {
|
|
@@ -2506,6 +2507,48 @@ async function stageBranches(baseStore, branches) {
|
|
|
2506
2507
|
};
|
|
2507
2508
|
}
|
|
2508
2509
|
|
|
2510
|
+
// src/graph-merge/tx-retry.ts
|
|
2511
|
+
var RETRYABLE_SQLSTATES = /* @__PURE__ */ new Set(["40001", "40P01"]);
|
|
2512
|
+
var RETRYABLE_MESSAGE_PATTERN = /could not serialize access|deadlock detected/i;
|
|
2513
|
+
var MAX_COMMIT_ATTEMPTS = 3;
|
|
2514
|
+
function isRetryableTxConflict(error) {
|
|
2515
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2516
|
+
let current = error;
|
|
2517
|
+
while (current !== void 0 && current !== null && typeof current === "object" && !seen.has(current)) {
|
|
2518
|
+
seen.add(current);
|
|
2519
|
+
const candidate = current;
|
|
2520
|
+
if (typeof candidate.code === "string" && RETRYABLE_SQLSTATES.has(candidate.code)) {
|
|
2521
|
+
return true;
|
|
2522
|
+
}
|
|
2523
|
+
if (typeof candidate.message === "string" && RETRYABLE_MESSAGE_PATTERN.test(candidate.message)) {
|
|
2524
|
+
return true;
|
|
2525
|
+
}
|
|
2526
|
+
current = candidate.cause;
|
|
2527
|
+
}
|
|
2528
|
+
return false;
|
|
2529
|
+
}
|
|
2530
|
+
async function withTxConflictRetry(commit) {
|
|
2531
|
+
let lastConflict;
|
|
2532
|
+
for (let attempt = 1; attempt <= MAX_COMMIT_ATTEMPTS; attempt += 1) {
|
|
2533
|
+
try {
|
|
2534
|
+
return await commit();
|
|
2535
|
+
} catch (error) {
|
|
2536
|
+
if (!isRetryableTxConflict(error)) {
|
|
2537
|
+
throw error;
|
|
2538
|
+
}
|
|
2539
|
+
lastConflict = error;
|
|
2540
|
+
}
|
|
2541
|
+
}
|
|
2542
|
+
throw new MergeError(
|
|
2543
|
+
`Merge commit aborted by transaction conflicts (serialization failure or deadlock) on ${MAX_COMMIT_ATTEMPTS} consecutive attempts; giving up.`,
|
|
2544
|
+
{
|
|
2545
|
+
cause: lastConflict,
|
|
2546
|
+
details: { attempts: MAX_COMMIT_ATTEMPTS },
|
|
2547
|
+
suggestion: "Reduce concurrent writes to the merge target, or serialize merges against it."
|
|
2548
|
+
}
|
|
2549
|
+
);
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2509
2552
|
// src/graph-merge/type-reconcile.ts
|
|
2510
2553
|
var INCOMPATIBLE_TYPES_FLAG_REASON = "type-reconcile:incompatible-kinds";
|
|
2511
2554
|
function distinctKinds(kinds) {
|
|
@@ -3225,20 +3268,48 @@ async function applyMergePlan(plan, nodesApi, edgesApi) {
|
|
|
3225
3268
|
}
|
|
3226
3269
|
return { nodes: committedNodeIds.size, edges: committedEdges };
|
|
3227
3270
|
}
|
|
3228
|
-
async function commitPlan(target, plan) {
|
|
3271
|
+
async function commitPlan(target, plan, expectedBaseVersion) {
|
|
3229
3272
|
if (!target.backend.capabilities.transactions) {
|
|
3230
3273
|
throw new MergeError(
|
|
3231
3274
|
"merge() requires a transaction-capable target backend. The merged plan (canonical upserts, soft-deletes, edge upserts) must commit atomically; a non-transactional fallback would leave a partially-merged graph on a mid-commit failure. (mergeIncremental() enforces the same requirement.)",
|
|
3232
3275
|
{ details: { capability: "transactions" } }
|
|
3233
3276
|
);
|
|
3234
3277
|
}
|
|
3235
|
-
return
|
|
3236
|
-
(tx) =>
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3278
|
+
return withTxConflictRetry(
|
|
3279
|
+
() => target.transaction(async (tx) => {
|
|
3280
|
+
if (expectedBaseVersion !== void 0) {
|
|
3281
|
+
await assertTargetUnchanged(tx.backend, target, expectedBaseVersion);
|
|
3282
|
+
}
|
|
3283
|
+
return applyMergePlan(
|
|
3284
|
+
plan,
|
|
3285
|
+
tx.nodes,
|
|
3286
|
+
tx.edges
|
|
3287
|
+
);
|
|
3288
|
+
}, MERGE_COMMIT_TX_OPTIONS)
|
|
3289
|
+
);
|
|
3290
|
+
}
|
|
3291
|
+
var MERGE_COMMIT_TX_OPTIONS = {
|
|
3292
|
+
isolationLevel: "serializable"
|
|
3293
|
+
};
|
|
3294
|
+
async function assertTargetUnchanged(txBackend, target, expectedBaseVersion) {
|
|
3295
|
+
const liveContent = await computeContentComponent(
|
|
3296
|
+
txBackend,
|
|
3297
|
+
target.graphId,
|
|
3298
|
+
target.graph
|
|
3241
3299
|
);
|
|
3300
|
+
const expectedContent = contentComponentOf(expectedBaseVersion);
|
|
3301
|
+
if (liveContent !== expectedContent) {
|
|
3302
|
+
throw new BaseVersionMismatchError(
|
|
3303
|
+
"The merge target was modified between the base@V check and the commit transaction; the resolved plan no longer describes the live target and was not applied.",
|
|
3304
|
+
{
|
|
3305
|
+
details: {
|
|
3306
|
+
expectedContentFingerprint: expectedContent,
|
|
3307
|
+
liveContentFingerprint: liveContent
|
|
3308
|
+
},
|
|
3309
|
+
suggestion: "Re-run the merge (and re-branch if the divergence is real), or serialize writers against merges on this target."
|
|
3310
|
+
}
|
|
3311
|
+
);
|
|
3312
|
+
}
|
|
3242
3313
|
}
|
|
3243
3314
|
function nodeCollection(nodes, kind) {
|
|
3244
3315
|
const collection = nodes[kind];
|
|
@@ -3276,7 +3347,7 @@ async function validateBaseVersions(target, branches) {
|
|
|
3276
3347
|
);
|
|
3277
3348
|
}
|
|
3278
3349
|
}
|
|
3279
|
-
return ok();
|
|
3350
|
+
return ok(targetVersion);
|
|
3280
3351
|
}
|
|
3281
3352
|
function tryNormalize(optionsInput) {
|
|
3282
3353
|
try {
|
|
@@ -3285,7 +3356,7 @@ function tryNormalize(optionsInput) {
|
|
|
3285
3356
|
return err(new MergeError("Invalid merge options.", { cause: error }));
|
|
3286
3357
|
}
|
|
3287
3358
|
}
|
|
3288
|
-
async function resolveMerge(store, target, branches, options, useBaseSources, incremental) {
|
|
3359
|
+
async function resolveMerge(store, target, branches, options, useBaseSources, incremental, expectedBaseVersion) {
|
|
3289
3360
|
let targetBranchSeen = false;
|
|
3290
3361
|
for (const branch2 of branches) {
|
|
3291
3362
|
if (branch2.id === BASE_PROVENANCE_BRANCH) {
|
|
@@ -3356,7 +3427,16 @@ async function resolveMerge(store, target, branches, options, useBaseSources, in
|
|
|
3356
3427
|
subClassClosure,
|
|
3357
3428
|
preferredBranchId
|
|
3358
3429
|
);
|
|
3359
|
-
const merged = incremental === void 0 ? await commitPlan(target, plan) : await commitIncrementalPlan(target, plan
|
|
3430
|
+
const merged = incremental === void 0 ? await commitPlan(target, plan, expectedBaseVersion) : await commitIncrementalPlan(target, plan, {
|
|
3431
|
+
stagedNewByKind: newNodesByKind(staging),
|
|
3432
|
+
options,
|
|
3433
|
+
introspectionKinds,
|
|
3434
|
+
// The committed (kind, id) keys the base sources matched at PLAN time;
|
|
3435
|
+
// anything NEW the in-tx re-probe surfaces is a window write.
|
|
3436
|
+
plannedBaseMatchKeys: new Set(
|
|
3437
|
+
candidates.data.baseMembers.map((member) => mergeKeyOf(member))
|
|
3438
|
+
)
|
|
3439
|
+
});
|
|
3360
3440
|
const provenance = options.provenance ? buildProvenanceIndex(plan.provenanceRecords) : { byBranch: () => ({ nodeIds: [], edgeIds: [] }) };
|
|
3361
3441
|
const warnings = [...plan.warnings];
|
|
3362
3442
|
let provenancePersisted;
|
|
@@ -3417,7 +3497,15 @@ async function merge(store, branches, optionsInput = {}) {
|
|
|
3417
3497
|
if (isErr(precondition)) {
|
|
3418
3498
|
return err(precondition.error);
|
|
3419
3499
|
}
|
|
3420
|
-
return resolveMerge(
|
|
3500
|
+
return resolveMerge(
|
|
3501
|
+
store,
|
|
3502
|
+
target,
|
|
3503
|
+
branches,
|
|
3504
|
+
options,
|
|
3505
|
+
false,
|
|
3506
|
+
void 0,
|
|
3507
|
+
precondition.data
|
|
3508
|
+
);
|
|
3421
3509
|
}
|
|
3422
3510
|
async function validateForkPointVersions(forkPoint, branches) {
|
|
3423
3511
|
const forkVersion = await computeBaseVersion(forkPoint);
|
|
@@ -3650,13 +3738,12 @@ async function validateIncrementalEdgeWrites(target, edgesApi, plan) {
|
|
|
3650
3738
|
}
|
|
3651
3739
|
const notFound = [...plannedKindById.keys()].filter((id) => !existingById.has(id)).sort((left, right) => compareStrings(left, right));
|
|
3652
3740
|
if (notFound.length > 0) {
|
|
3653
|
-
const perKind = await Promise.all(
|
|
3654
|
-
Object.keys(target.graph.edges).map(
|
|
3655
|
-
(kind) => edgeCollection(edgesApi, kind).getByIds(notFound, INCLUDE_TOMBSTONES)
|
|
3656
|
-
)
|
|
3657
|
-
);
|
|
3658
3741
|
const crossKindById = /* @__PURE__ */ new Map();
|
|
3659
|
-
for (const
|
|
3742
|
+
for (const kind of Object.keys(target.graph.edges)) {
|
|
3743
|
+
const rows = await edgeCollection(edgesApi, kind).getByIds(
|
|
3744
|
+
notFound,
|
|
3745
|
+
INCLUDE_TOMBSTONES
|
|
3746
|
+
);
|
|
3660
3747
|
for (const [index, id] of notFound.entries()) {
|
|
3661
3748
|
const row = rows[index];
|
|
3662
3749
|
if (row !== void 0 && !crossKindById.has(id)) {
|
|
@@ -3724,20 +3811,77 @@ async function validateIncrementalEdgeWrites(target, edgesApi, plan) {
|
|
|
3724
3811
|
}
|
|
3725
3812
|
}
|
|
3726
3813
|
}
|
|
3727
|
-
async function
|
|
3814
|
+
async function collectBaseMatchKeys(lookupStore, guard) {
|
|
3815
|
+
const matched = /* @__PURE__ */ new Set();
|
|
3816
|
+
for (const [kind, stagedNodes] of guard.stagedNewByKind) {
|
|
3817
|
+
const resolveConfig = guard.options.resolve[kind];
|
|
3818
|
+
if (resolveConfig === void 0) {
|
|
3819
|
+
continue;
|
|
3820
|
+
}
|
|
3821
|
+
const collection = lookupStore.nodes[kind];
|
|
3822
|
+
if (collection === void 0 || stagedNodes.length === 0) {
|
|
3823
|
+
continue;
|
|
3824
|
+
}
|
|
3825
|
+
const items = stagedNodes.map((staged) => ({ props: staged.node.props }));
|
|
3826
|
+
for (const constraint of uniqueConstraintsFor(
|
|
3827
|
+
guard.introspectionKinds,
|
|
3828
|
+
kind
|
|
3829
|
+
)) {
|
|
3830
|
+
const matches = await collection.bulkFindByConstraint(
|
|
3831
|
+
constraint.name,
|
|
3832
|
+
items
|
|
3833
|
+
);
|
|
3834
|
+
for (const base of matches) {
|
|
3835
|
+
if (base !== void 0) {
|
|
3836
|
+
matched.add(mergeKeyOf(base));
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
if (resolveConfig.blockIndex !== void 0) {
|
|
3841
|
+
const matchesByItem = await collection.bulkFindByIndex(
|
|
3842
|
+
resolveConfig.blockIndex,
|
|
3843
|
+
items
|
|
3844
|
+
);
|
|
3845
|
+
for (const perItem of matchesByItem) {
|
|
3846
|
+
for (const base of perItem) {
|
|
3847
|
+
matched.add(mergeKeyOf(base));
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
}
|
|
3851
|
+
}
|
|
3852
|
+
return matched;
|
|
3853
|
+
}
|
|
3854
|
+
async function assertBaseResolutionStable(lookupStore, guard) {
|
|
3855
|
+
const liveKeys = await collectBaseMatchKeys(lookupStore, guard);
|
|
3856
|
+
const appeared = [...liveKeys].filter((key) => !guard.plannedBaseMatchKeys.has(key)).sort((left, right) => compareMergeKeys(left, right));
|
|
3857
|
+
if (appeared.length === 0) {
|
|
3858
|
+
return;
|
|
3859
|
+
}
|
|
3860
|
+
throw new BaseVersionMismatchError(
|
|
3861
|
+
`mergeIncremental() resolved its branch additions against the target as of planning, but ${appeared.length} committed row(s) matching a branch addition's identity key (a unique constraint or block index) were inserted before the commit transaction. Committing the plan would create duplicate entities the base-source resolution would otherwise have collapsed.`,
|
|
3862
|
+
{
|
|
3863
|
+
details: { appeared },
|
|
3864
|
+
suggestion: "Re-run mergeIncremental(); the re-planned merge resolves the branch additions against the now-committed rows."
|
|
3865
|
+
}
|
|
3866
|
+
);
|
|
3867
|
+
}
|
|
3868
|
+
async function commitIncrementalPlan(target, plan, guard) {
|
|
3728
3869
|
if (!target.backend.capabilities.transactions) {
|
|
3729
3870
|
throw new MergeError(
|
|
3730
3871
|
"mergeIncremental() requires a transaction-capable target backend. Incremental writes must preflight existing rows and commit atomically; non-transactional fallback would allow partial graph writes.",
|
|
3731
3872
|
{ details: { capability: "transactions" } }
|
|
3732
3873
|
);
|
|
3733
3874
|
}
|
|
3734
|
-
return
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3875
|
+
return withTxConflictRetry(
|
|
3876
|
+
() => target.transaction(async (tx) => {
|
|
3877
|
+
const nodesApi = tx.nodes;
|
|
3878
|
+
const edgesApi = tx.edges;
|
|
3879
|
+
await assertBaseResolutionStable(tx, guard);
|
|
3880
|
+
await validateIncrementalNodeWrites(target, nodesApi, plan);
|
|
3881
|
+
await validateIncrementalEdgeWrites(target, edgesApi, plan);
|
|
3882
|
+
return applyMergePlan(plan, nodesApi, edgesApi);
|
|
3883
|
+
}, MERGE_COMMIT_TX_OPTIONS)
|
|
3884
|
+
);
|
|
3741
3885
|
}
|
|
3742
3886
|
async function mergeIncremental(args) {
|
|
3743
3887
|
const { forkPoint, target, branches } = args;
|