@nicia-ai/typegraph 0.13.0 → 0.14.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.
Files changed (67) hide show
  1. package/dist/backend/postgres/index.cjs +28 -28
  2. package/dist/backend/postgres/index.d.cts +1 -1
  3. package/dist/backend/postgres/index.d.ts +1 -1
  4. package/dist/backend/postgres/index.js +4 -4
  5. package/dist/backend/sqlite/index.cjs +14 -14
  6. package/dist/backend/sqlite/index.d.cts +1 -1
  7. package/dist/backend/sqlite/index.d.ts +1 -1
  8. package/dist/backend/sqlite/index.js +4 -4
  9. package/dist/backend/sqlite/local.cjs +7 -7
  10. package/dist/backend/sqlite/local.d.cts +1 -1
  11. package/dist/backend/sqlite/local.d.ts +1 -1
  12. package/dist/backend/sqlite/local.js +4 -4
  13. package/dist/{chunk-U3452TEU.js → chunk-6GWJH6AR.js} +15 -27
  14. package/dist/chunk-6GWJH6AR.js.map +1 -0
  15. package/dist/{chunk-52WSY6G5.js → chunk-BNIBR5U2.js} +70 -31
  16. package/dist/chunk-BNIBR5U2.js.map +1 -0
  17. package/dist/{chunk-54WJF3DW.js → chunk-GNIYZKBI.js} +29 -3
  18. package/dist/chunk-GNIYZKBI.js.map +1 -0
  19. package/dist/{chunk-2XPKLHHH.cjs → chunk-KE2BL3JZ.cjs} +74 -35
  20. package/dist/chunk-KE2BL3JZ.cjs.map +1 -0
  21. package/dist/{chunk-PYV4ADC6.js → chunk-KLOSTZDQ.js} +141 -13
  22. package/dist/chunk-KLOSTZDQ.js.map +1 -0
  23. package/dist/{chunk-JQDWEX6V.cjs → chunk-LELLOHJK.cjs} +22 -34
  24. package/dist/chunk-LELLOHJK.cjs.map +1 -0
  25. package/dist/{chunk-2WVFEIHR.cjs → chunk-MME3H4ZF.cjs} +40 -2
  26. package/dist/chunk-MME3H4ZF.cjs.map +1 -0
  27. package/dist/{chunk-NZMKJHE2.cjs → chunk-OEKH5PWL.cjs} +15 -15
  28. package/dist/{chunk-NZMKJHE2.cjs.map → chunk-OEKH5PWL.cjs.map} +1 -1
  29. package/dist/{chunk-7VITUTRA.cjs → chunk-QFZ5QB2J.cjs} +140 -12
  30. package/dist/chunk-QFZ5QB2J.cjs.map +1 -0
  31. package/dist/{chunk-J4SICP3X.js → chunk-RVUEBUBH.js} +3 -3
  32. package/dist/{chunk-J4SICP3X.js.map → chunk-RVUEBUBH.js.map} +1 -1
  33. package/dist/index.cjs +432 -578
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +12 -9
  36. package/dist/index.d.ts +12 -9
  37. package/dist/index.js +393 -539
  38. package/dist/index.js.map +1 -1
  39. package/dist/indexes/index.cjs +16 -16
  40. package/dist/indexes/index.js +2 -2
  41. package/dist/interchange/index.d.cts +3 -3
  42. package/dist/interchange/index.d.ts +3 -3
  43. package/dist/{manager-DGSnJa1v.d.cts → manager-oh2mTMvy.d.cts} +1 -1
  44. package/dist/{manager-BCLhWysp.d.ts → manager-qRSdnKEO.d.ts} +1 -1
  45. package/dist/profiler/index.cjs +6 -6
  46. package/dist/profiler/index.cjs.map +1 -1
  47. package/dist/profiler/index.d.cts +3 -3
  48. package/dist/profiler/index.d.ts +3 -3
  49. package/dist/profiler/index.js +6 -6
  50. package/dist/profiler/index.js.map +1 -1
  51. package/dist/schema/index.cjs +20 -20
  52. package/dist/schema/index.d.cts +4 -4
  53. package/dist/schema/index.d.ts +4 -4
  54. package/dist/schema/index.js +2 -2
  55. package/dist/{store-6-vH0ZIj.d.ts → store-B9ItxA-Q.d.ts} +37 -17
  56. package/dist/{store-Bmdt_dS6.d.cts → store-BJPIoe8u.d.cts} +37 -17
  57. package/dist/{types-1YJKodRv.d.ts → types-5t_MIcvv.d.ts} +12 -6
  58. package/dist/{types-GLkwvQvS.d.cts → types-Ckfwgv9l.d.cts} +12 -6
  59. package/package.json +1 -1
  60. package/dist/chunk-2WVFEIHR.cjs.map +0 -1
  61. package/dist/chunk-2XPKLHHH.cjs.map +0 -1
  62. package/dist/chunk-52WSY6G5.js.map +0 -1
  63. package/dist/chunk-54WJF3DW.js.map +0 -1
  64. package/dist/chunk-7VITUTRA.cjs.map +0 -1
  65. package/dist/chunk-JQDWEX6V.cjs.map +0 -1
  66. package/dist/chunk-PYV4ADC6.js.map +0 -1
  67. package/dist/chunk-U3452TEU.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,18 +1,68 @@
1
- import { META_EDGE_BRAND, META_EDGE_SUB_CLASS_OF, META_EDGE_NARROWER, META_EDGE_BROADER, META_EDGE_RELATED_TO, META_EDGE_EQUIVALENT_TO, META_EDGE_SAME_AS, META_EDGE_DIFFERENT_FROM, META_EDGE_DISJOINT_WITH, META_EDGE_HAS_PART, META_EDGE_PART_OF, META_EDGE_INVERSE_OF, META_EDGE_IMPLIES, ensureSchema, computeClosuresFromOntology, createEmptyClosures, KindRegistry, validateNodeProps, validateEdgeProps } from './chunk-PYV4ADC6.js';
2
- export { isMetaEdge } from './chunk-PYV4ADC6.js';
1
+ import { META_EDGE_BRAND, META_EDGE_SUB_CLASS_OF, META_EDGE_NARROWER, META_EDGE_BROADER, META_EDGE_RELATED_TO, META_EDGE_EQUIVALENT_TO, META_EDGE_SAME_AS, META_EDGE_DIFFERENT_FROM, META_EDGE_DISJOINT_WITH, META_EDGE_HAS_PART, META_EDGE_PART_OF, META_EDGE_INVERSE_OF, META_EDGE_IMPLIES, ensureSchema, computeClosuresFromOntology, createEmptyClosures, KindRegistry, validateNodeProps, validateEdgeProps } from './chunk-KLOSTZDQ.js';
2
+ export { isMetaEdge } from './chunk-KLOSTZDQ.js';
3
3
  import { NODE_TYPE_BRAND, EDGE_TYPE_BRAND, nowIso, validateOptionalIsoDate, getNodeKinds, getEdgeKinds } from './chunk-IRFD3MEU.js';
4
4
  export { defineGraph, getEdgeKinds, getNodeKinds, isEdgeType, isEdgeTypeWithEndpoints, isGraphDef, isNodeType } from './chunk-IRFD3MEU.js';
5
- import { ConfigurationError, UnsupportedPredicateError, ValidationError, CompilerInvariantError, KindNotFoundError, RestrictedDeleteError, NodeNotFoundError, DatabaseOperationError, EdgeNotFoundError, NodeConstraintNotFoundError, UniquenessError, EndpointNotFoundError, CardinalityError, EndpointError, DisjointError } from './chunk-SJ2QMDXY.js';
5
+ import { ConfigurationError, UnsupportedPredicateError, ValidationError, CompilerInvariantError, KindNotFoundError, DatabaseOperationError, EdgeNotFoundError, NodeConstraintNotFoundError, RestrictedDeleteError, NodeNotFoundError, EndpointNotFoundError, UniquenessError, CardinalityError, EndpointError, DisjointError } from './chunk-SJ2QMDXY.js';
6
6
  export { CardinalityError, CompilerInvariantError, ConfigurationError, DatabaseOperationError, DisjointError, EdgeNotFoundError, EndpointError, EndpointNotFoundError, KindNotFoundError, MigrationError, NodeConstraintNotFoundError, NodeNotFoundError, RestrictedDeleteError, SchemaMismatchError, TypeGraphError, UniquenessError, UnsupportedPredicateError, ValidationError, VersionConflictError, getErrorSuggestion, isConstraintError, isSystemError, isTypeGraphError, isUserRecoverable } from './chunk-SJ2QMDXY.js';
