@nicia-ai/typegraph 0.9.2 → 0.10.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 CHANGED
@@ -3033,7 +3033,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
3033
3033
  recursiveBaseWhereClauses.push(cycleCheck);
3034
3034
  }
3035
3035
  recursiveBaseWhereClauses.push(...edgePredicates, ...targetNodePredicates);
3036
- function compileRecursiveBranch(branch) {
3036
+ function compileRecursiveBranch2(branch) {
3037
3037
  const recursiveFilterClauses = [
3038
3038
  ...recursiveBaseWhereClauses,
3039
3039
  compileKindFilter2(branch.edgeKinds, "e.kind"),
@@ -3088,7 +3088,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
3088
3088
  const directTargetField = direction === "out" ? "to_id" : "from_id";
3089
3089
  const directJoinKindField = direction === "out" ? "from_kind" : "to_kind";
3090
3090
  const directTargetKindField = direction === "out" ? "to_kind" : "from_kind";
3091
- const directBranch = compileRecursiveBranch({
3091
+ const directBranch = compileRecursiveBranch2({
3092
3092
  joinField: directJoinField,
3093
3093
  targetField: directTargetField,
3094
3094
  joinKindField: directJoinKindField,
@@ -3104,7 +3104,7 @@ function compileRecursiveCte(ast, traversal, graphId, ctx, requiredColumnsByAlia
3104
3104
  (kind) => directEdgeKinds.includes(kind)
3105
3105
  );
3106
3106
  const duplicateGuard = overlappingKinds.length > 0 ? drizzleOrm.sql`NOT (e.from_id = e.to_id AND ${compileKindFilter2(overlappingKinds, "e.kind")})` : void 0;
3107
- const inverseBranch = compileRecursiveBranch({
3107
+ const inverseBranch = compileRecursiveBranch2({
3108
3108
  joinField: inverseJoinField,
3109
3109
  targetField: inverseTargetField,
3110
3110
  joinKindField: inverseJoinKindField,
@@ -11188,6 +11188,142 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11188
11188
  }
11189
11189
  return results;
11190
11190
  }
11191
+ var DEFAULT_SUBGRAPH_MAX_DEPTH = 10;
11192
+ function normalizeProps(value) {
11193
+ return typeof value === "string" ? value : JSON.stringify(value ?? {});
11194
+ }
11195
+ async function executeSubgraph(params) {
11196
+ const { options } = params;
11197
+ if (options.edges.length === 0) {
11198
+ return { nodes: [], edges: [] };
11199
+ }
11200
+ const maxDepth = Math.min(
11201
+ options.maxDepth ?? DEFAULT_SUBGRAPH_MAX_DEPTH,
11202
+ MAX_RECURSIVE_DEPTH
11203
+ );
11204
+ const ctx = {
11205
+ graphId: params.graphId,
11206
+ rootId: params.rootId,
11207
+ edgeKinds: options.edges,
11208
+ maxDepth,
11209
+ includeKinds: options.includeKinds,
11210
+ excludeRoot: options.excludeRoot ?? false,
11211
+ direction: options.direction ?? "out",
11212
+ cyclePolicy: options.cyclePolicy ?? "prevent",
11213
+ dialect: params.dialect,
11214
+ schema: params.schema ?? DEFAULT_SQL_SCHEMA,
11215
+ backend: params.backend
11216
+ };
11217
+ const reachableCte = buildReachableCte(ctx);
11218
+ const includedIdsCte = buildIncludedIdsCte(ctx);
11219
+ const [nodeRows, edgeRows] = await Promise.all([
11220
+ fetchSubgraphNodes(ctx, reachableCte, includedIdsCte),
11221
+ fetchSubgraphEdges(ctx, reachableCte, includedIdsCte)
11222
+ ]);
11223
+ const nodes = nodeRows.map(
11224
+ (row) => rowToNode({ ...row, props: normalizeProps(row.props) })
11225
+ );
11226
+ const edges = edgeRows.map(
11227
+ (row) => rowToEdge({ ...row, props: normalizeProps(row.props) })
11228
+ );
11229
+ return {
11230
+ nodes,
11231
+ edges
11232
+ };
11233
+ }
11234
+ function buildReachableCte(ctx) {
11235
+ const shouldTrackPath = ctx.cyclePolicy === "prevent";
11236
+ const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
11237
+ const initialPath = shouldTrackPath ? ctx.dialect.initializePath(drizzleOrm.sql.raw("n.id")) : void 0;
11238
+ const pathExtension = shouldTrackPath ? ctx.dialect.extendPath(drizzleOrm.sql.raw("r.path"), drizzleOrm.sql.raw("n.id")) : void 0;
11239
+ const cycleCheck = shouldTrackPath ? ctx.dialect.cycleCheck(drizzleOrm.sql.raw("n.id"), drizzleOrm.sql.raw("r.path")) : void 0;
11240
+ const baseColumns = [drizzleOrm.sql`n.id`, drizzleOrm.sql`n.kind`, drizzleOrm.sql`0 AS depth`];
11241
+ if (initialPath !== void 0) {
11242
+ baseColumns.push(drizzleOrm.sql`${initialPath} AS path`);
11243
+ }
11244
+ 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`;
11245
+ const recursiveColumns = [
11246
+ drizzleOrm.sql`n.id`,
11247
+ drizzleOrm.sql`n.kind`,
11248
+ drizzleOrm.sql`r.depth + 1 AS depth`
11249
+ ];
11250
+ if (pathExtension !== void 0) {
11251
+ recursiveColumns.push(drizzleOrm.sql`${pathExtension} AS path`);
11252
+ }
11253
+ const recursiveWhereClauses = [
11254
+ drizzleOrm.sql`e.graph_id = ${ctx.graphId}`,
11255
+ edgeKindFilter,
11256
+ drizzleOrm.sql`e.deleted_at IS NULL`,
11257
+ drizzleOrm.sql`n.deleted_at IS NULL`,
11258
+ drizzleOrm.sql`r.depth < ${ctx.maxDepth}`
11259
+ ];
11260
+ if (cycleCheck !== void 0) {
11261
+ recursiveWhereClauses.push(cycleCheck);
11262
+ }
11263
+ const forceWorktableOuterJoinOrder = ctx.dialect.capabilities.forceRecursiveWorktableOuterJoinOrder;
11264
+ const recursiveCase = ctx.direction === "both" ? compileBidirectionalBranch({
11265
+ recursiveColumns,
11266
+ whereClauses: recursiveWhereClauses,
11267
+ forceWorktableOuterJoinOrder,
11268
+ schema: ctx.schema
11269
+ }) : compileRecursiveBranch({
11270
+ recursiveColumns,
11271
+ whereClauses: recursiveWhereClauses,
11272
+ joinField: "from_id",
11273
+ targetField: "to_id",
11274
+ targetKindField: "to_kind",
11275
+ forceWorktableOuterJoinOrder,
11276
+ schema: ctx.schema
11277
+ });
11278
+ return drizzleOrm.sql`WITH RECURSIVE reachable AS (${baseCase} UNION ALL ${recursiveCase})`;
11279
+ }
11280
+ function compileRecursiveBranch(params) {
11281
+ const columns = [...params.recursiveColumns];
11282
+ const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
11283
+ 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)}`;
11284
+ if (params.forceWorktableOuterJoinOrder) {
11285
+ const allWhere = [
11286
+ ...params.whereClauses,
11287
+ drizzleOrm.sql`e.${drizzleOrm.sql.raw(params.joinField)} = r.id`
11288
+ ];
11289
+ return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
11290
+ }
11291
+ const where = [...params.whereClauses];
11292
+ 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 `)}`;
11293
+ }
11294
+ function compileBidirectionalBranch(params) {
11295
+ const columns = [...params.recursiveColumns];
11296
+ const selectClause = drizzleOrm.sql`SELECT ${drizzleOrm.sql.join(columns, drizzleOrm.sql`, `)}`;
11297
+ 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))`;
11298
+ if (params.forceWorktableOuterJoinOrder) {
11299
+ const allWhere = [
11300
+ ...params.whereClauses,
11301
+ drizzleOrm.sql`(e.from_id = r.id OR e.to_id = r.id)`
11302
+ ];
11303
+ return drizzleOrm.sql`${selectClause} FROM reachable r CROSS JOIN ${params.schema.edgesTable} e ${nodeJoin} WHERE ${drizzleOrm.sql.join(allWhere, drizzleOrm.sql` AND `)}`;
11304
+ }
11305
+ 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 `)}`;
11306
+ }
11307
+ function buildIncludedIdsCte(ctx) {
11308
+ const filters = [];
11309
+ if (ctx.includeKinds !== void 0 && ctx.includeKinds.length > 0) {
11310
+ filters.push(compileKindFilter(drizzleOrm.sql.raw("kind"), ctx.includeKinds));
11311
+ }
11312
+ if (ctx.excludeRoot) {
11313
+ filters.push(drizzleOrm.sql`id != ${ctx.rootId}`);
11314
+ }
11315
+ const whereClause = filters.length > 0 ? drizzleOrm.sql` WHERE ${drizzleOrm.sql.join(filters, drizzleOrm.sql` AND `)}` : drizzleOrm.sql``;
11316
+ return drizzleOrm.sql`, included_ids AS (SELECT DISTINCT id FROM reachable${whereClause})`;
11317
+ }
11318
+ async function fetchSubgraphNodes(ctx, reachableCte, includedIdsCte) {
11319
+ const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT n.kind, n.id, n.props, n.version, n.valid_from, n.valid_to, n.created_at, n.updated_at, n.deleted_at FROM ${ctx.schema.nodesTable} n WHERE n.graph_id = ${ctx.graphId} AND n.id IN (SELECT id FROM included_ids)`;
11320
+ return ctx.backend.execute(query);
11321
+ }
11322
+ async function fetchSubgraphEdges(ctx, reachableCte, includedIdsCte) {
11323
+ const edgeKindFilter = compileKindFilter(drizzleOrm.sql.raw("e.kind"), ctx.edgeKinds);
11324
+ const query = drizzleOrm.sql`${reachableCte}${includedIdsCte} SELECT e.id, e.kind, e.from_kind, e.from_id, e.to_kind, e.to_id, e.props, e.valid_from, e.valid_to, e.created_at, e.updated_at, e.deleted_at FROM ${ctx.schema.edgesTable} e WHERE e.graph_id = ${ctx.graphId} AND ${edgeKindFilter} AND e.deleted_at IS NULL AND e.from_id IN (SELECT id FROM included_ids) AND e.to_id IN (SELECT id FROM included_ids)`;
11325
+ return ctx.backend.execute(query);
11326
+ }
11191
11327
 
11192
11328
  // src/store/store.ts
11193
11329
  var Store = class {
@@ -11391,6 +11527,39 @@ var Store = class {
11391
11527
  query() {
11392
11528
  return this.#createQueryForBackend(this.#backend);
11393
11529
  }
11530
+ // === Subgraph Extraction ===
11531
+ /**
11532
+ * Extracts a typed subgraph by traversing from a root node.
11533
+ *
11534
+ * Performs a BFS traversal from `rootId` following the specified edge kinds,
11535
+ * then returns all reachable nodes and the edges connecting them.
11536
+ *
11537
+ * @example
11538
+ * ```typescript
11539
+ * const result = await store.subgraph(run.id, {
11540
+ * edges: ["has_task", "runs_agent", "uses_skill"],
11541
+ * maxDepth: 4,
11542
+ * includeKinds: ["Run", "Task", "Agent", "Skill"],
11543
+ * });
11544
+ *
11545
+ * for (const node of result.nodes) {
11546
+ * switch (node.kind) {
11547
+ * case "Task": console.log(node.name); break;
11548
+ * case "Agent": console.log(node.model); break;
11549
+ * }
11550
+ * }
11551
+ * ```
11552
+ */
11553
+ async subgraph(rootId, options) {
11554
+ return executeSubgraph({
11555
+ graphId: this.graphId,
11556
+ rootId,
11557
+ backend: this.#backend,
11558
+ dialect: chunk2WVFEIHR_cjs.getDialect(this.#backend.dialect),
11559
+ schema: this.#schema,
11560
+ options
11561
+ });
11562
+ }
11394
11563
  // === Transactions ===
11395
11564
  /**
11396
11565
  * Executes a function within a transaction.