@nicia-ai/typegraph 0.3.0 → 0.4.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 (75) hide show
  1. package/dist/backend/drizzle/index.cjs +6 -6
  2. package/dist/backend/drizzle/index.d.cts +1 -1
  3. package/dist/backend/drizzle/index.d.ts +1 -1
  4. package/dist/backend/drizzle/index.js +4 -4
  5. package/dist/backend/drizzle/postgres.cjs +4 -4
  6. package/dist/backend/drizzle/postgres.d.cts +2 -2
  7. package/dist/backend/drizzle/postgres.d.ts +2 -2
  8. package/dist/backend/drizzle/postgres.js +3 -3
  9. package/dist/backend/drizzle/sqlite.cjs +4 -4
  10. package/dist/backend/drizzle/sqlite.d.cts +1 -1
  11. package/dist/backend/drizzle/sqlite.d.ts +1 -1
  12. package/dist/backend/drizzle/sqlite.js +3 -3
  13. package/dist/backend/postgres/index.cjs +4 -4
  14. package/dist/backend/postgres/index.d.cts +1 -1
  15. package/dist/backend/postgres/index.d.ts +1 -1
  16. package/dist/backend/postgres/index.js +3 -3
  17. package/dist/backend/sqlite/index.cjs +6 -6
  18. package/dist/backend/sqlite/index.d.cts +1 -1
  19. package/dist/backend/sqlite/index.d.ts +1 -1
  20. package/dist/backend/sqlite/index.js +4 -4
  21. package/dist/{chunk-OYL2SGBD.cjs → chunk-44SXEVF4.cjs} +18 -2
  22. package/dist/chunk-44SXEVF4.cjs.map +1 -0
  23. package/dist/{chunk-SFY2PPOY.cjs → chunk-7RVSDXT3.cjs} +50 -10
  24. package/dist/chunk-7RVSDXT3.cjs.map +1 -0
  25. package/dist/{chunk-ZJHQZZT2.cjs → chunk-K2ROKOK3.cjs} +6 -6
  26. package/dist/{chunk-ZJHQZZT2.cjs.map → chunk-K2ROKOK3.cjs.map} +1 -1
  27. package/dist/{chunk-DD6ONEBN.cjs → chunk-LDM2AFKZ.cjs} +12 -12
  28. package/dist/{chunk-DD6ONEBN.cjs.map → chunk-LDM2AFKZ.cjs.map} +1 -1
  29. package/dist/{chunk-V7CS2MDB.cjs → chunk-LUARLSYT.cjs} +18 -18
  30. package/dist/chunk-LUARLSYT.cjs.map +1 -0
  31. package/dist/{chunk-NP4G4ZKM.js → chunk-M5SOQ7UV.js} +4 -4
  32. package/dist/{chunk-NP4G4ZKM.js.map → chunk-M5SOQ7UV.js.map} +1 -1
  33. package/dist/{chunk-CMHFS34N.cjs → chunk-NYDXJGA3.cjs} +16 -16
  34. package/dist/{chunk-CMHFS34N.cjs.map → chunk-NYDXJGA3.cjs.map} +1 -1
  35. package/dist/{chunk-WE5BKYNB.js → chunk-Q6PXIKRQ.js} +3 -3
  36. package/dist/chunk-Q6PXIKRQ.js.map +1 -0
  37. package/dist/{chunk-O5XPCJLF.js → chunk-SJ2QMDXY.js} +18 -3
  38. package/dist/chunk-SJ2QMDXY.js.map +1 -0
  39. package/dist/{chunk-XDTYTNYL.js → chunk-TGDFBLGS.js} +3 -3
  40. package/dist/{chunk-XDTYTNYL.js.map → chunk-TGDFBLGS.js.map} +1 -1
  41. package/dist/{chunk-F2BZSEFE.js → chunk-ZO2FRJ2U.js} +4 -4
  42. package/dist/{chunk-F2BZSEFE.js.map → chunk-ZO2FRJ2U.js.map} +1 -1
  43. package/dist/{chunk-4HARSV2G.js → chunk-ZQGOBVXZ.js} +43 -3
  44. package/dist/chunk-ZQGOBVXZ.js.map +1 -0
  45. package/dist/{index-Dkicw49A.d.ts → index-DyrR_d-H.d.cts} +9 -1
  46. package/dist/{index-Dkicw49A.d.cts → index-DyrR_d-H.d.ts} +9 -1
  47. package/dist/index.cjs +893 -218
  48. package/dist/index.cjs.map +1 -1
  49. package/dist/index.d.cts +7 -7
  50. package/dist/index.d.ts +7 -7
  51. package/dist/index.js +687 -16
  52. package/dist/index.js.map +1 -1
  53. package/dist/interchange/index.cjs +7 -7
  54. package/dist/interchange/index.d.cts +3 -3
  55. package/dist/interchange/index.d.ts +3 -3
  56. package/dist/interchange/index.js +2 -2
  57. package/dist/{manager-Jc5Btay9.d.cts → manager-0NysX4s6.d.cts} +1 -1
  58. package/dist/{manager-e9LXthrx.d.ts → manager-DFKe7ql3.d.ts} +1 -1
  59. package/dist/profiler/index.d.cts +3 -3
  60. package/dist/profiler/index.d.ts +3 -3
  61. package/dist/schema/index.cjs +22 -22
  62. package/dist/schema/index.d.cts +5 -5
  63. package/dist/schema/index.d.ts +5 -5
  64. package/dist/schema/index.js +4 -4
  65. package/dist/{store-nbBybLWP.d.cts → store-DhoA5uRc.d.cts} +93 -7
  66. package/dist/{store-DM3Tk3Pw.d.ts → store-SiovWEYA.d.ts} +93 -7
  67. package/dist/{types-Cdbi4hcx.d.ts → types-6EKrWTs9.d.ts} +14 -0
  68. package/dist/{types-DDP0MGBF.d.cts → types-BUy-pHKH.d.cts} +14 -0
  69. package/package.json +2 -2
  70. package/dist/chunk-4HARSV2G.js.map +0 -1
  71. package/dist/chunk-O5XPCJLF.js.map +0 -1
  72. package/dist/chunk-OYL2SGBD.cjs.map +0 -1
  73. package/dist/chunk-SFY2PPOY.cjs.map +0 -1
  74. package/dist/chunk-V7CS2MDB.cjs.map +0 -1
  75. package/dist/chunk-WE5BKYNB.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,9 +1,9 @@
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-NP4G4ZKM.js';
2
- export { isMetaEdge } from './chunk-NP4G4ZKM.js';
3
- import { NODE_TYPE_BRAND, EDGE_TYPE_BRAND, nowIso, validateOptionalIsoDate, getNodeKinds, getEdgeKinds } from './chunk-XDTYTNYL.js';
4
- export { defineGraph, getEdgeKinds, getNodeKinds, isEdgeType, isEdgeTypeWithEndpoints, isGraphDef, isNodeType } from './chunk-XDTYTNYL.js';
5
- import { ConfigurationError, UnsupportedPredicateError, ValidationError, CompilerInvariantError, KindNotFoundError, RestrictedDeleteError, NodeNotFoundError, DatabaseOperationError, EdgeNotFoundError, UniquenessError, EndpointNotFoundError, EndpointError, DisjointError, CardinalityError } from './chunk-O5XPCJLF.js';
6
- export { CardinalityError, CompilerInvariantError, ConfigurationError, DatabaseOperationError, DisjointError, EdgeNotFoundError, EndpointError, EndpointNotFoundError, KindNotFoundError, MigrationError, NodeNotFoundError, RestrictedDeleteError, SchemaMismatchError, TypeGraphError, UniquenessError, UnsupportedPredicateError, ValidationError, VersionConflictError, getErrorSuggestion, isConstraintError, isSystemError, isTypeGraphError, isUserRecoverable } from './chunk-O5XPCJLF.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-M5SOQ7UV.js';
2
+ export { isMetaEdge } from './chunk-M5SOQ7UV.js';
3
+ import { NODE_TYPE_BRAND, EDGE_TYPE_BRAND, nowIso, validateOptionalIsoDate, getNodeKinds, getEdgeKinds } from './chunk-TGDFBLGS.js';
4
+ export { defineGraph, getEdgeKinds, getNodeKinds, isEdgeType, isEdgeTypeWithEndpoints, isGraphDef, isNodeType } from './chunk-TGDFBLGS.js';
5
+ import { ConfigurationError, UnsupportedPredicateError, ValidationError, CompilerInvariantError, KindNotFoundError, RestrictedDeleteError, NodeNotFoundError, DatabaseOperationError, EdgeNotFoundError, NodeConstraintNotFoundError, UniquenessError, EndpointNotFoundError, CardinalityError, EndpointError, DisjointError } from './chunk-SJ2QMDXY.js';
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
7
  import { getDialect, NODE_META_KEYS, EDGE_META_KEYS } from './chunk-54WJF3DW.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';