7
- import { getDialect, NODE_META_KEYS, EDGE_META_KEYS } from './chunk-54WJF3DW.js';
7
+ import { MAX_PG_IDENTIFIER_LENGTH, DEFAULT_PAGINATION_LIMIT, getDialect, DEFAULT_STREAM_BATCH_SIZE, NODE_META_KEYS, EDGE_META_KEYS, SQLITE_PATH_DELIMITER, PG_ARRAY_START, PG_ARRAY_END, PG_PATH_ELEMENT_SEPARATOR, fnv1aBase36 } from './chunk-GNIYZKBI.js';
8
8
  import { jsonPointer, parseJsonPointer, createSchemaIntrospector, normalizeJsonPointer, joinJsonPointers, resolveFieldTypeInfoAtJsonPointer, getEmbeddingDimensions, isEmbeddingSchema } from './chunk-K7SQ3SWP.js';
9
9
  export { MAX_JSON_POINTER_DEPTH, embedding, getEmbeddingDimensions, isEmbeddingSchema, joinJsonPointers, jsonPointer, normalizeJsonPointer, parseJsonPointer } from './chunk-K7SQ3SWP.js';
10
10
  import { z } from 'zod';
11
11
  import { sql, Placeholder } from 'drizzle-orm';
12
12
  import { nanoid } from 'nanoid';
13
13
 
14
+ // src/store/reserved-keys.ts
15
+ var RESERVED_NODE_KEYS = /* @__PURE__ */ new Set([
16
+ "id",
17
+ "kind",
18
+ "meta"
19
+ ]);
20
+ var RESERVED_EDGE_KEYS = /* @__PURE__ */ new Set([
21
+ "id",
22
+ "kind",
23
+ "meta",
24
+ "fromKind",
25
+ "fromId",
26
+ "toKind",
27
+ "toId"
28
+ ]);
29
+ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
30
+ "__proto__",
31
+ "constructor",
32
+ "prototype"
33
+ ]);
34
+ function validateProjectionField(field2, entityType, kind) {
35
+ const reserved = entityType === "node" ? RESERVED_NODE_KEYS : RESERVED_EDGE_KEYS;
36
+ if (reserved.has(field2)) {
37
+ throw new ConfigurationError(
38
+ `Projection field "${field2}" on ${entityType} kind "${kind}" conflicts with a reserved structural key`,
39
+ { field: field2, kind, entityType, reservedKeys: [...reserved] },
40
+ {
41
+ suggestion: `Remove "${field2}" from the projection. Structural fields (${[...reserved].join(", ")}) are included automatically when relevant.`
42
+ }
43
+ );
44
+ }
45
+ if (PROTOTYPE_POLLUTION_KEYS.has(field2)) {
46
+ throw new ConfigurationError(
47
+ `Projection field "${field2}" on ${entityType} kind "${kind}" is not allowed`,
48
+ { field: field2, kind, entityType },
49
+ {
50
+ suggestion: `"${field2}" cannot be used as a projection field name.`
51
+ }
52
+ );
53
+ }
54
+ }
55
+ function filterReservedKeys(props, reservedKeys) {
56
+ const filtered = {};
57
+ for (const [key, value] of Object.entries(props)) {
58
+ if (!reservedKeys.has(key)) {
59
+ filtered[key] = value;
60
+ }
61
+ }
62
+ return filtered;
63
+ }
64
+
14
65
  // src/core/node.ts
15
- var RESERVED_NODE_KEYS = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
16
66
  function validateSchemaKeys(schema, name) {
17
67
  const shape = schema.shape;
18
68
  const conflicts = Object.keys(shape).filter(
@@ -37,15 +87,6 @@ function defineNode(name, options) {
37
87
  description: options.description
38
88
  });
39
89
  }
40
- var RESERVED_EDGE_KEYS = /* @__PURE__ */ new Set([
41
- "id",
42
- "kind",
43
- "meta",
44
- "fromKind",
45
- "fromId",
46
- "toKind",
47
- "toId"
48
- ]);
49
90
  var EMPTY_SCHEMA = z.object({});
