@nicia-ai/typegraph 0.18.0 → 0.20.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.cjs +454 -95
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +454 -95
- package/dist/index.js.map +1 -1
- package/dist/interchange/index.d.cts +1 -1
- package/dist/interchange/index.d.ts +1 -1
- package/dist/profiler/index.d.cts +1 -1
- package/dist/profiler/index.d.ts +1 -1
- package/dist/{store-DkY1pDfu.d.ts → store-BMrRi_mY.d.ts} +256 -19
- package/dist/{store-B_JzzHwb.d.cts → store-DRRvALu8.d.cts} +256 -19
- package/package.json +5 -5
package/dist/index.cjs
CHANGED
|
@@ -3449,6 +3449,116 @@ function decodeByValueType(value, valueType) {
|
|
|
3449
3449
|
}
|
|
3450
3450
|
}
|
|
3451
3451
|
}
|
|
3452
|
+
function buildReachableCte(options) {
|
|
3453
|
+
const trackPath = options.cyclePolicy === "prevent" || options.includePath;
|
|
3454
|
+
const edgeKindFilter = compileKindFilter(
|
|
3455
|
+
drizzleOrm.sql.raw("e.kind"),
|
|
3456
|
+
options.edgeKinds
|
|
3457
|
+
);
|
|
3458
|
+
const currentTimestamp = options.dialect.currentTimestamp();
|
|
3459
|
+
const nodeTemporalFilter = compileTemporalFilter({
|
|
3460
|
+
mode: options.temporalMode,
|
|
3461
|
+
asOf: options.asOf,
|
|
3462
|
+
tableAlias: "n",
|
|
3463
|
+
currentTimestamp
|
|
3464
|
+
});
|
|
3465
|
+
const edgeTemporalFilter = compileTemporalFilter({
|
|
3466
|
+
mode: options.temporalMode,
|
|
3467
|
+
asOf: options.asOf,
|
|
3468
|
+
tableAlias: "e",
|
|
3469
|
+
currentTimestamp
|
|
3470
|
+
});
|
|
3471
|
+
const initialPath = trackPath ? options.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3472
|
+
const pathExtension = trackPath ? options.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3473
|
+
const cycleCheck = options.cyclePolicy === "prevent" ? options.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
|
|
3474
|
+
const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
|
|
3475
|
+
if (initialPath !== void 0) {
|
|
3476
|
+
baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
|
|
3477
|
+
}
|
|
3478
|
+
const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${options.schema.nodesTable} n WHERE n.graph_id = ${options.graphId} AND n.id = ${options.sourceId} AND ${nodeTemporalFilter}`;
|
|
3479
|
+
const recursiveColumns = [
|
|
3480
|
+
drizzleOrm.sql`n.id`,
|
|
3481
|
+
drizzleOrm.sql`n.kind`,
|
|
3482
|
+
drizzleOrm.sql`r.depth + 1 AS depth`
|
|
3483
|
+
];
|
|
3484
|
+
if (pathExtension !== void 0) {
|
|
3485
|
+
recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
|
|
3486
|
+
}
|
|
3487
|
+
const recursiveWhere = [
|
|
3488
|
+
drizzleOrm.sql`e.graph_id = ${options.graphId}`,
|
|
3489
|
+
edgeKindFilter,
|
|
3490
|
+
edgeTemporalFilter,
|
|
3491
|
+
nodeTemporalFilter,
|
|
3492
|
+
drizzleOrm.sql`r.depth < ${options.maxHops}`
|
|
3493
|
+
];
|
|
3494
|
+
if (cycleCheck !== void 0) recursiveWhere.push(cycleCheck);
|
|
3495
|
+
const forceWorktableOuterJoinOrder = options.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
|
|
3496
|
+
const recursiveCase = compileRecursiveBranch({
|
|
3497
|
+
recursiveColumns,
|
|
3498
|
+
whereClauses: recursiveWhere,
|
|
3499
|
+
direction: options.direction,
|
|
3500
|
+
forceWorktableOuterJoinOrder,
|
|
3501
|
+
schema: options.schema
|
|
3502
|
+
});
|
|
3503
|
+
return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
|
|
3504
|
+
}
|
|
3505
|
+
function compileRecursiveBranch(options) {
|
|
3506
|
+
const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join([...options.recursiveColumns], drizzleOrm.sql`, `)}`;
|
|
3507
|
+
switch (options.direction) {
|
|
3508
|
+
case "out": {
|
|
3509
|
+
return buildDirectionalBranch({
|
|
3510
|
+
selectClause,
|
|
3511
|
+
whereClauses: options.whereClauses,
|
|
3512
|
+
joinField: "from_id",
|
|
3513
|
+
targetField: "to_id",
|
|
3514
|
+
targetKindField: "to_kind",
|
|
3515
|
+
forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
|
|
3516
|
+
schema: options.schema
|
|
3517
|
+
});
|
|
3518
|
+
}
|
|
3519
|
+
case "in": {
|
|
3520
|
+
return buildDirectionalBranch({
|
|
3521
|
+
selectClause,
|
|
3522
|
+
whereClauses: options.whereClauses,
|
|
3523
|
+
joinField: "to_id",
|
|
3524
|
+
targetField: "from_id",
|
|
3525
|
+
targetKindField: "from_kind",
|
|
3526
|
+
forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
|
|
3527
|
+
schema: options.schema
|
|
3528
|
+
});
|
|
3529
|
+
}
|
|
3530
|
+
case "both": {
|
|
3531
|
+
return buildBidirectionalBranch({
|
|
3532
|
+
selectClause,
|
|
3533
|
+
whereClauses: options.whereClauses,
|
|
3534
|
+
forceWorktableOuterJoinOrder: options.forceWorktableOuterJoinOrder,
|
|
3535
|
+
schema: options.schema
|
|
3536
|
+
});
|
|
3537
|
+
}
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
function buildDirectionalBranch(options) {
|
|
3541
|
+
const nodeJoin = drizzleOrm.sql`JOIN ${options.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(options.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(options.targetKindField)}`;
|
|
3542
|
+
if (options.forceWorktableOuterJoinOrder) {
|
|
3543
|
+
const allWhere = [
|
|
3544
|
+
...options.whereClauses,
|
|
3545
|
+
drizzleOrm.sql`e.${drizzleOrm.sql.raw(options.joinField)} = r.id`
|
|
3546
|
+
];
|
|
3547
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r CROSS JOIN ${options.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3548
|
+
}
|
|
3549
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r JOIN ${options.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(options.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join([...options.whereClauses], drizzleOrm.sql` AND `)}`;
|
|
3550
|
+
}
|
|
3551
|
+
function buildBidirectionalBranch(options) {
|
|
3552
|
+
const nodeJoin = drizzleOrm.sql`JOIN ${options.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
|
|
3553
|
+
if (options.forceWorktableOuterJoinOrder) {
|
|
3554
|
+
const allWhere = [
|
|
3555
|
+
...options.whereClauses,
|
|
3556
|
+
drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
|
|
3557
|
+
];
|
|
3558
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r CROSS JOIN ${options.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3559
|
+
}
|
|
3560
|
+
return drizzleOrm.sql`${options.selectClause} FROM reachable r JOIN ${options.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...options.whereClauses], drizzleOrm.sql` AND `)}`;
|
|
3561
|
+
}
|
|
3452
3562
|
|
|
3453
3563
|
// src/store/row-mappers.ts
|
|
3454
3564
|
function nullToUndefined2(value) {
|
|
@@ -3526,9 +3636,6 @@ function defineSubgraphProject(_graph) {
|
|
|
3526
3636
|
}
|
|
3527
3637
|
async function executeSubgraph(params) {
|
|
3528
3638
|
const { options } = params;
|
|
3529
|
-
if (options.edges.length === 0) {
|
|
3530
|
-
return { nodes: [], edges: [] };
|
|
3531
|
-
}
|
|
3532
3639
|
const maxDepth = Math.min(
|
|
3533
3640
|
options.maxDepth ?? DEFAULT_SUBGRAPH_MAX_DEPTH,
|
|
3534
3641
|
MAX_EXPLICIT_RECURSIVE_DEPTH
|
|
@@ -3542,6 +3649,8 @@ async function executeSubgraph(params) {
|
|
|
3542
3649
|
excludeRoot: options.excludeRoot ?? false,
|
|
3543
3650
|
direction: options.direction ?? "out",
|
|
3544
3651
|
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
3652
|
+
temporalMode: options.temporalMode ?? params.graph.defaults.temporalMode,
|
|
3653
|
+
asOf: options.asOf,
|
|
3545
3654
|
dialect: params.dialect,
|
|
3546
3655
|
schema: params.schema ?? DEFAULT_SQL_SCHEMA,
|
|
3547
3656
|
backend: params.backend
|
|
@@ -3559,21 +3668,47 @@ async function executeSubgraph(params) {
|
|
|
3559
3668
|
(kind, field2) => schemaIntrospector.getEdgeFieldTypeInfo(kind, field2),
|
|
3560
3669
|
"edge"
|
|
3561
3670
|
);
|
|
3562
|
-
const reachableCte = buildReachableCte(
|
|
3671
|
+
const reachableCte = buildReachableCte({
|
|
3672
|
+
graphId: ctx.graphId,
|
|
3673
|
+
sourceId: ctx.rootId,
|
|
3674
|
+
edgeKinds: ctx.edgeKinds,
|
|
3675
|
+
maxHops: ctx.maxDepth,
|
|
3676
|
+
direction: ctx.direction,
|
|
3677
|
+
cyclePolicy: ctx.cyclePolicy,
|
|
3678
|
+
includePath: false,
|
|
3679
|
+
temporalMode: ctx.temporalMode,
|
|
3680
|
+
...ctx.asOf !== void 0 && { asOf: ctx.asOf },
|
|
3681
|
+
dialect: ctx.dialect,
|
|
3682
|
+
schema: ctx.schema
|
|
3683
|
+
});
|
|
3563
3684
|
const includedIdsCte = buildIncludedIdsCte(ctx);
|
|
3564
3685
|
const [nodeRows, edgeRows] = await Promise.all([
|
|
3565
3686
|
fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, nodeProjectionPlan),
|
|
3566
3687
|
fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, edgeProjectionPlan)
|
|
3567
3688
|
]);
|
|
3568
|
-
const
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
);
|
|
3689
|
+
const nodesMap = /* @__PURE__ */ new Map();
|
|
3690
|
+
for (const row of nodeRows) {
|
|
3691
|
+
const node = mapSubgraphNodeRow(row, nodeProjectionPlan);
|
|
3692
|
+
nodesMap.set(node.id, node);
|
|
3693
|
+
}
|
|
3694
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
3695
|
+
const reverseAdjacency = /* @__PURE__ */ new Map();
|
|
3696
|
+
for (const row of edgeRows) {
|
|
3697
|
+
const edge = mapSubgraphEdgeRow(row, edgeProjectionPlan);
|
|
3698
|
+
insertAdjacencyEntry(adjacency, edge.fromId, edge.kind, edge);
|
|
3699
|
+
insertAdjacencyEntry(
|
|
3700
|
+
reverseAdjacency,
|
|
3701
|
+
edge.toId,
|
|
3702
|
+
edge.kind,
|
|
3703
|
+
edge
|
|
3704
|
+
);
|
|
3705
|
+
}
|
|
3706
|
+
const root = nodesMap.get(ctx.rootId);
|
|
3574
3707
|
return {
|
|
3575
|
-
|
|
3576
|
-
|
|
3708
|
+
root,
|
|
3709
|
+
nodes: nodesMap,
|
|
3710
|
+
adjacency,
|
|
3711
|
+
reverseAdjacency
|
|
3577
3712
|
};
|
|
3578
3713
|
}
|
|
3579
3714
|
var introspectorCache = /* @__PURE__ */ new WeakMap();
|
|
@@ -3643,79 +3778,6 @@ function getIncludedNodeKinds(graph, includeKinds) {
|
|
|
3643
3778
|
function dedupeStrings(values) {
|
|
3644
3779
|
return [...new Set(values)];
|
|
3645
3780
|
}
|
|
3646
|
-
function buildReachableCte(ctx) {
|
|
3647
|
-
const shouldTrackPath = ctx.cyclePolicy === "prevent";
|
|
3648
|
-
const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
|
|
3649
|
-
const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3650
|
-
const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
|
|
3651
|
-
const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
|
|
3652
|
-
const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
|
|
3653
|
-
if (initialPath !== void 0) {
|
|
3654
|
-
baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
|
|
3655
|
-
}
|
|
3656
|
-
const baseCase = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(baseColumns, drizzleOrm.sql`, `)} FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id = ${ctx.rootId} AND n.deleted_at IS NULL`;
|
|
3657
|
-
const recursiveColumns = [
|
|
3658
|
-
drizzleOrm.sql`n.id`,
|
|
3659
|
-
drizzleOrm.sql`n.kind`,
|
|
3660
|
-
drizzleOrm.sql`r.depth + 1 AS depth`
|
|
3661
|
-
];
|
|
3662
|
-
if (pathExtension !== void 0) {
|
|
3663
|
-
recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
|
|
3664
|
-
}
|
|
3665
|
-
const recursiveWhereClauses = [
|
|
3666
|
-
drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
|
|
3667
|
-
edgeKindFilter,
|
|
3668
|
-
drizzleOrm.sql`e.deleted_at IS NULL`,
|
|
3669
|
-
drizzleOrm.sql`n.deleted_at IS NULL`,
|
|
3670
|
-
drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
|
|
3671
|
-
];
|
|
3672
|
-
if (cycleCheck !== void 0) {
|
|
3673
|
-
recursiveWhereClauses.push(cycleCheck);
|
|
3674
|
-
}
|
|
3675
|
-
const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
|
|
3676
|
-
const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
|
|
3677
|
-
recursiveColumns,
|
|
3678
|
-
whereClauses: recursiveWhereClauses,
|
|
3679
|
-
forceWorktableOuterJoinOrder,
|
|
3680
|
-
schema: ctx.schema
|
|
3681
|
-
}) : compileRecursiveBranch({
|
|
3682
|
-
recursiveColumns,
|
|
3683
|
-
whereClauses: recursiveWhereClauses,
|
|
3684
|
-
joinField: "from_id",
|
|
3685
|
-
targetField: "to_id",
|
|
3686
|
-
targetKindField: "to_kind",
|
|
3687
|
-
forceWorktableOuterJoinOrder,
|
|
3688
|
-
schema: ctx.schema
|
|
3689
|
-
});
|
|
3690
|
-
return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
|
|
3691
|
-
}
|
|
3692
|
-
function compileRecursiveBranch(params) {
|
|
3693
|
-
const columns = [...params.recursiveColumns];
|
|
3694
|
-
const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
|
|
3695
|
-
const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND n.id = e.${drizzleOrm.sql.raw(params.targetField)} AND n.kind = e.${drizzleOrm.sql.raw(params.targetKindField)}`;
|
|
3696
|
-
if (params.forceWorktableOuterJoinOrder) {
|
|
3697
|
-
const allWhere = [
|
|
3698
|
-
...params.whereClauses,
|
|
3699
|
-
drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
|
|
3700
|
-
];
|
|
3701
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3702
|
-
}
|
|
3703
|
-
const where = [...params.whereClauses];
|
|
3704
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON e.${drizzleOrm.sql.raw(params.joinField)} = r.id ${nodeJoin} WHERE ${drizzleOrm.sql.join(where, drizzleOrm.sql` AND `)}`;
|
|
3705
|
-
}
|
|
3706
|
-
function compileBidirectionalBranch(params) {
|
|
3707
|
-
const columns = [...params.recursiveColumns];
|
|
3708
|
-
const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
|
|
3709
|
-
const nodeJoin = drizzleOrm.sql`JOIN ${params.schema.nodesTable} n ON n.graph_id = e.graph_id AND ((e.to_id = r.id AND n.id = e.from_id AND n.kind = e.from_kind) OR (e.from_id = r.id AND n.id = e.to_id AND n.kind = e.to_kind))`;
|
|
3710
|
-
if (params.forceWorktableOuterJoinOrder) {
|
|
3711
|
-
const allWhere = [
|
|
3712
|
-
...params.whereClauses,
|
|
3713
|
-
drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
|
|
3714
|
-
];
|
|
3715
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
|
|
3716
|
-
}
|
|
3717
|
-
return drizzleOrm.sql`${selectClause} FROM reachable r JOIN ${params.schema.edgesTable} e ON (e.from_id = r.id OR e.to_id = r.id) ${nodeJoin} WHERE ${drizzleOrm.sql.join([...params.whereClauses], drizzleOrm.sql` AND `)}`;
|
|
3718
|
-
}
|
|
3719
3781
|
function buildIncludedIdsCte(ctx) {
|
|
3720
3782
|
const filters = [];
|
|
3721
3783
|
if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
|
|
@@ -3747,6 +3809,12 @@ async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte, projectionP
|
|
|
3747
3809
|
}
|
|
3748
3810
|
async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionPlan) {
|
|
3749
3811
|
const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
|
|
3812
|
+
const edgeTemporalFilter = compileTemporalFilter({
|
|
3813
|
+
mode: ctx.temporalMode,
|
|
3814
|
+
asOf: ctx.asOf,
|
|
3815
|
+
tableAlias: "e",
|
|
3816
|
+
currentTimestamp: ctx.dialect.currentTimestamp()
|
|
3817
|
+
});
|
|
3750
3818
|
const columns = [
|
|
3751
3819
|
drizzleOrm.sql`e.id`,
|
|
3752
3820
|
drizzleOrm.sql`e.kind`,
|
|
@@ -3764,7 +3832,7 @@ async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte, projectionP
|
|
|
3764
3832
|
]),
|
|
3765
3833
|
...buildProjectedPropertyColumns("e", projectionPlan, ctx.dialect)
|
|
3766
3834
|
];
|
|
3767
|
-
const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND
|
|
3835
|
+
const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)} FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND ${edgeTemporalFilter} AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
|
|
3768
3836
|
return ctx.backend.execute(query);
|
|
3769
3837
|
}
|
|
3770
3838
|
function buildMetadataColumns(alias, plan, columns) {
|
|
@@ -3813,6 +3881,19 @@ function buildProjectedPropertyColumns(alias, plan, dialect) {
|
|
|
3813
3881
|
}
|
|
3814
3882
|
return columns;
|
|
3815
3883
|
}
|
|
3884
|
+
function insertAdjacencyEntry(index, nodeId, edgeKind, edge) {
|
|
3885
|
+
let kindMap = index.get(nodeId);
|
|
3886
|
+
if (kindMap === void 0) {
|
|
3887
|
+
kindMap = /* @__PURE__ */ new Map();
|
|
3888
|
+
index.set(nodeId, kindMap);
|
|
3889
|
+
}
|
|
3890
|
+
const edges = kindMap.get(edgeKind);
|
|
3891
|
+
if (edges === void 0) {
|
|
3892
|
+
kindMap.set(edgeKind, [edge]);
|
|
3893
|
+
} else {
|
|
3894
|
+
edges.push(edge);
|
|
3895
|
+
}
|
|
3896
|
+
}
|
|
3816
3897
|
function applyProjectedFields(target, row, kindPlan) {
|
|
3817
3898
|
for (const fieldPlan of kindPlan.propertyFields) {
|
|
3818
3899
|
target[fieldPlan.field] = decodeSelectedValue(
|
|
@@ -8896,6 +8977,246 @@ function extractEdgeTypes(graph) {
|
|
|
8896
8977
|
return result;
|
|
8897
8978
|
}
|
|
8898
8979
|
|
|
8980
|
+
// src/store/algorithms/context.ts
|
|
8981
|
+
var DEFAULT_ALGORITHM_MAX_HOPS = 10;
|
|
8982
|
+
var DEFAULT_NEIGHBOR_DEPTH = 1;
|
|
8983
|
+
function resolveTemporalOptions(ctx, options) {
|
|
8984
|
+
return {
|
|
8985
|
+
temporalMode: options.temporalMode ?? ctx.defaultTemporalMode,
|
|
8986
|
+
...options.asOf !== void 0 && { asOf: options.asOf }
|
|
8987
|
+
};
|
|
8988
|
+
}
|
|
8989
|
+
function resolveTemporalFilter(ctx, options, tableAlias) {
|
|
8990
|
+
const resolved = resolveTemporalOptions(ctx, options);
|
|
8991
|
+
const filterOptions = {
|
|
8992
|
+
mode: resolved.temporalMode,
|
|
8993
|
+
asOf: resolved.asOf,
|
|
8994
|
+
tableAlias,
|
|
8995
|
+
currentTimestamp: ctx.dialect.currentTimestamp()
|
|
8996
|
+
};
|
|
8997
|
+
return compileTemporalFilter(filterOptions);
|
|
8998
|
+
}
|
|
8999
|
+
function resolveMaxHops(rawMaxHops, fallback, optionName) {
|
|
9000
|
+
const value = rawMaxHops ?? fallback;
|
|
9001
|
+
if (!Number.isFinite(value) || !Number.isInteger(value)) {
|
|
9002
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9003
|
+
`Graph algorithm ${optionName} must be a finite integer, got ${String(value)}.`,
|
|
9004
|
+
{ option: optionName, value }
|
|
9005
|
+
);
|
|
9006
|
+
}
|
|
9007
|
+
if (value < 1) {
|
|
9008
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9009
|
+
`Graph algorithm ${optionName} must be at least 1, got ${value}.`,
|
|
9010
|
+
{ option: optionName, value }
|
|
9011
|
+
);
|
|
9012
|
+
}
|
|
9013
|
+
if (value > MAX_EXPLICIT_RECURSIVE_DEPTH) {
|
|
9014
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9015
|
+
`Graph algorithm ${optionName} (${value}) exceeds the maximum of ${MAX_EXPLICIT_RECURSIVE_DEPTH}.`,
|
|
9016
|
+
{ option: optionName, value, limit: MAX_EXPLICIT_RECURSIVE_DEPTH }
|
|
9017
|
+
);
|
|
9018
|
+
}
|
|
9019
|
+
return value;
|
|
9020
|
+
}
|
|
9021
|
+
function assertEdgeKinds(edges) {
|
|
9022
|
+
if (edges.length === 0) {
|
|
9023
|
+
throw new chunkO5PST6TB_cjs.ConfigurationError(
|
|
9024
|
+
`Graph algorithms require at least one edge kind in 'edges'.`,
|
|
9025
|
+
{ edges }
|
|
9026
|
+
);
|
|
9027
|
+
}
|
|
9028
|
+
}
|
|
9029
|
+
|
|
9030
|
+
// src/store/algorithms/degree.ts
|
|
9031
|
+
async function executeDegree(ctx, nodeId, options = {}) {
|
|
9032
|
+
const direction = options.direction ?? "both";
|
|
9033
|
+
const whereClauses = [
|
|
9034
|
+
drizzleOrm.sql`graph_id = ${ctx.graphId}`,
|
|
9035
|
+
resolveTemporalFilter(ctx, options),
|
|
9036
|
+
compileDirectionFilter(direction, nodeId)
|
|
9037
|
+
];
|
|
9038
|
+
if (options.edges !== void 0) {
|
|
9039
|
+
whereClauses.push(compileKindFilter(drizzleOrm.sql.raw("kind"), options.edges));
|
|
9040
|
+
}
|
|
9041
|
+
const countExpr = direction === "both" ? drizzleOrm.sql`COUNT(DISTINCT id)` : drizzleOrm.sql`COUNT(*)`;
|
|
9042
|
+
const query = drizzleOrm.sql`SELECT ${countExpr} AS count FROM ${ctx.schema.edgesTable} WHERE ${drizzleOrm.sql.join(whereClauses, drizzleOrm.sql` AND `)}`;
|
|
9043
|
+
const rows = await ctx.backend.execute(query);
|
|
9044
|
+
return Number(rows[0]?.count ?? 0);
|
|
9045
|
+
}
|
|
9046
|
+
function compileDirectionFilter(direction, nodeId) {
|
|
9047
|
+
switch (direction) {
|
|
9048
|
+
case "out": {
|
|
9049
|
+
return drizzleOrm.sql`from_id = ${nodeId}`;
|
|
9050
|
+
}
|
|
9051
|
+
case "in": {
|
|
9052
|
+
return drizzleOrm.sql`to_id = ${nodeId}`;
|
|
9053
|
+
}
|
|
9054
|
+
case "both": {
|
|
9055
|
+
return drizzleOrm.sql`(from_id = ${nodeId} OR to_id = ${nodeId})`;
|
|
9056
|
+
}
|
|
9057
|
+
}
|
|
9058
|
+
}
|
|
9059
|
+
async function executeReachable(ctx, sourceId, options) {
|
|
9060
|
+
assertEdgeKinds(options.edges);
|
|
9061
|
+
const maxHops = resolveMaxHops(
|
|
9062
|
+
options.maxHops,
|
|
9063
|
+
DEFAULT_ALGORITHM_MAX_HOPS,
|
|
9064
|
+
"maxHops"
|
|
9065
|
+
);
|
|
9066
|
+
const cte = buildReachableCte({
|
|
9067
|
+
graphId: ctx.graphId,
|
|
9068
|
+
sourceId,
|
|
9069
|
+
edgeKinds: options.edges,
|
|
9070
|
+
maxHops,
|
|
9071
|
+
direction: options.direction ?? "out",
|
|
9072
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9073
|
+
includePath: false,
|
|
9074
|
+
...resolveTemporalOptions(ctx, options),
|
|
9075
|
+
dialect: ctx.dialect,
|
|
9076
|
+
schema: ctx.schema
|
|
9077
|
+
});
|
|
9078
|
+
const sourceFilter = options.excludeSource === true ? drizzleOrm.sql` WHERE id != ${sourceId}` : drizzleOrm.sql``;
|
|
9079
|
+
const query = drizzleOrm.sql`${cte} SELECT id, kind, MIN(depth) AS depth FROM reachable${sourceFilter} GROUP BY id, kind ORDER BY depth ASC, id ASC`;
|
|
9080
|
+
const rows = await ctx.backend.execute(query);
|
|
9081
|
+
return rows.map((row) => ({
|
|
9082
|
+
id: row.id,
|
|
9083
|
+
kind: row.kind,
|
|
9084
|
+
depth: Number(row.depth)
|
|
9085
|
+
}));
|
|
9086
|
+
}
|
|
9087
|
+
async function executeCanReach(ctx, sourceId, targetId, options) {
|
|
9088
|
+
assertEdgeKinds(options.edges);
|
|
9089
|
+
const maxHops = resolveMaxHops(
|
|
9090
|
+
options.maxHops,
|
|
9091
|
+
DEFAULT_ALGORITHM_MAX_HOPS,
|
|
9092
|
+
"maxHops"
|
|
9093
|
+
);
|
|
9094
|
+
const cte = buildReachableCte({
|
|
9095
|
+
graphId: ctx.graphId,
|
|
9096
|
+
sourceId,
|
|
9097
|
+
edgeKinds: options.edges,
|
|
9098
|
+
maxHops,
|
|
9099
|
+
direction: options.direction ?? "out",
|
|
9100
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9101
|
+
includePath: false,
|
|
9102
|
+
...resolveTemporalOptions(ctx, options),
|
|
9103
|
+
dialect: ctx.dialect,
|
|
9104
|
+
schema: ctx.schema
|
|
9105
|
+
});
|
|
9106
|
+
const query = drizzleOrm.sql`${cte} SELECT 1 AS hit FROM reachable WHERE id = ${targetId} LIMIT 1`;
|
|
9107
|
+
const rows = await ctx.backend.execute(query);
|
|
9108
|
+
return rows.length > 0;
|
|
9109
|
+
}
|
|
9110
|
+
async function executeNeighbors(ctx, sourceId, options) {
|
|
9111
|
+
const depth = resolveMaxHops(options.depth, DEFAULT_NEIGHBOR_DEPTH, "depth");
|
|
9112
|
+
return executeReachable(ctx, sourceId, {
|
|
9113
|
+
edges: options.edges,
|
|
9114
|
+
maxHops: depth,
|
|
9115
|
+
direction: options.direction ?? "out",
|
|
9116
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9117
|
+
...options.temporalMode !== void 0 && {
|
|
9118
|
+
temporalMode: options.temporalMode
|
|
9119
|
+
},
|
|
9120
|
+
...options.asOf !== void 0 && { asOf: options.asOf },
|
|
9121
|
+
excludeSource: true
|
|
9122
|
+
});
|
|
9123
|
+
}
|
|
9124
|
+
async function executeShortestPath(ctx, sourceId, targetId, options) {
|
|
9125
|
+
assertEdgeKinds(options.edges);
|
|
9126
|
+
const maxHops = resolveMaxHops(
|
|
9127
|
+
options.maxHops,
|
|
9128
|
+
DEFAULT_ALGORITHM_MAX_HOPS,
|
|
9129
|
+
"maxHops"
|
|
9130
|
+
);
|
|
9131
|
+
if (sourceId === targetId) {
|
|
9132
|
+
const source = await fetchNodeKind(ctx, sourceId, options);
|
|
9133
|
+
if (source === void 0) return void 0;
|
|
9134
|
+
return { nodes: [source], depth: 0 };
|
|
9135
|
+
}
|
|
9136
|
+
const cte = buildReachableCte({
|
|
9137
|
+
graphId: ctx.graphId,
|
|
9138
|
+
sourceId,
|
|
9139
|
+
edgeKinds: options.edges,
|
|
9140
|
+
maxHops,
|
|
9141
|
+
direction: options.direction ?? "out",
|
|
9142
|
+
cyclePolicy: options.cyclePolicy ?? "prevent",
|
|
9143
|
+
includePath: true,
|
|
9144
|
+
...resolveTemporalOptions(ctx, options),
|
|
9145
|
+
dialect: ctx.dialect,
|
|
9146
|
+
schema: ctx.schema
|
|
9147
|
+
});
|
|
9148
|
+
const pathContainsCheck = drizzleOrm.sql`NOT (${ctx.dialect.cycleCheck(drizzleOrm.sql.raw("r.id"), drizzleOrm.sql.raw("h.path"))})`;
|
|
9149
|
+
const query = drizzleOrm.sql`${cte}, hit AS (SELECT id, kind, depth, path FROM reachable WHERE id = ${targetId} ORDER BY depth ASC LIMIT 1) SELECT h.id AS hit_id, h.kind AS hit_kind, h.depth AS hit_depth, h.path AS hit_path, r.id AS node_id, r.kind AS node_kind FROM hit h LEFT JOIN reachable r ON ${pathContainsCheck}`;
|
|
9150
|
+
const rows = await ctx.backend.execute(query);
|
|
9151
|
+
const first = rows[0];
|
|
9152
|
+
if (first === void 0) return void 0;
|
|
9153
|
+
const pathIds = normalizePath(first.hit_path);
|
|
9154
|
+
const kindById = buildKindIndex(rows);
|
|
9155
|
+
const pathNodes = pathIds.map((id) => ({
|
|
9156
|
+
id,
|
|
9157
|
+
kind: kindById.get(id) ?? ""
|
|
9158
|
+
}));
|
|
9159
|
+
return { nodes: pathNodes, depth: Number(first.hit_depth) };
|
|
9160
|
+
}
|
|
9161
|
+
function buildKindIndex(rows) {
|
|
9162
|
+
const map = /* @__PURE__ */ new Map();
|
|
9163
|
+
for (const row of rows) {
|
|
9164
|
+
if (row.node_id !== void 0 && row.node_kind !== void 0) {
|
|
9165
|
+
map.set(row.node_id, row.node_kind);
|
|
9166
|
+
}
|
|
9167
|
+
}
|
|
9168
|
+
return map;
|
|
9169
|
+
}
|
|
9170
|
+
async function fetchNodeKind(ctx, nodeId, options) {
|
|
9171
|
+
const temporalFilter = resolveTemporalFilter(ctx, options);
|
|
9172
|
+
const query = drizzleOrm.sql`SELECT id, kind FROM ${ctx.schema.nodesTable} WHERE graph_id = ${ctx.graphId} AND id = ${nodeId} AND ${temporalFilter} LIMIT 1`;
|
|
9173
|
+
const rows = await ctx.backend.execute(query);
|
|
9174
|
+
const row = rows[0];
|
|
9175
|
+
if (row === void 0) return void 0;
|
|
9176
|
+
return { id: row.id, kind: row.kind };
|
|
9177
|
+
}
|
|
9178
|
+
|
|
9179
|
+
// src/store/algorithms/index.ts
|
|
9180
|
+
function resolveNodeId(value) {
|
|
9181
|
+
return typeof value === "string" ? value : value.id;
|
|
9182
|
+
}
|
|
9183
|
+
function createGraphAlgorithms(params) {
|
|
9184
|
+
const ctx = {
|
|
9185
|
+
graphId: params.graphId,
|
|
9186
|
+
backend: params.backend,
|
|
9187
|
+
dialect: chunkMME3H4ZF_cjs.getDialect(params.backend.dialect),
|
|
9188
|
+
schema: params.schema ?? (params.backend.tableNames ? createSqlSchema(params.backend.tableNames) : createSqlSchema()),
|
|
9189
|
+
defaultTemporalMode: params.defaultTemporalMode
|
|
9190
|
+
};
|
|
9191
|
+
return {
|
|
9192
|
+
shortestPath(from, to, options) {
|
|
9193
|
+
return executeShortestPath(
|
|
9194
|
+
ctx,
|
|
9195
|
+
resolveNodeId(from),
|
|
9196
|
+
resolveNodeId(to),
|
|
9197
|
+
options
|
|
9198
|
+
);
|
|
9199
|
+
},
|
|
9200
|
+
reachable(from, options) {
|
|
9201
|
+
return executeReachable(ctx, resolveNodeId(from), options);
|
|
9202
|
+
},
|
|
9203
|
+
canReach(from, to, options) {
|
|
9204
|
+
return executeCanReach(
|
|
9205
|
+
ctx,
|
|
9206
|
+
resolveNodeId(from),
|
|
9207
|
+
resolveNodeId(to),
|
|
9208
|
+
options
|
|
9209
|
+
);
|
|
9210
|
+
},
|
|
9211
|
+
neighbors(node, options) {
|
|
9212
|
+
return executeNeighbors(ctx, resolveNodeId(node), options);
|
|
9213
|
+
},
|
|
9214
|
+
degree(node, options) {
|
|
9215
|
+
return executeDegree(ctx, resolveNodeId(node), options ?? {});
|
|
9216
|
+
}
|
|
9217
|
+
};
|
|
9218
|
+
}
|
|
9219
|
+
|
|
8899
9220
|
// src/store/collections/edge-collection.ts
|
|
8900
9221
|
function narrowEdge(edge) {
|
|
8901
9222
|
return edge;
|
|
@@ -11663,6 +11984,7 @@ var Store = class {
|
|
|
11663
11984
|
#defaultTraversalExpansion;
|
|
11664
11985
|
#nodeCollections;
|
|
11665
11986
|
#edgeCollections;
|
|
11987
|
+
#algorithms;
|
|
11666
11988
|
constructor(graph, backend, options) {
|
|
11667
11989
|
this.#graph = graph;
|
|
11668
11990
|
this.#backend = backend;
|
|
@@ -11744,6 +12066,38 @@ var Store = class {
|
|
|
11744
12066
|
}
|
|
11745
12067
|
return this.#edgeCollections;
|
|
11746
12068
|
}
|
|
12069
|
+
// === Graph Algorithms ===
|
|
12070
|
+
/**
|
|
12071
|
+
* Tier 1 graph algorithms: shortest path, reachability, neighborhoods, and
|
|
12072
|
+
* degree centrality.
|
|
12073
|
+
*
|
|
12074
|
+
* Each call compiles to a single recursive-CTE query against the backend.
|
|
12075
|
+
* The facade is built lazily on first access and cached for the lifetime
|
|
12076
|
+
* of the store.
|
|
12077
|
+
*
|
|
12078
|
+
* @example
|
|
12079
|
+
* ```typescript
|
|
12080
|
+
* const path = await store.algorithms.shortestPath(alice, bob, {
|
|
12081
|
+
* edges: ["knows"],
|
|
12082
|
+
* });
|
|
12083
|
+
*
|
|
12084
|
+
* const friends = await store.algorithms.neighbors(alice, {
|
|
12085
|
+
* edges: ["knows"],
|
|
12086
|
+
* depth: 2,
|
|
12087
|
+
* });
|
|
12088
|
+
* ```
|
|
12089
|
+
*/
|
|
12090
|
+
get algorithms() {
|
|
12091
|
+
if (this.#algorithms === void 0) {
|
|
12092
|
+
this.#algorithms = createGraphAlgorithms({
|
|
12093
|
+
graphId: this.graphId,
|
|
12094
|
+
backend: this.#backend,
|
|
12095
|
+
schema: this.#schema,
|
|
12096
|
+
defaultTemporalMode: this.#graph.defaults.temporalMode
|
|
12097
|
+
});
|
|
12098
|
+
}
|
|
12099
|
+
return this.#algorithms;
|
|
12100
|
+
}
|
|
11747
12101
|
// === Dynamic Collection Access ===
|
|
11748
12102
|
/**
|
|
11749
12103
|
* Returns the node collection for the given kind, or undefined if the kind
|
|
@@ -11924,22 +12278,26 @@ var Store = class {
|
|
|
11924
12278
|
* Extracts a typed subgraph by traversing from a root node.
|
|
11925
12279
|
*
|
|
11926
12280
|
* Performs a BFS traversal from `rootId` following the specified edge kinds,
|
|
11927
|
-
*
|
|
12281
|
+
* returning an indexed result with adjacency maps for immediate traversal.
|
|
11928
12282
|
*
|
|
11929
12283
|
* @example
|
|
11930
12284
|
* ```typescript
|
|
11931
|
-
* const
|
|
12285
|
+
* const sg = await store.subgraph(run.id, {
|
|
11932
12286
|
* edges: ["has_task", "runs_agent", "uses_skill"],
|
|
11933
12287
|
* maxDepth: 4,
|
|
11934
|
-
* includeKinds: ["Run", "Task", "Agent", "Skill"],
|
|
11935
12288
|
* });
|
|
11936
12289
|
*
|
|
11937
|
-
*
|
|
11938
|
-
*
|
|
11939
|
-
*
|
|
11940
|
-
*
|
|
11941
|
-
*
|
|
11942
|
-
*
|
|
12290
|
+
* // Root node (the traversal starting point)
|
|
12291
|
+
* console.log(sg.root?.kind);
|
|
12292
|
+
*
|
|
12293
|
+
* // Lookup by ID
|
|
12294
|
+
* const task = sg.nodes.get(taskId);
|
|
12295
|
+
*
|
|
12296
|
+
* // Forward adjacency: edges of a kind from a node
|
|
12297
|
+
* const taskEdges = sg.adjacency.get(run.id)?.get("has_task") ?? [];
|
|
12298
|
+
*
|
|
12299
|
+
* // Reverse adjacency: edges of a kind pointing to a node
|
|
12300
|
+
* const parentEdges = sg.reverseAdjacency.get(taskId)?.get("has_task") ?? [];
|
|
11943
12301
|
* ```
|
|
11944
12302
|
*/
|
|
11945
12303
|
async subgraph(rootId, options) {
|
|
@@ -12018,6 +12376,7 @@ var Store = class {
|
|
|
12018
12376
|
await (this.#backend.capabilities.transactions ? this.#backend.transaction(async (tx) => doClear(tx)) : doClear(this.#backend));
|
|
12019
12377
|
this.#nodeCollections = void 0;
|
|
12020
12378
|
this.#edgeCollections = void 0;
|
|
12379
|
+
this.#algorithms = void 0;
|
|
12021
12380
|
}
|
|
12022
12381
|
// === Lifecycle ===
|
|
12023
12382
|
/**
|