@@ -8452,7 +8452,7 @@ function createEdgeCollection(config) {
8452
8452
  const results = await executeEdgeCreateBatch2(batchInputs, backend);
8453
8453
  return narrowEdges(results);
8454
8454
  },
8455
- async bulkUpsert(items) {
8455
+ async bulkUpsertById(items) {
8456
8456
  if (items.length === 0) return [];
8457
8457
  const upsertAll = async (target) => {
8458
8458
  const ids = items.map((item) => item.id);
@@ -8478,7 +8478,7 @@ function createEdgeCollection(config) {
8478
8478
  if (existing) {
8479
8479
  const input = {
8480
8480
  id: item.id,
8481
- props: item.props ?? {}
8481
+ props: item.props
8482
8482
  };
8483
8483
  if (item.validTo !== void 0) input.validTo = item.validTo;
8484
8484
  toUpdate.push({
@@ -8494,7 +8494,7 @@ function createEdgeCollection(config) {
8494
8494
  fromId: item.from.id,
8495
8495
  toKind: item.to.kind,
8496
8496
  toId: item.to.id,
8497
- props: item.props ?? {}
8497
+ props: item.props
8498
8498
  };
8499
8499
  if (item.validFrom !== void 0) input.validFrom = item.validFrom;
8500
8500
  if (item.validTo !== void 0) input.validTo = item.validTo;
@@ -8548,6 +8548,57 @@ function createEdgeCollection(config) {
8548
8548
  return;
8549
8549
  }
8550
8550
  await deleteAll(backend);
8551
+ },
8552
+ async getOrCreateByEndpoints(from, to, props, options) {
8553
+ const getOrCreateOptions = {};
8554
+ if (options?.matchOn !== void 0)
8555
+ getOrCreateOptions.matchOn = options.matchOn;
8556
+ if (options?.ifExists !== void 0)
8557
+ getOrCreateOptions.ifExists = options.ifExists;
8558
+ const result = await config.executeGetOrCreateByEndpoints(
8559
+ kind,
8560
+ from.kind,
8561
+ from.id,
8562
+ to.kind,
8563
+ to.id,
8564
+ props,
8565
+ backend,
8566
+ getOrCreateOptions
8567
+ );
8568
+ return { edge: narrowEdge(result.edge), action: result.action };
8569
+ },
8570
+ async bulkGetOrCreateByEndpoints(items, options) {
8571
+ if (items.length === 0) return [];
8572
+ const mappedItems = items.map((item) => ({
8573
+ fromKind: item.from.kind,
8574
+ fromId: item.from.id,
8575
+ toKind: item.to.kind,
8576
+ toId: item.to.id,
8577
+ props: item.props
8578
+ }));
8579
+ const getOrCreateOptions = {};
8580
+ if (options?.matchOn !== void 0)
8581
+ getOrCreateOptions.matchOn = options.matchOn;
8582
+ if (options?.ifExists !== void 0)
8583
+ getOrCreateOptions.ifExists = options.ifExists;
8584
+ const getOrCreateAll = async (target) => {
8585
+ const results = await config.executeBulkGetOrCreateByEndpoints(
8586
+ kind,
8587
+ mappedItems,
8588
+ target,
8589
+ getOrCreateOptions
8590
+ );
8591
+ return results.map((result) => ({
8592
+ edge: narrowEdge(result.edge),
8593
+ action: result.action
8594
+ }));
8595
+ };
8596
+ if (backend.capabilities.transactions && "transaction" in backend) {
8597
+ return backend.transaction(
8598
+ async (txBackend) => getOrCreateAll(txBackend)
8599
+ );
8600
+ }
8601
+ return getOrCreateAll(backend);
8551
8602
  }
8552
8603
  };
8553
8604
  }
@@ -8586,7 +8637,9 @@ function createNodeCollection(config) {
8586
8637
  executeDelete: executeNodeDelete2,
8587
8638
  executeHardDelete: executeNodeHardDelete2,
8588
8639
  matchesTemporalMode,
8589
- createQuery
8640
+ createQuery,
8641
+ executeGetOrCreateByConstraint,
8642
+ executeBulkGetOrCreateByConstraint
8590
8643
  } = config;
8591
8644
  return {
8592
8645
  async create(props, options) {
@@ -8696,7 +8749,7 @@ function createNodeCollection(config) {
8696
8749
  }
8697
8750
  return backend.countNodesByKind(params);
8698
8751
  },
8699
- async upsert(id, props, options) {
8752
+ async upsertById(id, props, options) {
8700
8753
  const existing = await backend.getNode(graphId, kind, id);
8701
8754
  if (existing) {
8702
8755
  const input = {
@@ -8737,7 +8790,7 @@ function createNodeCollection(config) {
8737
8790
  const results = await executeNodeCreateBatch2(batchInputs, backend);
8738
8791
  return narrowNodes(results);
8739
8792
  },
8740
- async bulkUpsert(items) {
8793
+ async bulkUpsertById(items) {
8741
8794
  if (items.length === 0) return [];
8742
8795
  const upsertAll = async (target) => {
8743
8796
  const ids = items.map((item) => item.id);
@@ -8834,6 +8887,44 @@ function createNodeCollection(config) {
8834
8887
  return;
8835
8888
  }
8836
8889
  await deleteAll(backend);
8890
+ },
8891
+ async getOrCreateByConstraint(constraintName, props, options) {
8892
+ const execute = async (target) => {
8893
+ const result = await executeGetOrCreateByConstraint(
8894
+ kind,
8895
+ constraintName,
8896
+ props,
8897
+ target,
8898
+ options
8899
+ );
8900
+ return result;
8901
+ };
8902
+ if (backend.capabilities.transactions && "transaction" in backend) {
8903
+ return backend.transaction(async (txBackend) => execute(txBackend));
8904
+ }
8905
+ return execute(backend);
8906
+ },
8907
+ async bulkGetOrCreateByConstraint(constraintName, items, options) {
8908
+ if (items.length === 0) return [];
8909
+ const mappedItems = items.map((item) => ({
8910
+ props: item.props
8911
+ }));
8912
+ const getOrCreateAll = async (target) => {
8913
+ const results = await executeBulkGetOrCreateByConstraint(
8914
+ kind,
8915
+ constraintName,
8916
+ mappedItems,
8917
+ target,
8918
+ options
8919
+ );
8920
+ return results;
8921
+ };
8922
+ if (backend.capabilities.transactions && "transaction" in backend) {
8923
+ return backend.transaction(
8924
+ async (txBackend) => getOrCreateAll(txBackend)
8925
+ );
8926
+ }
8927
+ return getOrCreateAll(backend);
8837
8928
  }
8838
8929
  };
8839
8930
  }
@@ -8891,16 +8982,18 @@ function createEdgeCollectionsProxy(graph, graphId, registry, backend, operation
8891
8982
  }
8892
8983
 
8893
8984
  // src/constraints/index.ts
8985
+ var UNIQUE_KEY_SEPARATOR = "";
8986
+ var UNIQUE_KEY_NULL_MARKER = "";
8894
8987
  function computeUniqueKey(props, fields, collation) {
8895
8988
  const values = fields.map((field2) => {
8896
8989
  const value = props[field2];
8897
8990
  if (value === void 0 || value === null) {
8898
- return "\0";
8991
+ return UNIQUE_KEY_NULL_MARKER;
8899
8992
  }
8900
8993
  const stringValue = typeof value === "string" ? value : typeof value === "number" || typeof value === "boolean" ? value.toString() : JSON.stringify(value);
8901
8994
  return collation === "caseInsensitive" ? stringValue.toLowerCase() : stringValue;
8902
8995
  });
8903
- return values.join("\0");
8996
+ return values.join(UNIQUE_KEY_SEPARATOR);
8904
8997
  }
8905
8998
  function checkWherePredicate(constraint, props) {
8906
8999
  if (!constraint.where) {
@@ -9577,6 +9670,321 @@ async function executeEdgeHardDelete(ctx, id, backend) {
9577
9670
  });
9578
9671
  });
9579
9672
  }
9673
+ var RECORD_SEP = "";
9674
+ var UNIT_SEP = "";
9675
+ var UNDEFINED_SENTINEL = "";
9676
+ function validateMatchOnFields(schema, matchOn, edgeKind) {
9677
+ if (matchOn.length === 0) return;
9678
+ const shape = schema.shape;
9679
+ if (shape === void 0) {
9680
+ throw new ValidationError(
9681
+ `Edge kind "${edgeKind}" has no schema shape to validate matchOn fields against`,
9682
+ {
9683
+ kind: edgeKind,
9684
+ operation: "create",
9685
+ issues: matchOn.map((field2) => ({
9686
+ path: field2,
9687
+ message: `Field "${field2}" does not exist in edge schema`
9688
+ }))
9689
+ }
9690
+ );
9691
+ }
9692
+ const invalidFields = matchOn.filter((field2) => !(field2 in shape));
9693
+ if (invalidFields.length > 0) {
9694
+ throw new ValidationError(
9695
+ `Invalid matchOn fields for edge kind "${edgeKind}": ${invalidFields.join(", ")}`,
9696
+ {
9697
+ kind: edgeKind,
9698
+ operation: "create",
9699
+ issues: invalidFields.map((field2) => ({
9700
+ path: field2,
9701
+ message: `Field "${field2}" does not exist in edge schema`
9702
+ }))
9703
+ }
9704
+ );
9705
+ }
9706
+ }
9707
+ function stableStringify(value) {
9708
+ if (value === void 0) return UNDEFINED_SENTINEL;
9709
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
9710
+ if (Array.isArray(value)) {
9711
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
9712
+ }
9713
+ const sorted = Object.keys(value).toSorted();
9714
+ const entries = sorted.map(
9715
+ (key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`
9716
+ );
9717
+ return `{${entries.join(",")}}`;
9718
+ }
9719
+ function buildEdgeCompositeKey(fromKind, fromId, toKind, toId, props, matchOn) {
9720
+ const endpointPart = `${fromKind}${RECORD_SEP}${fromId}${RECORD_SEP}${toKind}${RECORD_SEP}${toId}`;
9721
+ if (matchOn.length === 0) return endpointPart;
9722
+ const sortedFields = [...matchOn].toSorted();
9723
+ const propertyParts = sortedFields.map(
9724
+ (field2) => `${RECORD_SEP}${field2}${UNIT_SEP}${stableStringify(props[field2])}`
9725
+ );
9726
+ return endpointPart + propertyParts.join("");
9727
+ }
9728
+ function buildEndpointPairKey(fromKind, fromId, toKind, toId) {
9729
+ return `${fromKind}${RECORD_SEP}${fromId}${RECORD_SEP}${toKind}${RECORD_SEP}${toId}`;
9730
+ }
9731
+ function findMatchingEdge(rows, matchOn, inputProps) {
9732
+ let liveRow;
9733
+ let deletedRow;
9734
+ for (const row of rows) {
9735
+ if (matchOn.length > 0) {
9736
+ const rowProps = JSON.parse(row.props);
9737
+ const matches = matchOn.every(
9738
+ (field2) => stableStringify(rowProps[field2]) === stableStringify(inputProps[field2])
9739
+ );
9740
+ if (!matches) continue;
9741
+ }
9742
+ if (row.deleted_at === void 0) {
9743
+ liveRow ??= row;
9744
+ } else {
9745
+ deletedRow ??= row;
9746
+ }
9747
+ if (liveRow !== void 0) break;
9748
+ }
9749
+ return { liveRow, deletedRow };
9750
+ }
9751
+ async function executeEdgeGetOrCreateByEndpoints(ctx, kind, fromKind, fromId, toKind, toId, props, backend, options) {
9752
+ const ifExists = options?.ifExists ?? "return";
9753
+ const matchOn = options?.matchOn ?? [];
9754
+ const registration = getEdgeRegistration(ctx.graph, kind);
9755
+ const edgeKind = registration.type;
9756
+ const validatedProps = validateEdgeProps(edgeKind.schema, props, {
9757
+ kind,
9758
+ operation: "create"
9759
+ });
9760
+ validateMatchOnFields(edgeKind.schema, matchOn, kind);
9761
+ const candidateRows = await backend.findEdgesByKind({
9762
+ graphId: ctx.graphId,
9763
+ kind,
9764
+ fromKind,
9765
+ fromId,
9766
+ toKind,
9767
+ toId,
9768
+ excludeDeleted: false,
9769
+ temporalMode: "includeTombstones"
9770
+ });
9771
+ const { liveRow, deletedRow } = findMatchingEdge(
9772
+ candidateRows,
9773
+ matchOn,
9774
+ validatedProps
9775
+ );
9776
+ if (liveRow === void 0 && deletedRow === void 0) {
9777
+ const input = {
9778
+ kind,
9779
+ fromKind,
9780
+ fromId,
9781
+ toKind,
9782
+ toId,
9783
+ props: validatedProps
9784
+ };
9785
+ const edge2 = await executeEdgeCreate(ctx, input, backend);
9786
+ return { edge: edge2, action: "created" };
9787
+ }
9788
+ if (liveRow !== void 0) {
9789
+ if (ifExists === "return") {
9790
+ return { edge: rowToEdge(liveRow), action: "found" };
9791
+ }
9792
+ const edge2 = await executeEdgeUpsertUpdate(
9793
+ ctx,
9794
+ { id: liveRow.id, props: validatedProps },
9795
+ backend
9796
+ );
9797
+ return { edge: edge2, action: "updated" };
9798
+ }
9799
+ const cardinality = registration.cardinality ?? "many";
9800
+ const matchedDeletedRow = deletedRow;
9801
+ const effectiveValidTo = matchedDeletedRow.valid_to;
9802
+ const constraintContext = {
9803
+ graphId: ctx.graphId,
9804
+ registry: ctx.registry,
9805
+ backend
9806
+ };
9807
+ await checkCardinalityConstraint(
9808
+ constraintContext,
9809
+ kind,
9810
+ cardinality,
9811
+ fromKind,
9812
+ fromId,
9813
+ toKind,
9814
+ toId,
9815
+ effectiveValidTo
9816
+ );
9817
+ const edge = await executeEdgeUpsertUpdate(
9818
+ ctx,
9819
+ { id: matchedDeletedRow.id, props: validatedProps },
9820
+ backend,
9821
+ { clearDeleted: true }
9822
+ );
9823
+ return { edge, action: "resurrected" };
9824
+ }
9825
+ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend, options) {
9826
+ if (items.length === 0) return [];
9827
+ const ifExists = options?.ifExists ?? "return";
9828
+ const matchOn = options?.matchOn ?? [];
9829
+ const registration = getEdgeRegistration(ctx.graph, kind);
9830
+ const edgeKind = registration.type;
9831
+ const cardinality = registration.cardinality ?? "many";
9832
+ validateMatchOnFields(edgeKind.schema, matchOn, kind);
9833
+ const validated = [];
9834
+ for (const item of items) {
9835
+ const validatedProps = validateEdgeProps(edgeKind.schema, item.props, {
9836
+ kind,
9837
+ operation: "create"
9838
+ });
9839
+ const compositeKey = buildEdgeCompositeKey(
9840
+ item.fromKind,
9841
+ item.fromId,
9842
+ item.toKind,
9843
+ item.toId,
9844
+ validatedProps,
9845
+ matchOn
9846
+ );
9847
+ const endpointKey = buildEndpointPairKey(
9848
+ item.fromKind,
9849
+ item.fromId,
9850
+ item.toKind,
9851
+ item.toId
9852
+ );
9853
+ validated.push({
9854
+ fromKind: item.fromKind,
9855
+ fromId: item.fromId,
9856
+ toKind: item.toKind,
9857
+ toId: item.toId,
9858
+ validatedProps,
9859
+ compositeKey,
9860
+ endpointKey
9861
+ });
9862
+ }
9863
+ const uniqueEndpoints = /* @__PURE__ */ new Map();
9864
+ for (const entry of validated) {
9865
+ if (!uniqueEndpoints.has(entry.endpointKey)) {
9866
+ uniqueEndpoints.set(entry.endpointKey, {
9867
+ fromKind: entry.fromKind,
9868
+ fromId: entry.fromId,
9869
+ toKind: entry.toKind,
9870
+ toId: entry.toId
9871
+ });
9872
+ }
9873
+ }
9874
+ const rowsByEndpoint = /* @__PURE__ */ new Map();
9875
+ for (const [endpointKey, endpoint] of uniqueEndpoints) {
9876
+ const rows = await backend.findEdgesByKind({
9877
+ graphId: ctx.graphId,
9878
+ kind,
9879
+ fromKind: endpoint.fromKind,
9880
+ fromId: endpoint.fromId,
9881
+ toKind: endpoint.toKind,
9882
+ toId: endpoint.toId,
9883
+ excludeDeleted: false,
9884
+ temporalMode: "includeTombstones"
9885
+ });
9886
+ rowsByEndpoint.set(endpointKey, rows);
9887
+ }
9888
+ const toCreate = [];
9889
+ const toFetch = [];
9890
+ const duplicateOf = [];
9891
+ const seenKeys = /* @__PURE__ */ new Map();
9892
+ for (const [index, entry] of validated.entries()) {
9893
+ const previousIndex = seenKeys.get(entry.compositeKey);
9894
+ if (previousIndex !== void 0) {
9895
+ duplicateOf.push({ index, sourceIndex: previousIndex });
9896
+ continue;
9897
+ }
9898
+ seenKeys.set(entry.compositeKey, index);
9899
+ const candidateRows = rowsByEndpoint.get(entry.endpointKey) ?? [];
9900
+ const { liveRow, deletedRow } = findMatchingEdge(
9901
+ candidateRows,
9902
+ matchOn,
9903
+ entry.validatedProps
9904
+ );
9905
+ if (liveRow === void 0 && deletedRow === void 0) {
9906
+ toCreate.push({
9907
+ index,
9908
+ input: {
9909
+ kind,
9910
+ fromKind: entry.fromKind,
9911
+ fromId: entry.fromId,
9912
+ toKind: entry.toKind,
9913
+ toId: entry.toId,
9914
+ props: entry.validatedProps
9915
+ }
9916
+ });
9917
+ } else {
9918
+ const bestRow = liveRow ?? deletedRow;
9919
+ toFetch.push({
9920
+ index,
9921
+ row: bestRow,
9922
+ isDeleted: liveRow === void 0,
9923
+ validatedProps: entry.validatedProps,
9924
+ fromKind: entry.fromKind,
9925
+ fromId: entry.fromId,
9926
+ toKind: entry.toKind,
9927
+ toId: entry.toId
9928
+ });
9929
+ }
9930
+ }
9931
+ const results = Array.from({ length: items.length });
9932
+ if (toCreate.length > 0) {
9933
+ const createInputs = toCreate.map((entry) => entry.input);
9934
+ const createdEdges = await executeEdgeCreateBatch(
9935
+ ctx,
9936
+ createInputs,
9937
+ backend
9938
+ );
9939
+ for (const [batchIndex, entry] of toCreate.entries()) {
9940
+ results[entry.index] = {
9941
+ edge: createdEdges[batchIndex],
9942
+ action: "created"
9943
+ };
9944
+ }
9945
+ }
9946
+ for (const entry of toFetch) {
9947
+ if (entry.isDeleted) {
9948
+ const effectiveValidTo = entry.row.valid_to;
9949
+ const constraintContext = {
9950
+ graphId: ctx.graphId,
9951
+ registry: ctx.registry,
9952
+ backend
9953
+ };
9954
+ await checkCardinalityConstraint(
9955
+ constraintContext,
9956
+ kind,
9957
+ cardinality,
9958
+ entry.fromKind,
9959
+ entry.fromId,
9960
+ entry.toKind,
9961
+ entry.toId,
9962
+ effectiveValidTo
9963
+ );
9964
+ const edge = await executeEdgeUpsertUpdate(
9965
+ ctx,
9966
+ { id: entry.row.id, props: entry.validatedProps },
9967
+ backend,
9968
+ { clearDeleted: true }
9969
+ );
9970
+ results[entry.index] = { edge, action: "resurrected" };
9971
+ } else if (ifExists === "update") {
9972
+ const edge = await executeEdgeUpsertUpdate(
9973
+ ctx,
9974
+ { id: entry.row.id, props: entry.validatedProps },
9975
+ backend
9976
+ );
9977
+ results[entry.index] = { edge, action: "updated" };
9978
+ } else {
9979
+ results[entry.index] = { edge: rowToEdge(entry.row), action: "found" };
9980
+ }
9981
+ }
9982
+ for (const { index, sourceIndex } of duplicateOf) {
9983
+ const sourceResult = results[sourceIndex];
9984
+ results[index] = { edge: sourceResult.edge, action: "found" };
9985
+ }
9986
+ return results;
9987
+ }
9580
9988
 
9581
9989
  // src/store/embedding-sync.ts
9582
9990
  function getEmbeddingFields(schema) {
@@ -10359,6 +10767,235 @@ async function executeNodeHardDelete(ctx, kind, id, backend) {
10359
10767
  await ("transaction" in backend && backend.capabilities.transactions ? backend.transaction(async (tx) => hardDelete(tx)) : hardDelete(backend));
10360
10768
  });
10361
10769
  }
10770
+ function resolveConstraint(graph, kind, constraintName) {
10771
+ const registration = getNodeRegistration(graph, kind);
10772
+ const constraints = registration.unique ?? [];
10773
+ const constraint = constraints.find((c) => c.name === constraintName);
10774
+ if (constraint === void 0) {
10775
+ throw new NodeConstraintNotFoundError(constraintName, kind);
10776
+ }
10777
+ return constraint;
10778
+ }
10779
+ async function executeNodeGetOrCreateByConstraint(ctx, kind, constraintName, props, backend, options) {
10780
+ const ifExists = options?.ifExists ?? "return";
10781
+ const registration = getNodeRegistration(ctx.graph, kind);
10782
+ const nodeKind = registration.type;
10783
+ const validatedProps = validateNodeProps(nodeKind.schema, props, {
10784
+ kind,
10785
+ operation: "create"
10786
+ });
10787
+ const constraint = resolveConstraint(ctx.graph, kind, constraintName);
10788
+ if (!checkWherePredicate(constraint, validatedProps)) {
10789
+ const input = { kind, props: validatedProps };
10790
+ const node = await executeNodeCreate(ctx, input, backend);
10791
+ return { node, action: "created" };
10792
+ }
10793
+ const key = computeUniqueKey(
10794
+ validatedProps,
10795
+ constraint.fields,
10796
+ constraint.collation
10797
+ );
10798
+ const kindsToCheck = getKindsForUniquenessCheck(
10799
+ kind,
10800
+ constraint.scope,
10801
+ ctx.registry
10802
+ );
10803
+ let existingUniqueRow;
10804
+ for (const kindToCheck of kindsToCheck) {
10805
+ const row = await backend.checkUnique({
10806
+ graphId: ctx.graphId,
10807
+ nodeKind: kindToCheck,
10808
+ constraintName: constraint.name,
10809
+ key,
10810
+ includeDeleted: true
10811
+ });
10812
+ if (row !== void 0) {
10813
+ existingUniqueRow = row;
10814
+ break;
10815
+ }
10816
+ }
10817
+ if (existingUniqueRow === void 0) {
10818
+ const input = { kind, props: validatedProps };
10819
+ const node = await executeNodeCreate(ctx, input, backend);
10820
+ return { node, action: "created" };
10821
+ }
10822
+ const existingRow = await backend.getNode(
10823
+ ctx.graphId,
10824
+ existingUniqueRow.concrete_kind,
10825
+ existingUniqueRow.node_id
10826
+ );
10827
+ if (existingRow === void 0) {
10828
+ const input = { kind, props: validatedProps };
10829
+ const node = await executeNodeCreate(ctx, input, backend);
10830
+ return { node, action: "created" };
10831
+ }
10832
+ const isSoftDeleted = existingRow.deleted_at !== void 0;
10833
+ if (isSoftDeleted || ifExists === "update") {
10834
+ const concreteKind = existingUniqueRow.concrete_kind;
10835
+ const node = await executeNodeUpsertUpdate(
10836
+ ctx,
10837
+ {
10838
+ kind: concreteKind,
10839
+ id: existingRow.id,
10840
+ props: validatedProps
10841
+ },
10842
+ backend,
10843
+ { clearDeleted: isSoftDeleted }
10844
+ );
10845
+ return { node, action: isSoftDeleted ? "resurrected" : "updated" };
10846
+ }
10847
+ return { node: rowToNode(existingRow), action: "found" };
10848
+ }
10849
+ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName, items, backend, options) {
10850
+ if (items.length === 0) return [];
10851
+ const ifExists = options?.ifExists ?? "return";
10852
+ const registration = getNodeRegistration(ctx.graph, kind);
10853
+ const nodeKind = registration.type;
10854
+ const constraint = resolveConstraint(ctx.graph, kind, constraintName);
10855
+ const validated = [];
10856
+ for (const item of items) {
10857
+ const validatedProps = validateNodeProps(nodeKind.schema, item.props, {
10858
+ kind,
10859
+ operation: "create"
10860
+ });
10861
+ const applies = checkWherePredicate(constraint, validatedProps);
10862
+ const key = applies ? computeUniqueKey(
10863
+ validatedProps,
10864
+ constraint.fields,
10865
+ constraint.collation
10866
+ ) : void 0;
10867
+ validated.push({ validatedProps, key });
10868
+ }
10869
+ const uniqueKeys = [
10870
+ ...new Set(
10871
+ validated.map((v) => v.key).filter((k) => k !== void 0)
10872
+ )
10873
+ ];
10874
+ const kindsToCheck = getKindsForUniquenessCheck(
10875
+ kind,
10876
+ constraint.scope,
10877
+ ctx.registry
10878
+ );
10879
+ const existingByKey = /* @__PURE__ */ new Map();
10880
+ if (uniqueKeys.length > 0) {
10881
+ for (const kindToCheck of kindsToCheck) {
10882
+ if (backend.checkUniqueBatch === void 0) {
10883
+ for (const key of uniqueKeys) {
10884
+ if (existingByKey.has(key)) continue;
10885
+ const row = await backend.checkUnique({
10886
+ graphId: ctx.graphId,
10887
+ nodeKind: kindToCheck,
10888
+ constraintName: constraint.name,
10889
+ key,
10890
+ includeDeleted: true
10891
+ });
10892
+ if (row !== void 0) {
10893
+ existingByKey.set(row.key, row);
10894
+ }
10895
+ }
10896
+ } else {
10897
+ const rows = await backend.checkUniqueBatch({
10898
+ graphId: ctx.graphId,
10899
+ nodeKind: kindToCheck,
10900
+ constraintName: constraint.name,
10901
+ keys: uniqueKeys,
10902
+ includeDeleted: true
10903
+ });
10904
+ for (const row of rows) {
10905
+ if (!existingByKey.has(row.key)) {
10906
+ existingByKey.set(row.key, row);
10907
+ }
10908
+ }
10909
+ }
10910
+ }
10911
+ }
10912
+ const toCreate = [];
10913
+ const toFetch = [];
10914
+ const duplicateOf = [];
10915
+ const seenKeys = /* @__PURE__ */ new Map();
10916
+ for (const [index, { validatedProps, key }] of validated.entries()) {
10917
+ if (key === void 0) {
10918
+ toCreate.push({
10919
+ index,
10920
+ input: { kind, props: validatedProps }
10921
+ });
10922
+ continue;
10923
+ }
10924
+ const previousIndex = seenKeys.get(key);
10925
+ if (previousIndex !== void 0) {
10926
+ duplicateOf.push({ index, sourceIndex: previousIndex });
10927
+ continue;
10928
+ }
10929
+ seenKeys.set(key, index);
10930
+ const existing = existingByKey.get(key);
10931
+ if (existing === void 0) {
10932
+ toCreate.push({
10933
+ index,
10934
+ input: { kind, props: validatedProps }
10935
+ });
10936
+ } else {
10937
+ toFetch.push({
10938
+ index,
10939
+ nodeId: existing.node_id,
10940
+ concreteKind: existing.concrete_kind,
10941
+ validatedProps,
10942
+ isSoftDeleted: existing.deleted_at !== void 0
10943
+ });
10944
+ }
10945
+ }
10946
+ const results = Array.from({ length: items.length });
10947
+ if (toCreate.length > 0) {
10948
+ const createInputs = toCreate.map((entry) => entry.input);
10949
+ const createdNodes = await executeNodeCreateBatch(
10950
+ ctx,
10951
+ createInputs,
10952
+ backend
10953
+ );
10954
+ for (const [batchIndex, entry] of toCreate.entries()) {
10955
+ results[entry.index] = {
10956
+ node: createdNodes[batchIndex],
10957
+ action: "created"
10958
+ };
10959
+ }
10960
+ }
10961
+ for (const entry of toFetch) {
10962
+ const { index, concreteKind, validatedProps, isSoftDeleted, nodeId } = entry;
10963
+ const existingRow = await backend.getNode(
10964
+ ctx.graphId,
10965
+ concreteKind,
10966
+ nodeId
10967
+ );
10968
+ if (existingRow === void 0) {
10969
+ const input = { kind, props: validatedProps };
10970
+ const node = await executeNodeCreate(ctx, input, backend);
10971
+ results[index] = { node, action: "created" };
10972
+ continue;
10973
+ }
10974
+ if (isSoftDeleted || ifExists === "update") {
10975
+ const node = await executeNodeUpsertUpdate(
10976
+ ctx,
10977
+ {
10978
+ kind: concreteKind,
10979
+ id: existingRow.id,
10980
+ props: validatedProps
10981
+ },
10982
+ backend,
10983
+ { clearDeleted: isSoftDeleted }
10984
+ );
10985
+ results[index] = {
10986
+ node,
10987
+ action: isSoftDeleted ? "resurrected" : "updated"
10988
+ };
10989
+ } else {
10990
+ results[index] = { node: rowToNode(existingRow), action: "found" };
10991
+ }
10992
+ }
10993
+ for (const { index, sourceIndex } of duplicateOf) {
10994
+ const sourceResult = results[sourceIndex];
10995
+ results[index] = { node: sourceResult.node, action: "found" };
10996
+ }
10997
+ return results;
10998
+ }
10362
10999
 
10363
11000
  // src/store/store.ts
10364
11001
  var Store = class {
@@ -10472,7 +11109,23 @@ var Store = class {
10472
11109
  executeDelete: (kind, id, backend) => executeNodeDelete(ctx, kind, id, backend),
10473
11110
  executeHardDelete: (kind, id, backend) => executeNodeHardDelete(ctx, kind, id, backend),
10474
11111
  matchesTemporalMode: (row, options) => this.#matchesTemporalMode(row, options),
10475
- createQuery: () => this.query()
11112
+ createQuery: () => this.query(),
11113
+ executeGetOrCreateByConstraint: (kind, constraintName, props, backend, options) => executeNodeGetOrCreateByConstraint(
11114
+ ctx,
11115
+ kind,
11116
+ constraintName,
11117
+ props,
11118
+ backend,
11119
+ options
11120
+ ),
11121
+ executeBulkGetOrCreateByConstraint: (kind, constraintName, items, backend, options) => executeNodeBulkGetOrCreateByConstraint(
11122
+ ctx,
11123
+ kind,
11124
+ constraintName,
11125
+ items,
11126
+ backend,
11127
+ options
11128
+ )
10476
11129
  };
10477
11130
  }
10478
11131
  /**
@@ -10491,7 +11144,25 @@ var Store = class {
10491
11144
  executeDelete: (id, backend) => executeEdgeDelete(ctx, id, backend),
10492
11145
  executeHardDelete: (id, backend) => executeEdgeHardDelete(ctx, id, backend),
10493
11146
  matchesTemporalMode: (row, options) => this.#matchesTemporalMode(row, options),
10494
- createQuery: () => this.query()
11147
+ createQuery: () => this.query(),
11148
+ executeGetOrCreateByEndpoints: (kind, fromKind, fromId, toKind, toId, props, backend, options) => executeEdgeGetOrCreateByEndpoints(
11149
+ ctx,
11150
+ kind,
11151
+ fromKind,
11152
+ fromId,
11153
+ toKind,
11154
+ toId,
11155
+ props,
11156
+ backend,
11157
+ options
11158
+ ),
11159
+ executeBulkGetOrCreateByEndpoints: (kind, items, backend, options) => executeEdgeBulkGetOrCreateByEndpoints(
11160
+ ctx,
11161
+ kind,
11162
+ items,
11163
+ backend,
11164
+ options
11165
+ )
10495
11166
  };
10496
11167
  }
10497
11168
  // === Query Builder ===