50
91
  function validateSchemaKeys2(schema, name) {
51
92
  const shape = schema.shape;
@@ -1376,14 +1417,14 @@ function extractVectorSimilarityPredicates(predicates) {
1376
1417
  break;
1377
1418
  }
1378
1419
  case "and": {
1379
- for (const p of expr.predicates) {
1380
- visit(p, inDisallowedBranch);
1420
+ for (const predicate2 of expr.predicates) {
1421
+ visit(predicate2, inDisallowedBranch);
1381
1422
  }
1382
1423
  break;
1383
1424
  }
1384
1425
  case "or": {
1385
- for (const p of expr.predicates) {
1386
- visit(p, true);
1426
+ for (const predicate2 of expr.predicates) {
1427
+ visit(predicate2, true);
1387
1428
  }
1388
1429
  break;
1389
1430
  }
@@ -2406,6 +2447,9 @@ function compileTemporalFilter(options) {
2406
2447
  return sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${now}) AND (${validTo} IS NULL OR ${validTo} > ${now})`;
2407
2448
  }
2408
2449
  case "asOf": {
2450
+ if (asOf === void 0) {
2451
+ throw new Error(`asOf timestamp is required for temporal mode "asOf"`);
2452
+ }
2409
2453
  const timestamp = asOf;
2410
2454
  return sql`${deletedAt} IS NULL AND (${validFrom} IS NULL OR ${validFrom} <= ${timestamp}) AND (${validTo} IS NULL OR ${validTo} > ${timestamp})`;
2411
2455
  }
@@ -2813,7 +2857,7 @@ function lowerSetOperationToLogicalPlan(input) {
2813
2857
  }
2814
2858
 
2815
2859
  // src/query/compiler/recursive.ts
2816
- var MAX_RECURSIVE_DEPTH = 100;
2860
+ var MAX_RECURSIVE_DEPTH = 10;
2817
2861
  var MAX_EXPLICIT_RECURSIVE_DEPTH = 1e3;
2818
2862
  var NO_ALWAYS_REQUIRED_COLUMNS = /* @__PURE__ */ new Set();
2819
2863
  function runRecursiveQueryPassPipeline(ast, graphId, ctx) {
@@ -3306,15 +3350,14 @@ var DEFAULT_TABLE_NAMES = {
3306
3350
  edges: "typegraph_edges",
3307
3351
  embeddings: "typegraph_node_embeddings"
3308
3352
  };
3309
- var MAX_IDENTIFIER_LENGTH = 63;
3310
3353
  var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
3311
3354
  function validateTableName(name, label) {
3312
3355
  if (!name || name.length === 0) {
3313
3356
  throw new ConfigurationError(`${label} table name cannot be empty`);
3314
3357
  }
3315
- if (name.length > MAX_IDENTIFIER_LENGTH) {
3358
+ if (name.length > MAX_PG_IDENTIFIER_LENGTH) {
3316
3359
  throw new ConfigurationError(
3317
- `${label} table name exceeds maximum length of ${MAX_IDENTIFIER_LENGTH} characters`
3360
+ `${label} table name exceeds maximum length of ${MAX_PG_IDENTIFIER_LENGTH} characters`
3318
3361
  );
3319
3362
  }
3320
3363
  if (!VALID_IDENTIFIER_PATTERN.test(name)) {
@@ -3397,64 +3440,13 @@ function decodeByValueType(value, valueType) {
3397
3440
  }
3398
3441
  }
3399
3442
 
3400
- // src/store/reserved-keys.ts
3401
- var RESERVED_NODE_KEYS2 = /* @__PURE__ */ new Set([
3402
- "id",
3403
- "kind",
3404
- "meta"
3405
- ]);
3406
- var RESERVED_EDGE_KEYS2 = /* @__PURE__ */ new Set([
3407
- "id",
3408
- "kind",
3409
- "meta",
3410
- "fromKind",
3411
- "fromId",
3412
- "toKind",
3413
- "toId"
3414
- ]);
3415
- var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
3416
- "__proto__",
3417
- "constructor",
3418
- "prototype"
3419
- ]);
3420
- function validateProjectionField(field2, entityType, kind) {
3421
- const reserved = entityType === "node" ? RESERVED_NODE_KEYS2 : RESERVED_EDGE_KEYS2;
3422
- if (reserved.has(field2)) {
3423
- throw new ConfigurationError(
3424
- `Projection field "${field2}" on ${entityType} kind "${kind}" conflicts with a reserved structural key`,
3425
- { field: field2, kind, entityType, reservedKeys: [...reserved] },
3426
- {
3427
- suggestion: `Remove "${field2}" from the projection. Structural fields (${[...reserved].join(", ")}) are included automatically when relevant.`
3428
- }
3429
- );
3430
- }
3431
- if (PROTOTYPE_POLLUTION_KEYS.has(field2)) {
3432
- throw new ConfigurationError(
3433
- `Projection field "${field2}" on ${entityType} kind "${kind}" is not allowed`,
3434
- { field: field2, kind, entityType },
3435
- {
3436
- suggestion: `"${field2}" cannot be used as a projection field name.`
3437
- }
3438
- );
3439
- }
3440
- }
3441
- function filterReservedKeys(props, reservedKeys) {
3442
- const filtered = {};
3443
- for (const [key, value] of Object.entries(props)) {
3444
- if (!reservedKeys.has(key)) {
3445
- filtered[key] = value;
3446
- }
3447
- }
3448
- return filtered;
3449
- }
3450
-
3451
3443
  // src/store/row-mappers.ts
3452
3444
  function nullToUndefined2(value) {
3453
3445
  return value === null ? void 0 : value;
3454
3446
  }
3455
3447
  function rowToNode(row) {
3456
3448
  const rawProps = JSON.parse(row.props);
3457
- const props = filterReservedKeys(rawProps, RESERVED_NODE_KEYS2);
3449
+ const props = filterReservedKeys(rawProps, RESERVED_NODE_KEYS);
3458
3450
  return {
3459
3451
  kind: row.kind,
3460
3452
  id: row.id,
@@ -3474,7 +3466,7 @@ function rowToNodeMeta(row) {
3474
3466
  }
3475
3467
  function rowToEdge(row) {
3476
3468
  const rawProps = JSON.parse(row.props);
3477
- const props = filterReservedKeys(rawProps, RESERVED_EDGE_KEYS2);
3469
+ const props = filterReservedKeys(rawProps, RESERVED_EDGE_KEYS);
3478
3470
  return {
3479
3471
  id: row.id,
3480
3472
  kind: row.kind,
@@ -3498,17 +3490,6 @@ function rowToEdgeMeta(row) {
3498
3490
 
3499
3491
  // src/store/subgraph.ts
3500
3492
  var DEFAULT_SUBGRAPH_MAX_DEPTH = 10;
3501
- var MAX_PG_IDENTIFIER_LENGTH = 63;
3502
- function fnv1aBase36(input) {
3503
- let hash = 2166136261;
3504
- for (const character of input) {
3505
- const codePoint = character.codePointAt(0);
3506
- if (codePoint === void 0) continue;
3507
- hash ^= codePoint;
3508
- hash = Math.imul(hash, 16777619);
3509
- }
3510
- return (hash >>> 0).toString(36);
3511
- }
3512
3493
  var TEXT_ENCODER = new TextEncoder();
3513
3494
  function truncateToBytes(value, maxBytes) {
3514
3495
  const encoded = TEXT_ENCODER.encode(value);
@@ -3540,7 +3521,7 @@ async function executeSubgraph(params) {
3540
3521
  }
3541
3522
  const maxDepth = Math.min(
3542
3523
  options.maxDepth ?? DEFAULT_SUBGRAPH_MAX_DEPTH,
3543
- MAX_RECURSIVE_DEPTH
3524
+ MAX_EXPLICIT_RECURSIVE_DEPTH
3544
3525
  );
3545
3526
  const ctx = {
3546
3527
  graphId: params.graphId,
@@ -4224,6 +4205,7 @@ function compileFieldColumnForSetOp(field2, prefix, dialect) {
4224
4205
  updated_at: "_updated_at",
4225
4206
  deleted_at: "_deleted_at"
4226
4207
  };
4208
+ if (columnName === void 0) return sql.raw(`${cteName}.${alias}_props`);
4227
4209
  const suffix = columnMap[columnName];
4228
4210
  if (suffix) {
4229
4211
  return sql.raw(`${cteName}.${alias}${suffix}`);
@@ -4302,18 +4284,18 @@ function buildSetOperationSuffixClauses(op, dialect) {
4302
4284
  );
4303
4285
  }
4304
4286
  const orderParts = [];
4305
- for (const o of op.orderBy) {
4306
- const projected = matchFieldToProjection(o.field, projection);
4287
+ for (const orderSpec of op.orderBy) {
4288
+ const projected = matchFieldToProjection(orderSpec.field, projection);
4307
4289
  if (!projected) {
4308
- const fieldDesc = o.field.jsonPointer ? `${o.field.alias}.props${o.field.jsonPointer}` : `${o.field.alias}.${o.field.path.join(".")}`;
4290
+ const fieldDesc = orderSpec.field.jsonPointer ? `${orderSpec.field.alias}.props${orderSpec.field.jsonPointer}` : `${orderSpec.field.alias}.${orderSpec.field.path.join(".")}`;
4309
4291
  const availableFields = projection.fields.map((f) => f.outputName).join(", ");
4310
4292
  throw new UnsupportedPredicateError(
4311
4293
  `Set operation ORDER BY field "${fieldDesc}" is not in the projection. ORDER BY for UNION/INTERSECT/EXCEPT must reference projected columns. Available columns: ${availableFields}`
4312
4294
  );
4313
4295
  }
4314
4296
  const columnRef = sql.raw(dialect.quoteIdentifier(projected.outputName));
4315
- const dir = sql.raw(o.direction.toUpperCase());
4316
- const nulls = o.nulls ?? (o.direction === "asc" ? "last" : "first");
4297
+ const dir = sql.raw(orderSpec.direction.toUpperCase());
4298
+ const nulls = orderSpec.nulls ?? (orderSpec.direction === "asc" ? "last" : "first");
4317
4299
  const nullsDir = sql.raw(nulls === "first" ? "DESC" : "ASC");
4318
4300
  orderParts.push(
4319
4301
  sql`(${columnRef} IS NULL) ${nullsDir}`,
@@ -5180,13 +5162,14 @@ function generateId() {
5180
5162
 
5181
5163
  // src/utils/path.ts
5182
5164
  function parseSqlitePath(path) {
5183
- if (!path || path === "||") return [];
5165
+ const emptyPath = `${SQLITE_PATH_DELIMITER}${SQLITE_PATH_DELIMITER}`;
5166
+ if (!path || path === emptyPath) return [];
5184
5167
  const trimmed = path.slice(1, -1);
5185
5168
  if (trimmed === "") return [];
5186
- return trimmed.split("|");
5169
+ return trimmed.split(SQLITE_PATH_DELIMITER);
5187
5170
  }
5188
5171
  function isSqlitePath(value) {
5189
- return typeof value === "string" && value.startsWith("|") && value.endsWith("|");
5172
+ return typeof value === "string" && value.startsWith(SQLITE_PATH_DELIMITER) && value.endsWith(SQLITE_PATH_DELIMITER);
5190
5173
  }
5191
5174
  function normalizePath(value) {
5192
5175
  if (Array.isArray(value)) {
@@ -5201,20 +5184,20 @@ function normalizePath(value) {
5201
5184
  return [];
5202
5185
  }
5203
5186
  function isPostgresTextArray(value) {
5204
- return typeof value === "string" && value.startsWith("{") && value.endsWith("}");
5187
+ return typeof value === "string" && value.startsWith(PG_ARRAY_START) && value.endsWith(PG_ARRAY_END);
5205
5188
  }
5206
5189
  function parsePostgresTextArray(value) {
5207
5190
  const inner = value.slice(1, -1);
5208
5191
  if (inner === "") return [];
5209
- return inner.split(",");
5192
+ return inner.split(PG_PATH_ELEMENT_SEPARATOR);
5210
5193
  }
5211
5194
 
5212
5195
  // src/query/execution/result-mapper.ts
5213
5196
  function transformPathColumns(rows, state, _dialect) {
5214
5197
  const pathAliases = [];
5215
- for (const t of state.traversals) {
5216
- if (t.variableLength?.pathAlias !== void 0) {
5217
- pathAliases.push(t.variableLength.pathAlias);
5198
+ for (const traversal of state.traversals) {
5199
+ if (traversal.variableLength?.pathAlias !== void 0) {
5200
+ pathAliases.push(traversal.variableLength.pathAlias);
5218
5201
  }
5219
5202
  }
5220
5203
  if (pathAliases.length === 0) return rows;
@@ -5238,8 +5221,8 @@ function transformPathColumns(rows, state, _dialect) {
5238
5221
  }
5239
5222
  return changed ? result : rows;
5240
5223
  }
5241
- var RESERVED_NODE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
5242
- var RESERVED_EDGE_KEYS3 = /* @__PURE__ */ new Set(["id", "kind", "fromId", "toId", "meta"]);
5224
+ var RESERVED_NODE_KEYS2 = /* @__PURE__ */ new Set(["id", "kind", "meta"]);
5225
+ var RESERVED_EDGE_KEYS2 = /* @__PURE__ */ new Set(["id", "kind", "fromId", "toId", "meta"]);
5243
5226
  function nullToUndefined3(value) {
5244
5227
  return value === null ? void 0 : value;
5245
5228
  }
@@ -5277,7 +5260,7 @@ function buildSelectableNode(row, alias) {
5277
5260
  deletedAt
5278
5261
  }
5279
5262
  };
5280
- assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS3);
5263
+ assignPropsExcludingReserved(result, rawProps, RESERVED_NODE_KEYS2);
5281
5264
  return result;
5282
5265
  }
5283
5266
  function buildSelectableNodeOrUndefined(row, alias) {
@@ -5319,7 +5302,7 @@ function buildSelectableEdge(row, alias) {
5319
5302
  deletedAt
5320
5303
  }
5321
5304
  };
5322
- assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS3);
5305
+ assignPropsExcludingReserved(result, rawProps, RESERVED_EDGE_KEYS2);
5323
5306
  return result;
5324
5307
  }
5325
5308
  function buildSelectContext(row, startAlias, traversals) {
@@ -5447,7 +5430,7 @@ async function* createStreamIterable(batchSize, paginate) {
5447
5430
  }
5448
5431
  }
5449
5432
  function getStreamBatchSize(options) {
5450
- return options?.batchSize ?? 1e3;
5433
+ return options?.batchSize ?? DEFAULT_STREAM_BATCH_SIZE;
5451
5434
  }
5452
5435
 
5453
5436
  // src/query/ast.ts
@@ -5859,6 +5842,9 @@ function buildSelectiveContext(row, plans, traversals) {
5859
5842
  return context;
5860
5843
  }
5861
5844
  function buildOptionalAliasValue(row, plan) {
5845
+ if (plan.idOutputName === void 0) {
5846
+ return void 0;
5847
+ }
5862
5848
  const idValue = row[plan.idOutputName];
5863
5849
  if (idValue === null || idValue === void 0) {
5864
5850
  return void 0;
@@ -7335,7 +7321,7 @@ var ExecutableQuery = class _ExecutableQuery {
7335
7321
  );
7336
7322
  }
7337
7323
  const isBackward = options.last !== void 0 || options.before !== void 0;
7338
- const limit = options.first ?? options.last ?? 20;
7324
+ const limit = options.first ?? options.last ?? DEFAULT_PAGINATION_LIMIT;
7339
7325
  const cursor = options.after ?? options.before;
7340
7326
  let cursorData;
7341
7327
  if (cursor) {
@@ -7658,7 +7644,8 @@ var TraversalBuilder = class _TraversalBuilder {
7658
7644
  }
7659
7645
  /**
7660
7646
  * Enables variable-length (recursive) traversal.
7661
- * By default, traverses unlimited depth with cycle prevention.
7647
+ * Defaults to MAX_RECURSIVE_DEPTH (10) hops with cycle prevention.
7648
+ * Use `maxHops` to override (up to MAX_EXPLICIT_RECURSIVE_DEPTH).
7662
7649
  */
7663
7650
  recursive(options) {
7664
7651
  const minDepth = options?.minHops ?? this.#variableLength.minDepth;
@@ -10432,6 +10419,9 @@ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, to
10432
10419
  return { edge: edge2, action: "updated" };
10433
10420
  }
10434
10421
  const cardinality = registration.cardinality ?? "many";
10422
+ if (deletedRow === void 0) {
10423
+ throw new Error("Expected deletedRow to be defined");
10424
+ }
10435
10425
  const matchedDeletedRow = deletedRow;
10436
10426
  const effectiveValidTo = matchedDeletedRow.valid_to;
10437
10427
  const constraintContext = {
@@ -10551,6 +10541,9 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
10551
10541
  });
10552
10542
  } else {
10553
10543
  const bestRow = liveRow ?? deletedRow;
10544
+ if (bestRow === void 0) {
10545
+ throw new Error("Expected at least one of liveRow or deletedRow");
10546
+ }
10554
10547
  toFetch.push({
10555
10548
  index,
10556
10549
  row: bestRow,
@@ -10851,11 +10844,12 @@ function getNodeRegistration(graph, kind) {
10851
10844
  if (registration === void 0) throw new KindNotFoundError(kind, "node");
10852
10845
  return registration;
10853
10846
  }
10847
+ var CACHE_KEY_SEPARATOR = "\0";
10854
10848
  function buildNodeCacheKey(graphId, kind, id) {
10855
- return `${graphId}\0${kind}\0${id}`;
10849
+ return `${graphId}${CACHE_KEY_SEPARATOR}${kind}${CACHE_KEY_SEPARATOR}${id}`;
10856
10850
  }
10857
10851
  function buildUniqueCacheKey(graphId, nodeKind, constraintName, key) {
10858
- return `${graphId}\0${nodeKind}\0${constraintName}\0${key}`;
10852
+ return `${graphId}${CACHE_KEY_SEPARATOR}${nodeKind}${CACHE_KEY_SEPARATOR}${constraintName}${CACHE_KEY_SEPARATOR}${key}`;
10859
10853
  }
10860
10854
  function createNodeAlreadyExistsError(kind, id) {
10861
10855
  return new ValidationError(
@@ -10892,6 +10886,23 @@ function createPendingUniqueRow(graphId, nodeKind, constraintName, key, nodeId)
10892
10886
  deleted_at: void 0
10893
10887
  };
10894
10888
  }
10889
+ function resolveConstraint(graph, kind, constraintName) {
10890
+ const registration = getNodeRegistration(graph, kind);
10891
+ const constraints = registration.unique ?? [];
10892
+ const constraint = constraints.find(
10893
+ (candidate) => candidate.name === constraintName
10894
+ );
10895
+ if (constraint === void 0) {
10896
+ throw new NodeConstraintNotFoundError(constraintName, kind);
10897
+ }
10898
+ return constraint;
10899
+ }
10900
+ function createUniquenessContext(graphId, registry, backend) {
10901
+ return { graphId, registry, backend };
10902
+ }
10903
+ function createEmbeddingSyncContext(graphId, nodeKind, nodeId, backend) {
10904
+ return { graphId, nodeKind, nodeId, backend };
10905
+ }
10895
10906
  function createNodeBatchValidationBackend(graphId, registry, backend) {
10896
10907
  const nodeCache = /* @__PURE__ */ new Map();
10897
10908
  const pendingNodes = /* @__PURE__ */ new Map();
@@ -10900,12 +10911,8 @@ function createNodeBatchValidationBackend(graphId, registry, backend) {
10900
10911
  async function getNodeCached(lookupGraphId, kind, id) {
10901
10912
  const cacheKey = buildNodeCacheKey(lookupGraphId, kind, id);
10902
10913
  const pendingNode = pendingNodes.get(cacheKey);
10903
- if (pendingNode !== void 0) {
10904
- return pendingNode;
10905
- }
10906
- if (nodeCache.has(cacheKey)) {
10907
- return nodeCache.get(cacheKey);
10908
- }
10914
+ if (pendingNode !== void 0) return pendingNode;
10915
+ if (nodeCache.has(cacheKey)) return nodeCache.get(cacheKey);
10909
10916
  const existing = await backend.getNode(lookupGraphId, kind, id);
10910
10917
  nodeCache.set(cacheKey, existing);
10911
10918
  return existing;
@@ -10927,16 +10934,14 @@ function createNodeBatchValidationBackend(graphId, registry, backend) {
10927
10934
  pendingOwner
10928
10935
  );
10929
10936
  }
10930
- if (uniqueCache.has(cacheKey)) {
10931
- return uniqueCache.get(cacheKey);
10932
- }
10937
+ if (uniqueCache.has(cacheKey)) return uniqueCache.get(cacheKey);
10933
10938
  const existing = await backend.checkUnique(params);
10934
10939
  uniqueCache.set(cacheKey, existing);
10935
10940
  return existing;
10936
10941
  }
10937
10942
  function registerPendingNode(params) {
10938
10943
  const cacheKey = buildNodeCacheKey(params.graphId, params.kind, params.id);
10939
- const pendingNode = {
10944
+ pendingNodes.set(cacheKey, {
10940
10945
  graph_id: params.graphId,
10941
10946
  kind: params.kind,
10942
10947
  id: params.id,
@@ -10947,14 +10952,11 @@ function createNodeBatchValidationBackend(graphId, registry, backend) {
10947
10952
  created_at: "",
10948
10953
  updated_at: "",
10949
10954
  deleted_at: void 0
10950
- };
10951
- pendingNodes.set(cacheKey, pendingNode);
10955
+ });
10952
10956
  }
10953
10957
  function registerPendingUniqueEntries(kind, id, props, constraints) {
10954
10958
  for (const constraint of constraints) {
10955
- if (!checkWherePredicate(constraint, props)) {
10956
- continue;
10957
- }
10959
+ if (!checkWherePredicate(constraint, props)) continue;
10958
10960
  const key = computeUniqueKey(
10959
10961
  props,
10960
10962
  constraint.fields,
@@ -11016,14 +11018,9 @@ async function validateAndPrepareNodeCreate(ctx, input, id, backend) {
11016
11018
  backend
11017
11019
  };
11018
11020
  await checkDisjointnessConstraint(constraintContext, kind, id);
11019
- const uniquenessContext = {
11020
- graphId: ctx.graphId,
11021
- registry: ctx.registry,
11022
- backend
11023
- };
11024
11021
  const uniqueConstraints = registration.unique ?? [];
11025
11022
  await checkUniquenessConstraints(
11026
- uniquenessContext,
11023
+ createUniquenessContext(ctx.graphId, ctx.registry, backend),
11027
11024
  kind,
11028
11025
  id,
11029
11026
  validatedProps,
@@ -11046,64 +11043,99 @@ async function validateAndPrepareNodeCreate(ctx, input, id, backend) {
11046
11043
  };
11047
11044
  }
11048
11045
  async function finalizeNodeCreate(ctx, prepared, backend) {
11049
- const uniquenessContext = {
11050
- graphId: ctx.graphId,
11051
- registry: ctx.registry,
11052
- backend
11053
- };
11054
11046
  await insertUniquenessEntries(
11055
- uniquenessContext,
11047
+ createUniquenessContext(ctx.graphId, ctx.registry, backend),
11056
11048
  prepared.kind,
11057
11049
  prepared.id,
11058
11050
  prepared.validatedProps,
11059
11051
  prepared.uniqueConstraints
11060
11052
  );
11061
- const embeddingSyncContext = {
11062
- graphId: ctx.graphId,
11063
- nodeKind: prepared.kind,
11064
- nodeId: prepared.id,
11065
- backend
11066
- };
11067
11053
  await syncEmbeddings(
11068
- embeddingSyncContext,
11054
+ createEmbeddingSyncContext(
11055
+ ctx.graphId,
11056
+ prepared.kind,
11057
+ prepared.id,
11058
+ backend
11059
+ ),
11069
11060
  prepared.nodeKind.schema,
11070
11061
  prepared.validatedProps
11071
11062
  );
11072
11063
  }
11073
- async function executeNodeCreateInternal(ctx, input, backend, options) {
11074
- const kind = input.kind;
11075
- const id = input.id ?? generateId();
11076
- const opContext = ctx.createOperationContext("create", "node", kind, id);
11077
- return ctx.withOperationHooks(opContext, async () => {
11078
- const prepared = await validateAndPrepareNodeCreate(
11079
- ctx,
11080
- input,
11081
- id,
11082
- backend
11083
- );
11084
- let row;
11085
- {
11086
- row = await backend.insertNode(prepared.insertParams);
11087
- }
11088
- await finalizeNodeCreate(ctx, prepared, backend);
11089
- if (row === void 0) return;
11090
- return rowToNode(row);
11064
+ async function performNodeUpdate(ctx, input, backend, options) {
11065
+ const { kind, id } = input;
11066
+ const registration = getNodeRegistration(ctx.graph, kind);
11067
+ const existing = await backend.getNode(ctx.graphId, kind, id);
11068
+ if (!existing || existing.deleted_at && !options?.clearDeleted) {
11069
+ throw new NodeNotFoundError(kind, id);
11070
+ }
11071
+ const existingProps = JSON.parse(existing.props);
11072
+ const mergedProps = { ...existingProps, ...input.props };
11073
+ const nodeKind = registration.type;
11074
+ const validatedProps = validateNodeProps(nodeKind.schema, mergedProps, {
11075
+ kind,
11076
+ operation: "update",
11077
+ id
11091
11078
  });
11079
+ const validTo = validateOptionalIsoDate(input.validTo, "validTo");
11080
+ await updateUniquenessEntries(
11081
+ createUniquenessContext(ctx.graphId, ctx.registry, backend),
11082
+ kind,
11083
+ id,
11084
+ existingProps,
11085
+ validatedProps,
11086
+ registration.unique ?? []
11087
+ );
11088
+ const updateParams = {
11089
+ graphId: ctx.graphId,
11090
+ kind,
11091
+ id,
11092
+ props: validatedProps,
11093
+ incrementVersion: true
11094
+ };
11095
+ if (validTo !== void 0) updateParams.validTo = validTo;
11096
+ if (options?.clearDeleted) updateParams.clearDeleted = true;
11097
+ const row = await backend.updateNode(updateParams);
11098
+ await syncEmbeddings(
11099
+ createEmbeddingSyncContext(ctx.graphId, kind, id, backend),
11100
+ nodeKind.schema,
11101
+ validatedProps
11102
+ );
11103
+ return rowToNode(row);
11092
11104
  }
11093
- async function executeNodeCreate(ctx, input, backend) {
11094
- const result = await executeNodeCreateInternal(ctx, input, backend);
11095
- if (!result) {
11096
- throw new DatabaseOperationError(
11097
- "Node create failed: expected created node row",
11098
- { operation: "insert", entity: "node" }
11099
- );
11105
+ async function enforceDeleteBehavior(ctx, kind, id, mode, backend, registration) {
11106
+ const deleteBehavior = registration.onDelete ?? "restrict";
11107
+ const connectedEdges = await backend.findEdgesConnectedTo({
11108
+ graphId: ctx.graphId,
11109
+ nodeKind: kind,
11110
+ nodeId: id
11111
+ });
11112
+ if (connectedEdges.length === 0) return;
11113
+ switch (deleteBehavior) {
11114
+ case "restrict": {
11115
+ const edgeKinds = [...new Set(connectedEdges.map((edge) => edge.kind))];
11116
+ throw new RestrictedDeleteError({
11117
+ nodeKind: kind,
11118
+ nodeId: id,
11119
+ edgeCount: connectedEdges.length,
11120
+ edgeKinds
11121
+ });
11122
+ }
11123
+ case "cascade":
11124
+ case "disconnect": {
11125
+ for (const edge of connectedEdges) {
11126
+ await (mode === "hard" ? backend.hardDeleteEdge({
11127
+ graphId: ctx.graphId,
11128
+ id: edge.id
11129
+ }) : backend.deleteEdge({
11130
+ graphId: ctx.graphId,
11131
+ id: edge.id
11132
+ }));
11133
+ }
11134
+ break;
11135
+ }
11100
11136
  }
11101
- return result;
11102
11137
  }
11103
- async function executeNodeCreateNoReturnBatch(ctx, inputs, backend) {
11104
- if (inputs.length === 0) {
11105
- return;
11106
- }
11138
+ async function prepareBatchCreates(ctx, inputs, backend) {
11107
11139
  const {
11108
11140
  backend: validationBackend,
11109
11141
  registerPendingNode,
@@ -11130,46 +11162,109 @@ async function executeNodeCreateNoReturnBatch(ctx, inputs, backend) {
11130
11162
  const batchInsertParams = preparedCreates.map(
11131
11163
  (prepared) => prepared.insertParams
11132
11164
  );
11133
- if (backend.insertNodesBatch === void 0) {
11134
- for (const insertParams of batchInsertParams) {
11135
- await (backend.insertNodeNoReturn?.(insertParams) ?? backend.insertNode(insertParams));
11136
- }
11137
- } else {
11138
- await backend.insertNodesBatch(batchInsertParams);
11139
- }
11140
- for (const prepared of preparedCreates) {
11141
- await finalizeNodeCreate(ctx, prepared, backend);
11142
- }
11165
+ return { preparedCreates, batchInsertParams };
11143
11166
  }
11144
- async function executeNodeCreateBatch(ctx, inputs, backend) {
11145
- if (inputs.length === 0) {
11146
- return [];
11167
+ async function findUniqueRowAcrossKinds(backend, graphId, constraintName, key, kindsToCheck, includeDeleted) {
11168
+ for (const kindToCheck of kindsToCheck) {
11169
+ const row = await backend.checkUnique({
11170
+ graphId,
11171
+ nodeKind: kindToCheck,
11172
+ constraintName,
11173
+ key,
11174
+ includeDeleted
11175
+ });
11176
+ if (row !== void 0) return row;
11147
11177
  }
11148
- const {
11149
- backend: validationBackend,
11150
- registerPendingNode,
11151
- registerPendingUniqueEntries
11152
- } = createNodeBatchValidationBackend(ctx.graphId, ctx.registry, backend);
11153
- const preparedCreates = [];
11154
- for (const input of inputs) {
11155
- const id = input.id ?? generateId();
11156
- const prepared = await validateAndPrepareNodeCreate(
11157
- ctx,
11178
+ return void 0;
11179
+ }
11180
+ async function batchCheckUniqueAcrossKinds(backend, graphId, constraintName, uniqueKeys, kindsToCheck, includeDeleted) {
11181
+ const existingByKey = /* @__PURE__ */ new Map();
11182
+ for (const kindToCheck of kindsToCheck) {
11183
+ if (backend.checkUniqueBatch === void 0) {
11184
+ for (const key of uniqueKeys) {
11185
+ if (existingByKey.has(key)) continue;
11186
+ const row = await backend.checkUnique({
11187
+ graphId,
11188
+ nodeKind: kindToCheck,
11189
+ constraintName,
11190
+ key,
11191
+ includeDeleted
11192
+ });
11193
+ if (row !== void 0) {
11194
+ existingByKey.set(row.key, row);
11195
+ }
11196
+ }
11197
+ } else {
11198
+ const rows = await backend.checkUniqueBatch({
11199
+ graphId,
11200
+ nodeKind: kindToCheck,
11201
+ constraintName,
11202
+ keys: uniqueKeys,
11203
+ includeDeleted
11204
+ });
11205
+ for (const row of rows) {
11206
+ if (!existingByKey.has(row.key)) {
11207
+ existingByKey.set(row.key, row);
11208
+ }
11209
+ }
11210
+ }
11211
+ }
11212
+ return existingByKey;
11213
+ }
11214
+ async function executeNodeCreateInternal(ctx, input, backend, options) {
11215
+ const kind = input.kind;
11216
+ const id = input.id ?? generateId();
11217
+ const opContext = ctx.createOperationContext("create", "node", kind, id);
11218
+ return ctx.withOperationHooks(opContext, async () => {
11219
+ const prepared = await validateAndPrepareNodeCreate(
11220
+ ctx,
11158
11221
  input,
11159
11222
  id,
11160
- validationBackend
11223
+ backend
11161
11224
  );
11162
- preparedCreates.push(prepared);
11163
- registerPendingNode(prepared.insertParams);
11164
- registerPendingUniqueEntries(
11165
- prepared.kind,
11166
- prepared.id,
11167
- prepared.validatedProps,
11168
- prepared.uniqueConstraints
11225
+ let row;
11226
+ {
11227
+ row = await backend.insertNode(prepared.insertParams);
11228
+ }
11229
+ await finalizeNodeCreate(ctx, prepared, backend);
11230
+ if (row === void 0) return;
11231
+ return rowToNode(row);
11232
+ });
11233
+ }
11234
+ async function executeNodeCreate(ctx, input, backend) {
11235
+ const result = await executeNodeCreateInternal(ctx, input, backend);
11236
+ if (!result) {
11237
+ throw new DatabaseOperationError(
11238
+ "Node create failed: expected created node row",
11239
+ { operation: "insert", entity: "node" }
11169
11240
  );
11170
11241
  }
11171
- const batchInsertParams = preparedCreates.map(
11172
- (prepared) => prepared.insertParams
11242
+ return result;
11243
+ }
11244
+ async function executeNodeCreateNoReturnBatch(ctx, inputs, backend) {
11245
+ if (inputs.length === 0) return;
11246
+ const { preparedCreates, batchInsertParams } = await prepareBatchCreates(
11247
+ ctx,
11248
+ inputs,
11249
+ backend
11250
+ );
11251
+ if (backend.insertNodesBatch === void 0) {
11252
+ for (const insertParams of batchInsertParams) {
11253
+ await (backend.insertNodeNoReturn?.(insertParams) ?? backend.insertNode(insertParams));
11254
+ }
11255
+ } else {
11256
+ await backend.insertNodesBatch(batchInsertParams);
11257
+ }
11258
+ for (const prepared of preparedCreates) {
11259
+ await finalizeNodeCreate(ctx, prepared, backend);
11260
+ }
11261
+ }
11262
+ async function executeNodeCreateBatch(ctx, inputs, backend) {
11263
+ if (inputs.length === 0) return [];
11264
+ const { preparedCreates, batchInsertParams } = await prepareBatchCreates(
11265
+ ctx,
11266
+ inputs,
11267
+ backend
11173
11268
  );
11174
11269
  let rows;
11175
11270
  if (backend.insertNodesBatchReturning === void 0) {
@@ -11187,170 +11282,40 @@ async function executeNodeCreateBatch(ctx, inputs, backend) {
11187
11282
  return rows.map((row) => rowToNode(row));
11188
11283
  }
11189
11284
  async function executeNodeUpdate(ctx, input, backend, options) {
11190
- const kind = input.kind;
11191
- const id = input.id;
11192
- const opContext = ctx.createOperationContext("update", "node", kind, id);
11193
- return ctx.withOperationHooks(opContext, async () => {
11194
- const registration = getNodeRegistration(ctx.graph, kind);
11195
- const existing = await backend.getNode(ctx.graphId, kind, id);
11196
- if (!existing || existing.deleted_at && !options?.clearDeleted) {
11197
- throw new NodeNotFoundError(kind, id);
11198
- }
11199
- const existingProps = JSON.parse(existing.props);
11200
- const mergedProps = { ...existingProps, ...input.props };
11201
- const nodeKind = registration.type;
11202
- const validatedProps = validateNodeProps(nodeKind.schema, mergedProps, {
11203
- kind,
11204
- operation: "update",
11205
- id
11206
- });
11207
- const validTo = validateOptionalIsoDate(input.validTo, "validTo");
11208
- const uniquenessContext = {
11209
- graphId: ctx.graphId,
11210
- registry: ctx.registry,
11211
- backend
11212
- };
11213
- await updateUniquenessEntries(
11214
- uniquenessContext,
11215
- kind,
11216
- id,
11217
- existingProps,
11218
- validatedProps,
11219
- registration.unique ?? []
11220
- );
11221
- const updateParams = {
11222
- graphId: ctx.graphId,
11223
- kind,
11224
- id,
11225
- props: validatedProps,
11226
- incrementVersion: true
11227
- };
11228
- if (validTo !== void 0) updateParams.validTo = validTo;
11229
- if (options?.clearDeleted) updateParams.clearDeleted = true;
11230
- const row = await backend.updateNode(updateParams);
11231
- const embeddingSyncContext = {
11232
- graphId: ctx.graphId,
11233
- nodeKind: kind,
11234
- nodeId: id,
11235
- backend
11236
- };
11237
- await syncEmbeddings(embeddingSyncContext, nodeKind.schema, validatedProps);
11238
- return rowToNode(row);
11239
- });
11285
+ const opContext = ctx.createOperationContext(
11286
+ "update",
11287
+ "node",
11288
+ input.kind,
11289
+ input.id
11290
+ );
11291
+ return ctx.withOperationHooks(
11292
+ opContext,
11293
+ () => performNodeUpdate(ctx, input, backend, options)
11294
+ );
11240
11295
  }
11241
11296
  async function executeNodeUpsertUpdate(ctx, input, backend, options) {
11242
- const kind = input.kind;
11243
- const id = input.id;
11244
- const registration = getNodeRegistration(ctx.graph, kind);
11245
- const existing = await backend.getNode(ctx.graphId, kind, id);
11246
- if (!existing || existing.deleted_at && !options?.clearDeleted) {
11247
- throw new NodeNotFoundError(kind, id);
11248
- }
11249
- const existingProps = JSON.parse(existing.props);
11250
- const mergedProps = { ...existingProps, ...input.props };
11251
- const nodeKind = registration.type;
11252
- const validatedProps = validateNodeProps(nodeKind.schema, mergedProps, {
11253
- kind,
11254
- operation: "update",
11255
- id
11256
- });
11257
- const validTo = validateOptionalIsoDate(input.validTo, "validTo");
11258
- const uniquenessContext = {
11259
- graphId: ctx.graphId,
11260
- registry: ctx.registry,
11261
- backend
11262
- };
11263
- await updateUniquenessEntries(
11264
- uniquenessContext,
11265
- kind,
11266
- id,
11267
- existingProps,
11268
- validatedProps,
11269
- registration.unique ?? []
11270
- );
11271
- const updateParams = {
11272
- graphId: ctx.graphId,
11273
- kind,
11274
- id,
11275
- props: validatedProps,
11276
- incrementVersion: true
11277
- };
11278
- if (validTo !== void 0) updateParams.validTo = validTo;
11279
- if (options?.clearDeleted) updateParams.clearDeleted = true;
11280
- const row = await backend.updateNode(updateParams);
11281
- const embeddingSyncContext = {
11282
- graphId: ctx.graphId,
11283
- nodeKind: kind,
11284
- nodeId: id,
11285
- backend
11286
- };
11287
- await syncEmbeddings(embeddingSyncContext, nodeKind.schema, validatedProps);
11288
- return rowToNode(row);
11297
+ return performNodeUpdate(ctx, input, backend, options);
11289
11298
  }
11290
11299
  async function executeNodeDelete(ctx, kind, id, backend) {
11291
11300
  const opContext = ctx.createOperationContext("delete", "node", kind, id);
11292
11301
  return ctx.withOperationHooks(opContext, async () => {
11293
11302
  const registration = getNodeRegistration(ctx.graph, kind);
11294
11303
  const existing = await backend.getNode(ctx.graphId, kind, id);
11295
- if (!existing || existing.deleted_at) {
11296
- return;
11297
- }
11304
+ if (!existing || existing.deleted_at) return;
11298
11305
  const existingProps = JSON.parse(existing.props);
11299
- const deleteBehavior = registration.onDelete ?? "restrict";
11300
- const connectedEdges = await backend.findEdgesConnectedTo({
11301
- graphId: ctx.graphId,
11302
- nodeKind: kind,
11303
- nodeId: id
11304
- });
11305
- if (connectedEdges.length > 0) {
11306
- switch (deleteBehavior) {
11307
- case "restrict": {
11308
- const edgeKinds = [
11309
- ...new Set(connectedEdges.map((edge) => edge.kind))
11310
- ];
11311
- throw new RestrictedDeleteError({
11312
- nodeKind: kind,
11313
- nodeId: id,
11314
- edgeCount: connectedEdges.length,
11315
- edgeKinds
11316
- });
11317
- }
11318
- case "cascade":
11319
- case "disconnect": {
11320
- for (const edge of connectedEdges) {
11321
- await backend.deleteEdge({
11322
- graphId: ctx.graphId,
11323
- id: edge.id
11324
- });
11325
- }
11326
- break;
11327
- }
11328
- }
11329
- }
11330
- await backend.deleteNode({
11331
- graphId: ctx.graphId,
11332
- kind,
11333
- id
11334
- });
11335
- const uniquenessContext = {
11336
- graphId: ctx.graphId,
11337
- registry: ctx.registry,
11338
- backend
11339
- };
11306
+ await enforceDeleteBehavior(ctx, kind, id, "soft", backend, registration);
11307
+ await backend.deleteNode({ graphId: ctx.graphId, kind, id });
11340
11308
  await deleteUniquenessEntries(
11341
- uniquenessContext,
11309
+ createUniquenessContext(ctx.graphId, ctx.registry, backend),
11342
11310
  kind,
11343
11311
  existingProps,
11344
11312
  registration.unique ?? []
11345
11313
  );
11346
11314
  const nodeKind = registration.type;
11347
- const embeddingSyncContext = {
11348
- graphId: ctx.graphId,
11349
- nodeKind: kind,
11350
- nodeId: id,
11351
- backend
11352
- };
11353
- await deleteNodeEmbeddings(embeddingSyncContext, nodeKind.schema);
11315
+ await deleteNodeEmbeddings(
11316
+ createEmbeddingSyncContext(ctx.graphId, kind, id, backend),
11317
+ nodeKind.schema
11318
+ );
11354
11319
  });
11355
11320
  }
11356
11321
  async function executeNodeHardDelete(ctx, kind, id, backend) {
@@ -11358,59 +11323,14 @@ async function executeNodeHardDelete(ctx, kind, id, backend) {
11358
11323
  return ctx.withOperationHooks(opContext, async () => {
11359
11324
  const registration = getNodeRegistration(ctx.graph, kind);
11360
11325
  const existing = await backend.getNode(ctx.graphId, kind, id);
11361
- if (!existing) {
11362
- return;
11363
- }
11364
- const deleteBehavior = registration.onDelete ?? "restrict";
11365
- const connectedEdges = await backend.findEdgesConnectedTo({
11366
- graphId: ctx.graphId,
11367
- nodeKind: kind,
11368
- nodeId: id
11369
- });
11370
- if (connectedEdges.length > 0) {
11371
- switch (deleteBehavior) {
11372
- case "restrict": {
11373
- const edgeKinds = [
11374
- ...new Set(connectedEdges.map((edge) => edge.kind))
11375
- ];
11376
- throw new RestrictedDeleteError({
11377
- nodeKind: kind,
11378
- nodeId: id,
11379
- edgeCount: connectedEdges.length,
11380
- edgeKinds
11381
- });
11382
- }
11383
- case "cascade":
11384
- case "disconnect": {
11385
- for (const edge of connectedEdges) {
11386
- await backend.hardDeleteEdge({
11387
- graphId: ctx.graphId,
11388
- id: edge.id
11389
- });
11390
- }
11391
- break;
11392
- }
11393
- }
11394
- }
11326
+ if (!existing) return;
11327
+ await enforceDeleteBehavior(ctx, kind, id, "hard", backend, registration);
11395
11328
  const hardDelete = async (target) => {
11396
- await target.hardDeleteNode({
11397
- graphId: ctx.graphId,
11398
- kind,
11399
- id
11400
- });
11329
+ await target.hardDeleteNode({ graphId: ctx.graphId, kind, id });
11401
11330
  };
11402
11331
  await ("transaction" in backend && backend.capabilities.transactions ? backend.transaction(async (tx) => hardDelete(tx)) : hardDelete(backend));
11403
11332
  });
11404
11333
  }
11405
- function resolveConstraint(graph, kind, constraintName) {
11406
- const registration = getNodeRegistration(graph, kind);
11407
- const constraints = registration.unique ?? [];
11408
- const constraint = constraints.find((c) => c.name === constraintName);
11409
- if (constraint === void 0) {
11410
- throw new NodeConstraintNotFoundError(constraintName, kind);
11411
- }
11412
- return constraint;
11413
- }
11414
11334
  async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, props, backend, options) {
11415
11335
  const ifExists = options?.ifExists ?? "return";
11416
11336
  const registration = getNodeRegistration(ctx.graph, kind);
@@ -11421,8 +11341,11 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
11421
11341
  });
11422
11342
  const constraint = resolveConstraint(ctx.graph, kind, constraintName);
11423
11343
  if (!checkWherePredicate(constraint, validatedProps)) {
11424
- const input = { kind, props: validatedProps };
11425
- const node = await executeNodeCreate(ctx, input, backend);
11344
+ const node = await executeNodeCreate(
11345
+ ctx,
11346
+ { kind, props: validatedProps },
11347
+ backend
11348
+ );
11426
11349
  return { node, action: "created" };
11427
11350
  }
11428
11351
  const key = computeUniqueKey(
@@ -11435,23 +11358,20 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
11435
11358
  constraint.scope,
11436
11359
  ctx.registry
11437
11360
  );
11438
- let existingUniqueRow;
11439
- for (const kindToCheck of kindsToCheck) {
11440
- const row = await backend.checkUnique({
11441
- graphId: ctx.graphId,
11442
- nodeKind: kindToCheck,
11443
- constraintName: constraint.name,
11444
- key,
11445
- includeDeleted: true
11446
- });
11447
- if (row !== void 0) {
11448
- existingUniqueRow = row;
11449
- break;
11450
- }
11451
- }
11361
+ const existingUniqueRow = await findUniqueRowAcrossKinds(
11362
+ backend,
11363
+ ctx.graphId,
11364
+ constraint.name,
11365
+ key,
11366
+ kindsToCheck,
11367
+ true
11368
+ );
11452
11369
  if (existingUniqueRow === void 0) {
11453
- const input = { kind, props: validatedProps };
11454
- const node = await executeNodeCreate(ctx, input, backend);
11370
+ const node = await executeNodeCreate(
11371
+ ctx,
11372
+ { kind, props: validatedProps },
11373
+ backend
11374
+ );
11455
11375
  return { node, action: "created" };
11456
11376
  }
11457
11377
  const existingRow = await backend.getNode(
@@ -11460,8 +11380,11 @@ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, pro
11460
11380
  existingUniqueRow.node_id
11461
11381
  );
11462
11382
  if (existingRow === void 0) {
11463
- const input = { kind, props: validatedProps };
11464
- const node = await executeNodeCreate(ctx, input, backend);
11383
+ const node = await executeNodeCreate(
11384
+ ctx,
11385
+ { kind, props: validatedProps },
11386
+ backend
11387
+ );
11465
11388
  return { node, action: "created" };
11466
11389
  }
11467
11390
  const isSoftDeleted = existingRow.deleted_at !== void 0;
@@ -11500,20 +11423,14 @@ async function executeNodeFindByConstraint(ctx, kind, constraintName, props, bac
11500
11423
  constraint.scope,
11501
11424
  ctx.registry
11502
11425
  );
11503
- let existingUniqueRow;
11504
- for (const kindToCheck of kindsToCheck) {
11505
- const row = await backend.checkUnique({
11506
- graphId: ctx.graphId,
11507
- nodeKind: kindToCheck,
11508
- constraintName: constraint.name,
11509
- key,
11510
- includeDeleted: false
11511
- });
11512
- if (row !== void 0) {
11513
- existingUniqueRow = row;
11514
- break;
11515
- }
11516
- }
11426
+ const existingUniqueRow = await findUniqueRowAcrossKinds(
11427
+ backend,
11428
+ ctx.graphId,
11429
+ constraint.name,
11430
+ key,
11431
+ kindsToCheck,
11432
+ false
11433
+ );
11517
11434
  if (existingUniqueRow === void 0) return void 0;
11518
11435
  const existingRow = await backend.getNode(
11519
11436
  ctx.graphId,
@@ -11524,11 +11441,7 @@ async function executeNodeFindByConstraint(ctx, kind, constraintName, props, bac
11524
11441
  return void 0;
11525
11442
  return rowToNode(existingRow);
11526
11443
  }
11527
- async function executeNodeBulkFindByConstraint(ctx, kind, constraintName, items, backend) {
11528
- if (items.length === 0) return [];
11529
- const registration = getNodeRegistration(ctx.graph, kind);
11530
- const nodeKind = registration.type;
11531
- const constraint = resolveConstraint(ctx.graph, kind, constraintName);
11444
+ function validateAndComputeKeys(nodeKind, kind, constraint, items) {
11532
11445
  const validated = [];
11533
11446
  for (const item of items) {
11534
11447
  const validatedProps = validateNodeProps(nodeKind.schema, item.props, {
@@ -11543,49 +11456,35 @@ async function executeNodeBulkFindByConstraint(ctx, kind, constraintName, items,
11543
11456
  ) : void 0;
11544
11457
  validated.push({ validatedProps, key });
11545
11458
  }
11546
- const uniqueKeys = [
11459
+ return validated;
11460
+ }
11461
+ function collectUniqueKeys(validated) {
11462
+ return [
11547
11463
  ...new Set(
11548
- validated.map((v) => v.key).filter((k) => k !== void 0)
11464
+ validated.map((entry) => entry.key).filter((key) => key !== void 0)
11549
11465
  )
11550
11466
  ];
11467
+ }
11468
+ async function executeNodeBulkFindByConstraint(ctx, kind, constraintName, items, backend) {
11469
+ if (items.length === 0) return [];
11470
+ const registration = getNodeRegistration(ctx.graph, kind);
11471
+ const nodeKind = registration.type;
11472
+ const constraint = resolveConstraint(ctx.graph, kind, constraintName);
11473
+ const validated = validateAndComputeKeys(nodeKind, kind, constraint, items);
11474
+ const uniqueKeys = collectUniqueKeys(validated);
11551
11475
  const kindsToCheck = getKindsForUniquenessCheck(
11552
11476
  kind,
11553
11477
  constraint.scope,
11554
11478
  ctx.registry
11555
11479
  );
11556
- const existingByKey = /* @__PURE__ */ new Map();
11557
- if (uniqueKeys.length > 0) {
11558
- for (const kindToCheck of kindsToCheck) {
11559
- if (backend.checkUniqueBatch === void 0) {
11560
- for (const key of uniqueKeys) {
11561
- if (existingByKey.has(key)) continue;
11562
- const row = await backend.checkUnique({
11563
- graphId: ctx.graphId,
11564
- nodeKind: kindToCheck,
11565
- constraintName: constraint.name,
11566
- key,
11567
- includeDeleted: false
11568
- });
11569
- if (row !== void 0) {
11570
- existingByKey.set(row.key, row);
11571
- }
11572
- }
11573
- } else {
11574
- const rows = await backend.checkUniqueBatch({
11575
- graphId: ctx.graphId,
11576
- nodeKind: kindToCheck,
11577
- constraintName: constraint.name,
11578
- keys: uniqueKeys,
11579
- includeDeleted: false
11580
- });
11581
- for (const row of rows) {
11582
- if (!existingByKey.has(row.key)) {
11583
- existingByKey.set(row.key, row);
11584
- }
11585
- }
11586
- }
11587
- }
11588
- }
11480
+ const existingByKey = uniqueKeys.length > 0 ? await batchCheckUniqueAcrossKinds(
11481
+ backend,
11482
+ ctx.graphId,
11483
+ constraint.name,
11484
+ uniqueKeys,
11485
+ kindsToCheck,
11486
+ false
11487
+ ) : /* @__PURE__ */ new Map();
11589
11488
  const results = Array.from({ length: items.length });
11590
11489
  const seenKeys = /* @__PURE__ */ new Map();
11591
11490
  for (const [index, { key }] of validated.entries()) {
@@ -11623,73 +11522,28 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11623
11522
  const registration = getNodeRegistration(ctx.graph, kind);
11624
11523
  const nodeKind = registration.type;
11625
11524
  const constraint = resolveConstraint(ctx.graph, kind, constraintName);
11626
- const validated = [];
11627
- for (const item of items) {
11628
- const validatedProps = validateNodeProps(nodeKind.schema, item.props, {
11629
- kind,
11630
- operation: "create"
11631
- });
11632
- const applies = checkWherePredicate(constraint, validatedProps);
11633
- const key = applies ? computeUniqueKey(
11634
- validatedProps,
11635
- constraint.fields,
11636
- constraint.collation
11637
- ) : void 0;
11638
- validated.push({ validatedProps, key });
11639
- }
11640
- const uniqueKeys = [
11641
- ...new Set(
11642
- validated.map((v) => v.key).filter((k) => k !== void 0)
11643
- )
11644
- ];
11525
+ const validated = validateAndComputeKeys(nodeKind, kind, constraint, items);
11526
+ const uniqueKeys = collectUniqueKeys(validated);
11645
11527
  const kindsToCheck = getKindsForUniquenessCheck(
11646
11528
  kind,
11647
11529
  constraint.scope,
11648
11530
  ctx.registry
11649
11531
  );
11650
- const existingByKey = /* @__PURE__ */ new Map();
11651
- if (uniqueKeys.length > 0) {
11652
- for (const kindToCheck of kindsToCheck) {
11653
- if (backend.checkUniqueBatch === void 0) {
11654
- for (const key of uniqueKeys) {
11655
- if (existingByKey.has(key)) continue;
11656
- const row = await backend.checkUnique({
11657
- graphId: ctx.graphId,
11658
- nodeKind: kindToCheck,
11659
- constraintName: constraint.name,
11660
- key,
11661
- includeDeleted: true
11662
- });
11663
- if (row !== void 0) {
11664
- existingByKey.set(row.key, row);
11665
- }
11666
- }
11667
- } else {
11668
- const rows = await backend.checkUniqueBatch({
11669
- graphId: ctx.graphId,
11670
- nodeKind: kindToCheck,
11671
- constraintName: constraint.name,
11672
- keys: uniqueKeys,
11673
- includeDeleted: true
11674
- });
11675
- for (const row of rows) {
11676
- if (!existingByKey.has(row.key)) {
11677
- existingByKey.set(row.key, row);
11678
- }
11679
- }
11680
- }
11681
- }
11682
- }
11532
+ const existingByKey = uniqueKeys.length > 0 ? await batchCheckUniqueAcrossKinds(
11533
+ backend,
11534
+ ctx.graphId,
11535
+ constraint.name,
11536
+ uniqueKeys,
11537
+ kindsToCheck,
11538
+ true
11539
+ ) : /* @__PURE__ */ new Map();
11683
11540
  const toCreate = [];
11684
11541
  const toFetch = [];
11685
11542
  const duplicateOf = [];
11686
11543
  const seenKeys = /* @__PURE__ */ new Map();
11687
11544
  for (const [index, { validatedProps, key }] of validated.entries()) {
11688
11545
  if (key === void 0) {
11689
- toCreate.push({
11690
- index,
11691
- input: { kind, props: validatedProps }
11692
- });
11546
+ toCreate.push({ index, input: { kind, props: validatedProps } });
11693
11547
  continue;
11694
11548
  }
11695
11549
  const previousIndex = seenKeys.get(key);
@@ -11700,10 +11554,7 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11700
11554
  seenKeys.set(key, index);
11701
11555
  const existing = existingByKey.get(key);
11702
11556
  if (existing === void 0) {
11703
- toCreate.push({
11704
- index,
11705
- input: { kind, props: validatedProps }
11706
- });
11557
+ toCreate.push({ index, input: { kind, props: validatedProps } });
11707
11558
  } else {
11708
11559
  toFetch.push({
11709
11560
  index,
@@ -11737,8 +11588,11 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
11737
11588
  nodeId
11738
11589
  );
11739
11590
  if (existingRow === void 0) {
11740
- const input = { kind, props: validatedProps };
11741
- const node = await executeNodeCreate(ctx, input, backend);
11591
+ const node = await executeNodeCreate(
11592
+ ctx,
11593
+ { kind, props: validatedProps },
11594
+ backend
11595
+ );
11742
11596
  results[index] = { node, action: "created" };
11743
11597
  continue;
11744
11598
  }