@nicia-ai/typegraph 0.24.1 → 0.25.1

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 (159) hide show
  1. package/dist/backend/postgres/index.cjs +226 -55
  2. package/dist/backend/postgres/index.cjs.map +1 -1
  3. package/dist/backend/postgres/index.d.cts +6 -7
  4. package/dist/backend/postgres/index.d.ts +6 -7
  5. package/dist/backend/postgres/index.js +195 -28
  6. package/dist/backend/postgres/index.js.map +1 -1
  7. package/dist/backend/sqlite/index.cjs +19 -17
  8. package/dist/backend/sqlite/index.d.cts +6 -7
  9. package/dist/backend/sqlite/index.d.ts +6 -7
  10. package/dist/backend/sqlite/index.js +9 -7
  11. package/dist/backend/sqlite/libsql.cjs +12 -10
  12. package/dist/backend/sqlite/libsql.cjs.map +1 -1
  13. package/dist/backend/sqlite/libsql.d.cts +4 -5
  14. package/dist/backend/sqlite/libsql.d.ts +4 -5
  15. package/dist/backend/sqlite/libsql.js +9 -7
  16. package/dist/backend/sqlite/libsql.js.map +1 -1
  17. package/dist/backend/sqlite/local.cjs +14 -12
  18. package/dist/backend/sqlite/local.cjs.map +1 -1
  19. package/dist/backend/sqlite/local.d.cts +4 -5
  20. package/dist/backend/sqlite/local.d.ts +4 -5
  21. package/dist/backend/sqlite/local.js +9 -7
  22. package/dist/backend/sqlite/local.js.map +1 -1
  23. package/dist/chunk-4BOWVUGQ.js +2029 -0
  24. package/dist/chunk-4BOWVUGQ.js.map +1 -0
  25. package/dist/chunk-7V335GHG.cjs +112 -0
  26. package/dist/chunk-7V335GHG.cjs.map +1 -0
  27. package/dist/chunk-AAA2CR3P.js +2628 -0
  28. package/dist/chunk-AAA2CR3P.js.map +1 -0
  29. package/dist/{chunk-QOLTW7OZ.js → chunk-CLEUFN2K.js} +195 -13
  30. package/dist/chunk-CLEUFN2K.js.map +1 -0
  31. package/dist/{chunk-OVJ2BE5Q.cjs → chunk-CLGC7AQ2.cjs} +215 -33
  32. package/dist/chunk-CLGC7AQ2.cjs.map +1 -0
  33. package/dist/{chunk-GUTQTMOV.js → chunk-CPFR3O7L.js} +2 -2
  34. package/dist/chunk-CPFR3O7L.js.map +1 -0
  35. package/dist/{chunk-5HVDOPZU.cjs → chunk-CQVNGKU7.cjs} +452 -104
  36. package/dist/chunk-CQVNGKU7.cjs.map +1 -0
  37. package/dist/chunk-CTHNVU6K.cjs +551 -0
  38. package/dist/chunk-CTHNVU6K.cjs.map +1 -0
  39. package/dist/chunk-D2GO3RBL.cjs +12 -0
  40. package/dist/chunk-D2GO3RBL.cjs.map +1 -0
  41. package/dist/{chunk-WFMTAEEN.js → chunk-DOTEN2FG.js} +81 -7
  42. package/dist/chunk-DOTEN2FG.js.map +1 -0
  43. package/dist/chunk-E5JZSEGJ.cjs +301 -0
  44. package/dist/chunk-E5JZSEGJ.cjs.map +1 -0
  45. package/dist/chunk-EG6TJ3I5.js +286 -0
  46. package/dist/chunk-EG6TJ3I5.js.map +1 -0
  47. package/dist/chunk-EJCDOA37.js +537 -0
  48. package/dist/chunk-EJCDOA37.js.map +1 -0
  49. package/dist/{chunk-UVZHTBZ5.cjs → chunk-EK6RJ373.cjs} +7 -150
  50. package/dist/chunk-EK6RJ373.cjs.map +1 -0
  51. package/dist/{chunk-3JK66E3I.cjs → chunk-JTI5WT4P.cjs} +630 -59
  52. package/dist/chunk-JTI5WT4P.cjs.map +1 -0
  53. package/dist/chunk-NHQWXSTS.js +10 -0
  54. package/dist/chunk-NHQWXSTS.js.map +1 -0
  55. package/dist/chunk-NZ5PMBTG.js +105 -0
  56. package/dist/chunk-NZ5PMBTG.js.map +1 -0
  57. package/dist/{chunk-SUHW2UYL.cjs → chunk-OECDVACY.cjs} +2 -2
  58. package/dist/chunk-OECDVACY.cjs.map +1 -0
  59. package/dist/{chunk-EYELZHKY.js → chunk-OOS2RKRH.js} +5 -140
  60. package/dist/chunk-OOS2RKRH.js.map +1 -0
  61. package/dist/{chunk-EY4LBX3F.js → chunk-PSKUWVCB.js} +373 -38
  62. package/dist/chunk-PSKUWVCB.js.map +1 -0
  63. package/dist/chunk-QMQYK3IF.cjs +2680 -0
  64. package/dist/chunk-QMQYK3IF.cjs.map +1 -0
  65. package/dist/chunk-RCAJZG67.cjs +2069 -0
  66. package/dist/chunk-RCAJZG67.cjs.map +1 -0
  67. package/dist/{chunk-O5PST6TB.cjs → chunk-W5MTHMTE.cjs} +83 -6
  68. package/dist/chunk-W5MTHMTE.cjs.map +1 -0
  69. package/dist/chunk-WVQXAFP3.js +79 -0
  70. package/dist/chunk-WVQXAFP3.js.map +1 -0
  71. package/dist/chunk-X3244EKP.cjs +84 -0
  72. package/dist/chunk-X3244EKP.cjs.map +1 -0
  73. package/dist/{chunk-753F33CV.js → chunk-YZZBDCLV.js} +617 -44
  74. package/dist/chunk-YZZBDCLV.js.map +1 -0
  75. package/dist/{ddl-B_H8JM-E.d.cts → ddl-BqMmpNor.d.ts} +1328 -283
  76. package/dist/{ddl-1tFIvt9V.d.ts → ddl-Bwjh8Hff.d.cts} +1328 -283
  77. package/dist/extension-types-D07e_NTw.d.cts +543 -0
  78. package/dist/extension-types-D07e_NTw.d.ts +543 -0
  79. package/dist/graph-extension/index.cjs +71 -0
  80. package/dist/graph-extension/index.cjs.map +1 -0
  81. package/dist/graph-extension/index.d.cts +278 -0
  82. package/dist/graph-extension/index.d.ts +278 -0
  83. package/dist/graph-extension/index.js +6 -0
  84. package/dist/graph-extension/index.js.map +1 -0
  85. package/dist/{index-DXkEXpNz.d.ts → index-CT6nvtxL.d.cts} +114 -5
  86. package/dist/{index-DXkEXpNz.d.cts → index-DQheiVQV.d.ts} +114 -5
  87. package/dist/index.cjs +2672 -901
  88. package/dist/index.cjs.map +1 -1
  89. package/dist/index.d.cts +29 -10
  90. package/dist/index.d.ts +29 -10
  91. package/dist/index.js +2248 -598
  92. package/dist/index.js.map +1 -1
  93. package/dist/indexes/index.cjs +25 -23
  94. package/dist/indexes/index.d.cts +40 -22
  95. package/dist/indexes/index.d.ts +40 -22
  96. package/dist/indexes/index.js +5 -3
  97. package/dist/interchange/index.cjs +8 -7
  98. package/dist/interchange/index.cjs.map +1 -1
  99. package/dist/interchange/index.d.cts +5 -4
  100. package/dist/interchange/index.d.ts +5 -4
  101. package/dist/interchange/index.js +3 -2
  102. package/dist/interchange/index.js.map +1 -1
  103. package/dist/{manager-Vkz28eAC.d.cts → manager-Cy3dhwYO.d.cts} +194 -6
  104. package/dist/{manager-nIhmttD-.d.ts → manager-sZkU2VPC.d.ts} +194 -6
  105. package/dist/materialize-indexes-BB9N5uQl.d.cts +68 -0
  106. package/dist/materialize-indexes-BYpU2RKh.d.ts +68 -0
  107. package/dist/profiler/index.cjs +10 -8
  108. package/dist/profiler/index.cjs.map +1 -1
  109. package/dist/profiler/index.d.cts +8 -7
  110. package/dist/profiler/index.d.ts +8 -7
  111. package/dist/profiler/index.js +3 -1
  112. package/dist/profiler/index.js.map +1 -1
  113. package/dist/schema/index.cjs +43 -22
  114. package/dist/schema/index.cjs.map +1 -1
  115. package/dist/schema/index.d.cts +17 -8
  116. package/dist/schema/index.d.ts +17 -8
  117. package/dist/schema/index.js +9 -4
  118. package/dist/schema/index.js.map +1 -1
  119. package/dist/{sqlite-D0aceIe0.d.cts → sqlite-BhFswiAR.d.cts} +837 -120
  120. package/dist/{sqlite-SUxbZqfy.d.ts → sqlite-h6dta1IJ.d.ts} +837 -120
  121. package/dist/{store-CpvwTMxV.d.cts → store-BS_hehiz.d.ts} +659 -23
  122. package/dist/{store-U3i2tUch.d.ts → store-CkCvqRUf.d.cts} +659 -23
  123. package/dist/{types-yH4_p0XB.d.cts → types-BpgACExN.d.cts} +3 -2
  124. package/dist/{types-qYFZ9SqD.d.ts → types-Bz-Pi681.d.ts} +3 -2
  125. package/dist/types-DptZHe-l.d.ts +2132 -0
  126. package/dist/types-WF5ItKQu.d.cts +2132 -0
  127. package/dist/where-C40InMIp.d.ts +11 -0
  128. package/dist/where-DZYMiXVq.d.cts +11 -0
  129. package/package.json +33 -73
  130. package/dist/ast-C8S37w1A.d.ts +0 -497
  131. package/dist/ast-dgLhLOg0.d.cts +0 -497
  132. package/dist/chunk-3JK66E3I.cjs.map +0 -1
  133. package/dist/chunk-3PPEHLGK.js +0 -1116
  134. package/dist/chunk-3PPEHLGK.js.map +0 -1
  135. package/dist/chunk-5HVDOPZU.cjs.map +0 -1
  136. package/dist/chunk-753F33CV.js.map +0 -1
  137. package/dist/chunk-EY4LBX3F.js.map +0 -1
  138. package/dist/chunk-EYELZHKY.js.map +0 -1
  139. package/dist/chunk-GFGVREHH.js +0 -1381
  140. package/dist/chunk-GFGVREHH.js.map +0 -1
  141. package/dist/chunk-GUTQTMOV.js.map +0 -1
  142. package/dist/chunk-LBORRUNJ.js +0 -135
  143. package/dist/chunk-LBORRUNJ.js.map +0 -1
  144. package/dist/chunk-O5PST6TB.cjs.map +0 -1
  145. package/dist/chunk-OLROXS2S.cjs +0 -147
  146. package/dist/chunk-OLROXS2S.cjs.map +0 -1
  147. package/dist/chunk-OVJ2BE5Q.cjs.map +0 -1
  148. package/dist/chunk-PAUN562Y.cjs +0 -1417
  149. package/dist/chunk-PAUN562Y.cjs.map +0 -1
  150. package/dist/chunk-PEE4EWXZ.cjs +0 -1131
  151. package/dist/chunk-PEE4EWXZ.cjs.map +0 -1
  152. package/dist/chunk-QOLTW7OZ.js.map +0 -1
  153. package/dist/chunk-SUHW2UYL.cjs.map +0 -1
  154. package/dist/chunk-UVZHTBZ5.cjs.map +0 -1
  155. package/dist/chunk-WFMTAEEN.js.map +0 -1
  156. package/dist/types-C-zfYDJB.d.ts +0 -159
  157. package/dist/types-CQx4iW5Y.d.cts +0 -159
  158. package/dist/types-Qk2l3IkQ.d.cts +0 -1254
  159. package/dist/types-Qk2l3IkQ.d.ts +0 -1254
@@ -0,0 +1,2029 @@
1
+ import { TypeGraphError, ConfigurationError } from './chunk-DOTEN2FG.js';
2
+ import { encodeJsonPointerSegment } from './chunk-NZ5PMBTG.js';
3
+
4
+ // src/graph-extension/errors.ts
5
+ var GRAPH_EXTENSION_ISSUE_CODES = [
6
+ "UNSUPPORTED_PROPERTY_TYPE",
7
+ "INVALID_PROPERTY_REFINEMENT",
8
+ "NESTED_ARRAY",
9
+ "NESTED_OBJECT_TOO_DEEP",
10
+ "INVALID_MODIFIER_TARGET",
11
+ "INVALID_ENUM_VALUES",
12
+ "INVALID_NUMBER_BOUNDS",
13
+ "INVALID_LENGTH_BOUNDS",
14
+ "INVALID_PATTERN",
15
+ "INVALID_EMBEDDING_DIMENSIONS",
16
+ "INVALID_SEARCHABLE_LANGUAGE",
17
+ "RESERVED_PROPERTY_NAME",
18
+ "INVALID_KIND_NAME",
19
+ "DUPLICATE_KIND_NAME",
20
+ "EMPTY_PROPERTIES",
21
+ "EMPTY_FROM_OR_TO",
22
+ "DUPLICATE_UNIQUE_CONSTRAINT",
23
+ "EMPTY_UNIQUE_FIELDS",
24
+ "DUPLICATE_UNIQUE_FIELD",
25
+ "UNKNOWN_UNIQUE_FIELD",
26
+ "INVALID_UNIQUE_WHERE_OP",
27
+ "UNKNOWN_UNIQUE_WHERE_FIELD",
28
+ "INVALID_ANNOTATION",
29
+ "UNKNOWN_META_EDGE",
30
+ "ONTOLOGY_CYCLE",
31
+ "ONTOLOGY_SELF_LOOP",
32
+ "ONTOLOGY_DISJOINT_CONFLICT",
33
+ "DUPLICATE_ONTOLOGY_RELATION",
34
+ "INVALID_DOCUMENT_SHAPE",
35
+ "UNKNOWN_DOCUMENT_KEY",
36
+ "UNSUPPORTED_STRING_FORMAT",
37
+ "INVALID_INDEX_DECLARATION",
38
+ "DUPLICATE_INDEX_NAME",
39
+ "EMPTY_INDEX_FIELDS",
40
+ "UNKNOWN_PROPERTY_KEY"
41
+ ];
42
+ var GraphExtensionError = class extends TypeGraphError {
43
+ constructor(spec) {
44
+ super(spec.message, spec.code, {
45
+ details: spec.details,
46
+ category: spec.category ?? "user",
47
+ suggestion: spec.suggestion,
48
+ ...spec.cause === void 0 ? {} : { cause: spec.cause }
49
+ });
50
+ this.name = new.target.name;
51
+ }
52
+ };
53
+ var GraphExtensionValidationError = class extends GraphExtensionError {
54
+ code = "GRAPH_EXTENSION_INVALID";
55
+ issues;
56
+ constructor(issues, cause) {
57
+ const frozenIssues = Object.freeze([...issues]);
58
+ super({
59
+ message: `Graph extension document is invalid (${issues.length} issue${issues.length === 1 ? "" : "s"}): ${summarizeIssues(issues)}`,
60
+ code: "GRAPH_EXTENSION_INVALID",
61
+ details: { issues: frozenIssues },
62
+ suggestion: `Inspect error.issues for per-field paths and codes; each issue's "path" is a JSON pointer into the source document.`,
63
+ cause
64
+ });
65
+ this.issues = frozenIssues;
66
+ }
67
+ };
68
+ function summarizeWithOverflow(items, formatItem, emptyMessage) {
69
+ if (items.length === 0) return emptyMessage;
70
+ const head = items.slice(0, 3).map((item) => formatItem(item)).join("; ");
71
+ const overflow = items.length > 3 ? ` (+${items.length - 3} more)` : "";
72
+ return `${head}${overflow}`;
73
+ }
74
+ function summarizeIssues(issues) {
75
+ return summarizeWithOverflow(
76
+ issues,
77
+ (issue) => `${issue.path || "(root)"} \u2014 ${issue.message}`,
78
+ "no specific issues recorded"
79
+ );
80
+ }
81
+ var KindCollisionError = class extends GraphExtensionError {
82
+ code = "KIND_COLLISION";
83
+ kindName;
84
+ entity;
85
+ constructor(kindName, entity, graphId) {
86
+ super({
87
+ message: `Graph extension declares ${entity} kind "${kindName}" which already exists as a compile-time kind on graph "${graphId}". Graph-extension-declared kinds cannot collide with compile-time kinds.`,
88
+ code: "KIND_COLLISION",
89
+ details: { kindName, entity, graphId },
90
+ suggestion: "Pick a different graph-extension-declared kind name, or remove the compile-time declaration."
91
+ });
92
+ this.kindName = kindName;
93
+ this.entity = entity;
94
+ }
95
+ };
96
+ var INCOMPATIBLE_CHANGE_TYPES = [
97
+ "REMOVE_PROPERTY",
98
+ "ADD_REQUIRED_PROPERTY",
99
+ "TIGHTEN_OPTIONALITY",
100
+ "TIGHTEN_CONSTRAINT",
101
+ "ADD_PATTERN",
102
+ "CHANGE_PATTERN",
103
+ "ADD_FORMAT",
104
+ "CHANGE_FORMAT",
105
+ "TIGHTEN_INT",
106
+ "TIGHTEN_ENUM",
107
+ "TYPE_CHANGE",
108
+ "ADD_UNIQUE_ON_POPULATED",
109
+ "TIGHTEN_EDGE_ENDPOINTS"
110
+ ];
111
+ var IncompatibleChangeError = class extends GraphExtensionError {
112
+ code = "INCOMPATIBLE_CHANGE";
113
+ changes;
114
+ constructor(changes, graphId) {
115
+ const frozenChanges = Object.freeze([...changes]);
116
+ super({
117
+ message: `Graph extension contains ${changes.length} incompatible change${changes.length === 1 ? "" : "s"} against existing graph-extension kinds on graph "${graphId}": ${summarizeChanges(changes)}`,
118
+ code: "INCOMPATIBLE_CHANGE",
119
+ details: { graphId, changes: frozenChanges },
120
+ suggestion: "Inspect error.changes for the per-field rejections. Compatible options: change the modification to an additive one (e.g. `optional: true`), or apply the change against an empty kind."
121
+ });
122
+ this.changes = frozenChanges;
123
+ }
124
+ };
125
+ function summarizeChanges(changes) {
126
+ return summarizeWithOverflow(
127
+ changes,
128
+ (change) => {
129
+ const field = change.field === void 0 ? "" : `.${change.field}`;
130
+ const detail = change.detail === void 0 ? "" : ` (${change.detail})`;
131
+ return `${change.kind}${field}: ${change.type}${detail}`;
132
+ },
133
+ "(no specific changes recorded)"
134
+ );
135
+ }
136
+ var GraphExtensionUnresolvedEndpointError = class extends GraphExtensionError {
137
+ code = "GRAPH_EXTENSION_UNRESOLVED_ENDPOINT";
138
+ edgeKind;
139
+ side;
140
+ endpoint;
141
+ constructor(edgeKind, side, endpoint, graphId) {
142
+ super({
143
+ message: `Graph-extension edge "${edgeKind}" ${side}-endpoint "${endpoint}" does not resolve to any kind on graph "${graphId}". The compile-time kind may have been removed since the extension was committed.`,
144
+ code: "GRAPH_EXTENSION_UNRESOLVED_ENDPOINT",
145
+ details: { edgeKind, side, endpoint, graphId },
146
+ suggestion: "Restore the compile-time kind, or remove the graph-extension-declared kind via store.removeKinds before redeploying without it."
147
+ });
148
+ this.edgeKind = edgeKind;
149
+ this.side = side;
150
+ this.endpoint = endpoint;
151
+ }
152
+ };
153
+ var RemoveCompileTimeKindError = class extends GraphExtensionError {
154
+ code = "REMOVE_COMPILE_TIME_KIND";
155
+ kindName;
156
+ entity;
157
+ constructor(kindName, entity, graphId) {
158
+ super({
159
+ message: `Cannot remove compile-time ${entity} kind "${kindName}" via store.removeKinds. Compile-time kinds are removed by recompiling and redeploying without them on graph "${graphId}".`,
160
+ code: "REMOVE_COMPILE_TIME_KIND",
161
+ details: { kindName, entity, graphId },
162
+ suggestion: "Drop the kind from your defineGraph call and redeploy. Only graph-extension kinds (added via store.evolve) are removable through removeKinds."
163
+ });
164
+ this.kindName = kindName;
165
+ this.entity = entity;
166
+ }
167
+ };
168
+ var KindHasReferentsError = class extends GraphExtensionError {
169
+ code = "KIND_HAS_REFERENTS";
170
+ kindName;
171
+ referents;
172
+ constructor(kindName, referents, graphId) {
173
+ const summary = referents.map((referent) => `${referent.type}:${referent.name}`).join(", ");
174
+ const frozenReferents = Object.freeze([...referents]);
175
+ super({
176
+ message: `Cannot remove graph-extension kind "${kindName}" on graph "${graphId}" \u2014 it is referenced by compile-time declarations: ${summary}.`,
177
+ code: "KIND_HAS_REFERENTS",
178
+ details: { kindName, graphId, referents: frozenReferents },
179
+ suggestion: "Remove the compile-time references first (drop the edge or ontology relation from defineGraph), or pick a different kind to remove."
180
+ });
181
+ this.kindName = kindName;
182
+ this.referents = frozenReferents;
183
+ }
184
+ };
185
+ var GraphExtensionVersionUnsupportedError = class extends GraphExtensionError {
186
+ code = "GRAPH_EXTENSION_VERSION_UNSUPPORTED";
187
+ persistedVersion;
188
+ currentVersion;
189
+ constructor(persistedVersion, currentVersion) {
190
+ super({
191
+ message: `Persisted extension was authored against graph-extension format v${persistedVersion}, but this library only supports up to v${currentVersion}. Upgrade @nicia-ai/typegraph.`,
192
+ code: "GRAPH_EXTENSION_VERSION_UNSUPPORTED",
193
+ details: { persistedVersion, currentVersion },
194
+ category: "system",
195
+ suggestion: `Upgrade @nicia-ai/typegraph to a version that supports graph-extension format v${persistedVersion}, or downgrade the writer.`
196
+ });
197
+ this.persistedVersion = persistedVersion;
198
+ this.currentVersion = currentVersion;
199
+ }
200
+ };
201
+
202
+ // src/graph-extension/extension-types.ts
203
+ var LEGACY_GRAPH_EXTENSION_VERSION = 1;
204
+ var CURRENT_GRAPH_EXTENSION_VERSION = 1;
205
+ var GRAPH_EXTENSION_TOP_LEVEL_KEYS = [
206
+ "version",
207
+ "nodes",
208
+ "edges",
209
+ "ontology",
210
+ "indexes"
211
+ ];
212
+
213
+ // src/utils/object.ts
214
+ function isPlainObject(value) {
215
+ if (typeof value !== "object" || value === null) return false;
216
+ const prototype = Object.getPrototypeOf(value);
217
+ return prototype === Object.prototype || prototype === null;
218
+ }
219
+ function compactUndefined(value) {
220
+ const result = {};
221
+ for (const [key, fieldValue] of Object.entries(value)) {
222
+ if (fieldValue !== void 0) result[key] = fieldValue;
223
+ }
224
+ return result;
225
+ }
226
+ function freezeDeep(value) {
227
+ if (value === null || typeof value !== "object") return value;
228
+ if (Object.isFrozen(value)) return value;
229
+ for (const nested of Object.values(value)) {
230
+ freezeDeep(nested);
231
+ }
232
+ return Object.freeze(value);
233
+ }
234
+
235
+ // src/core/json-value.ts
236
+ function assertJsonValue(value, rootLabel, ownerKind) {
237
+ walk(value, rootLabel, ownerKind);
238
+ }
239
+ function walk(value, path, ownerKind) {
240
+ if (value === null) return;
241
+ const valueType = typeof value;
242
+ if (valueType === "number") {
243
+ if (!Number.isFinite(value)) {
244
+ throwInvalid(path, describeNonFinite(value), ownerKind);
245
+ }
246
+ return;
247
+ }
248
+ if (valueType === "string" || valueType === "boolean") return;
249
+ if (valueType === "bigint" || valueType === "function" || valueType === "symbol" || valueType === "undefined") {
250
+ throwInvalid(path, valueType, ownerKind);
251
+ }
252
+ if (Array.isArray(value)) {
253
+ for (const [index, element] of value.entries()) {
254
+ walk(element, `${path}[${index}]`, ownerKind);
255
+ }
256
+ return;
257
+ }
258
+ if (!isPlainObject(value)) {
259
+ throwInvalid(path, describeNonPlain(value), ownerKind);
260
+ }
261
+ for (const [key, nested] of Object.entries(value)) {
262
+ walk(nested, `${path}.${key}`, ownerKind);
263
+ }
264
+ }
265
+ function describeNonFinite(value) {
266
+ if (Number.isNaN(value)) return "NaN";
267
+ if (value === Number.POSITIVE_INFINITY) return "Infinity";
268
+ if (value === Number.NEGATIVE_INFINITY) return "-Infinity";
269
+ return "non-finite number";
270
+ }
271
+ function describeNonPlain(value) {
272
+ const constructorName = value?.constructor?.name;
273
+ if (typeof constructorName === "string" && constructorName.length > 0) {
274
+ return `${constructorName} instance`;
275
+ }
276
+ return "non-plain object";
277
+ }
278
+ function throwInvalid(path, kind, ownerKind) {
279
+ throw new ConfigurationError(
280
+ `${ownerKind} ${path} contains a non-JSON value (${kind}). Annotations must be JSON-serializable.`,
281
+ { path, valueKind: kind },
282
+ {
283
+ suggestion: "Use only strings, numbers, booleans, null, arrays, and plain objects in annotations."
284
+ }
285
+ );
286
+ }
287
+
288
+ // src/ontology/closures.ts
289
+ function computeTransitiveClosure(relations) {
290
+ const closure = /* @__PURE__ */ new Map();
291
+ const allNodes = /* @__PURE__ */ new Set();
292
+ for (const [from, to] of relations) {
293
+ allNodes.add(from);
294
+ allNodes.add(to);
295
+ }
296
+ for (const node of allNodes) {
297
+ closure.set(node, /* @__PURE__ */ new Set());
298
+ }
299
+ for (const [from, to] of relations) {
300
+ closure.get(from)?.add(to);
301
+ }
302
+ for (const k of allNodes) {
303
+ for (const index of allNodes) {
304
+ const indexReaches = closure.get(index);
305
+ if (!indexReaches?.has(k)) continue;
306
+ const kReaches = closure.get(k);
307
+ if (!kReaches) continue;
308
+ for (const index2 of kReaches) {
309
+ indexReaches.add(index2);
310
+ }
311
+ }
312
+ }
313
+ return closure;
314
+ }
315
+ function invertClosure(closure) {
316
+ const result = /* @__PURE__ */ new Map();
317
+ for (const [from, tos] of closure) {
318
+ for (const to of tos) {
319
+ const existing = result.get(to) ?? /* @__PURE__ */ new Set();
320
+ existing.add(from);
321
+ result.set(to, existing);
322
+ }
323
+ }
324
+ return result;
325
+ }
326
+ function isReachable(closure, source, target) {
327
+ return closure.get(source)?.has(target) ?? false;
328
+ }
329
+
330
+ // src/ontology/constants.ts
331
+ var META_EDGE_SUB_CLASS_OF = "subClassOf";
332
+ var META_EDGE_BROADER = "broader";
333
+ var META_EDGE_NARROWER = "narrower";
334
+ var META_EDGE_RELATED_TO = "relatedTo";
335
+ var META_EDGE_EQUIVALENT_TO = "equivalentTo";
336
+ var META_EDGE_SAME_AS = "sameAs";
337
+ var META_EDGE_DIFFERENT_FROM = "differentFrom";
338
+ var META_EDGE_DISJOINT_WITH = "disjointWith";
339
+ var META_EDGE_PART_OF = "partOf";
340
+ var META_EDGE_HAS_PART = "hasPart";
341
+ var META_EDGE_INVERSE_OF = "inverseOf";
342
+ var META_EDGE_IMPLIES = "implies";
343
+ var ALL_META_EDGE_NAMES = [
344
+ META_EDGE_SUB_CLASS_OF,
345
+ META_EDGE_BROADER,
346
+ META_EDGE_NARROWER,
347
+ META_EDGE_RELATED_TO,
348
+ META_EDGE_EQUIVALENT_TO,
349
+ META_EDGE_SAME_AS,
350
+ META_EDGE_DIFFERENT_FROM,
351
+ META_EDGE_DISJOINT_WITH,
352
+ META_EDGE_PART_OF,
353
+ META_EDGE_HAS_PART,
354
+ META_EDGE_INVERSE_OF,
355
+ META_EDGE_IMPLIES
356
+ ];
357
+
358
+ // src/store/reserved-keys.ts
359
+ var RESERVED_NODE_KEYS = /* @__PURE__ */ new Set([
360
+ "id",
361
+ "kind",
362
+ "meta"
363
+ ]);
364
+ var RESERVED_EDGE_KEYS = /* @__PURE__ */ new Set([
365
+ "id",
366
+ "kind",
367
+ "meta",
368
+ "fromKind",
369
+ "fromId",
370
+ "toKind",
371
+ "toId"
372
+ ]);
373
+ var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
374
+ "__proto__",
375
+ "constructor",
376
+ "prototype"
377
+ ]);
378
+ var RESERVED_PROPERTY_PREFIX = "$";
379
+ function isReservedPropertyName(name) {
380
+ return name.startsWith(RESERVED_PROPERTY_PREFIX);
381
+ }
382
+ function assertSchemaKeysAreFree(entityKind, name, keys, reservedStructuralKeys) {
383
+ const structuralConflicts = keys.filter(
384
+ (key) => reservedStructuralKeys.has(key)
385
+ );
386
+ if (structuralConflicts.length > 0) {
387
+ const label = entityKind.toLowerCase();
388
+ throw new ConfigurationError(
389
+ `${entityKind} "${name}" schema contains reserved property names: ${structuralConflicts.join(", ")}`,
390
+ {
391
+ [`${label}Type`]: name,
392
+ conflicts: structuralConflicts,
393
+ reservedKeys: [...reservedStructuralKeys]
394
+ },
395
+ {
396
+ suggestion: `Rename the conflicting properties. Reserved names (${[...reservedStructuralKeys].join(", ")}) are added automatically to all ${label}s.`
397
+ }
398
+ );
399
+ }
400
+ const prefixConflicts = keys.filter((key) => isReservedPropertyName(key));
401
+ if (prefixConflicts.length > 0) {
402
+ const label = entityKind.toLowerCase();
403
+ throw new ConfigurationError(
404
+ `${entityKind} "${name}" schema uses the reserved "${RESERVED_PROPERTY_PREFIX}" prefix on: ${prefixConflicts.join(", ")}`,
405
+ {
406
+ [`${label}Type`]: name,
407
+ conflicts: prefixConflicts,
408
+ reservedPrefix: RESERVED_PROPERTY_PREFIX
409
+ },
410
+ {
411
+ suggestion: `Property names starting with "${RESERVED_PROPERTY_PREFIX}" are reserved for TypeGraph accessors (e.g. $fulltext). Rename each field.`
412
+ }
413
+ );
414
+ }
415
+ }
416
+ function validateProjectionField(field, entityType, kind) {
417
+ const reserved = entityType === "node" ? RESERVED_NODE_KEYS : RESERVED_EDGE_KEYS;
418
+ if (reserved.has(field)) {
419
+ throw new ConfigurationError(
420
+ `Projection field "${field}" on ${entityType} kind "${kind}" conflicts with a reserved structural key`,
421
+ { field, kind, entityType, reservedKeys: [...reserved] },
422
+ {
423
+ suggestion: `Remove "${field}" from the projection. Structural fields (${[...reserved].join(", ")}) are included automatically when relevant.`
424
+ }
425
+ );
426
+ }
427
+ if (PROTOTYPE_POLLUTION_KEYS.has(field)) {
428
+ throw new ConfigurationError(
429
+ `Projection field "${field}" on ${entityType} kind "${kind}" is not allowed`,
430
+ { field, kind, entityType },
431
+ {
432
+ suggestion: `"${field}" cannot be used as a projection field name.`
433
+ }
434
+ );
435
+ }
436
+ }
437
+ function filterReservedKeys(props, reservedKeys) {
438
+ const filtered = {};
439
+ for (const [key, value] of Object.entries(props)) {
440
+ if (!reservedKeys.has(key)) {
441
+ filtered[key] = value;
442
+ }
443
+ }
444
+ return filtered;
445
+ }
446
+
447
+ // src/utils/result.ts
448
+ function ok(data) {
449
+ return { success: true, data };
450
+ }
451
+ function err(error) {
452
+ return { success: false, error };
453
+ }
454
+ function unwrap(result) {
455
+ if (result.success) {
456
+ return result.data;
457
+ }
458
+ throw result.error;
459
+ }
460
+
461
+ // src/graph-extension/validation.ts
462
+ var META_EDGE_NAME_SET = new Set(ALL_META_EDGE_NAMES);
463
+ var SUPPORTED_PROPERTY_TYPES = /* @__PURE__ */ new Set([
464
+ "string",
465
+ "number",
466
+ "boolean",
467
+ "enum",
468
+ "array",
469
+ "object"
470
+ ]);
471
+ var SUPPORTED_STRING_FORMATS = /* @__PURE__ */ new Set([
472
+ "datetime",
473
+ "uri",
474
+ "email",
475
+ "uuid",
476
+ "date"
477
+ ]);
478
+ var COMMON_PROPERTY_KEYS = /* @__PURE__ */ new Set([
479
+ "type",
480
+ "optional",
481
+ "description",
482
+ "searchable",
483
+ "embedding"
484
+ ]);
485
+ var PROPERTY_TYPE_KEYS = {
486
+ string: /* @__PURE__ */ new Set(["minLength", "maxLength", "pattern", "format"]),
487
+ number: /* @__PURE__ */ new Set(["min", "max", "int"]),
488
+ boolean: /* @__PURE__ */ new Set(),
489
+ enum: /* @__PURE__ */ new Set(["values"]),
490
+ array: /* @__PURE__ */ new Set(["items"]),
491
+ object: /* @__PURE__ */ new Set(["properties"])
492
+ };
493
+ function rejectUnknownPropertyKeys(raw, type, path, issues) {
494
+ const typeKeys = PROPERTY_TYPE_KEYS[type] ?? /* @__PURE__ */ new Set();
495
+ const recognized = [...COMMON_PROPERTY_KEYS, ...typeKeys];
496
+ for (const key of Object.keys(raw)) {
497
+ if (COMMON_PROPERTY_KEYS.has(key) || typeKeys.has(key)) continue;
498
+ issues.push({
499
+ path: `${path}/${encodeJsonPointerSegment(key)}`,
500
+ message: `Unknown property key "${key}" for type "${type}". Recognized keys: ${recognized.join(", ")}.`,
501
+ code: "UNKNOWN_PROPERTY_KEY"
502
+ });
503
+ }
504
+ }
505
+ function rejectUnknownObjectKeys(raw, allowed, label, path, code, issues) {
506
+ for (const key of Object.keys(raw)) {
507
+ if (allowed.has(key)) continue;
508
+ issues.push({
509
+ path: `${path}/${encodeJsonPointerSegment(key)}`,
510
+ message: `Unknown ${label} key "${key}". Allowed: ${[...allowed].join(", ")}.`,
511
+ code
512
+ });
513
+ }
514
+ }
515
+ var KNOWN_DOCUMENT_KEYS = new Set(
516
+ GRAPH_EXTENSION_TOP_LEVEL_KEYS
517
+ );
518
+ var NODE_BODY_KEYS = /* @__PURE__ */ new Set([
519
+ "description",
520
+ "annotations",
521
+ "properties",
522
+ "unique"
523
+ ]);
524
+ var EDGE_BODY_KEYS = /* @__PURE__ */ new Set([
525
+ "description",
526
+ "annotations",
527
+ "from",
528
+ "to",
529
+ "properties"
530
+ ]);
531
+ var ONTOLOGY_ENTRY_KEYS = /* @__PURE__ */ new Set([
532
+ "metaEdge",
533
+ "from",
534
+ "to"
535
+ ]);
536
+ var UNIQUE_CONSTRAINT_KEYS = /* @__PURE__ */ new Set([
537
+ "name",
538
+ "fields",
539
+ "scope",
540
+ "collation",
541
+ "where"
542
+ ]);
543
+ var WHERE_CLAUSE_KEYS = /* @__PURE__ */ new Set(["field", "op"]);
544
+ var NODE_INDEX_KEYS = /* @__PURE__ */ new Set([
545
+ "entity",
546
+ "kind",
547
+ "name",
548
+ "fields",
549
+ "coveringFields",
550
+ "unique",
551
+ "scope",
552
+ "where"
553
+ ]);
554
+ var EDGE_INDEX_KEYS = /* @__PURE__ */ new Set([
555
+ "entity",
556
+ "kind",
557
+ "name",
558
+ "fields",
559
+ "coveringFields",
560
+ "unique",
561
+ "scope",
562
+ "direction",
563
+ "where"
564
+ ]);
565
+ function validateGraphExtension(input, options = {}) {
566
+ const issues = [];
567
+ const strict = options.strict === true;
568
+ if (!isPlainObject(input)) {
569
+ issues.push({
570
+ path: "",
571
+ message: "Document must be a plain object.",
572
+ code: "INVALID_DOCUMENT_SHAPE"
573
+ });
574
+ return err(new GraphExtensionValidationError(issues));
575
+ }
576
+ const documentRecord = input;
577
+ if (strict) {
578
+ for (const key of Object.keys(documentRecord)) {
579
+ if (!KNOWN_DOCUMENT_KEYS.has(key)) {
580
+ issues.push({
581
+ path: `/${encodeJsonPointerSegment(key)}`,
582
+ message: `Unknown top-level key "${key}". Did you mean one of: ${[...KNOWN_DOCUMENT_KEYS].join(", ")}?`,
583
+ code: "UNKNOWN_DOCUMENT_KEY"
584
+ });
585
+ }
586
+ }
587
+ }
588
+ const version = validateVersion(documentRecord.version, issues);
589
+ const nodes = validateNodesSection(documentRecord.nodes, issues, strict);
590
+ const edges = validateEdgesSection(documentRecord.edges, issues, strict);
591
+ const ontology = validateOntologySection(
592
+ documentRecord.ontology,
593
+ issues,
594
+ strict
595
+ );
596
+ if (ontology !== void 0) {
597
+ validateOntology(ontology, nodes, issues);
598
+ }
599
+ const indexes = validateIndexesSection(
600
+ documentRecord.indexes,
601
+ issues,
602
+ strict
603
+ );
604
+ if (issues.length > 0) {
605
+ return err(new GraphExtensionValidationError(issues));
606
+ }
607
+ return ok(freezeDocument({ version, nodes, edges, ontology, indexes }));
608
+ }
609
+ function validateVersion(raw, issues) {
610
+ if (raw === void 0) return LEGACY_GRAPH_EXTENSION_VERSION;
611
+ if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 1) {
612
+ issues.push({
613
+ path: "/version",
614
+ message: `Document version must be a positive integer; received ${JSON.stringify(raw)}.`,
615
+ code: "INVALID_DOCUMENT_SHAPE"
616
+ });
617
+ return LEGACY_GRAPH_EXTENSION_VERSION;
618
+ }
619
+ if (raw > CURRENT_GRAPH_EXTENSION_VERSION) {
620
+ throw new GraphExtensionVersionUnsupportedError(
621
+ raw,
622
+ CURRENT_GRAPH_EXTENSION_VERSION
623
+ );
624
+ }
625
+ return raw;
626
+ }
627
+ function validateNodesSection(rawNodes, issues, strict) {
628
+ if (rawNodes === void 0) return void 0;
629
+ if (!isPlainObject(rawNodes)) {
630
+ issues.push({
631
+ path: "/nodes",
632
+ message: "`nodes` must be a plain object keyed by node-kind name.",
633
+ code: "INVALID_DOCUMENT_SHAPE"
634
+ });
635
+ return void 0;
636
+ }
637
+ const result = {};
638
+ const recorded = /* @__PURE__ */ new Set();
639
+ for (const [kindName, rawNode] of Object.entries(rawNodes)) {
640
+ const path = `/nodes/${encodeJsonPointerSegment(kindName)}`;
641
+ if (!isValidKindName(kindName)) {
642
+ issues.push({
643
+ path,
644
+ message: `Node kind name "${kindName}" must match /^[A-Za-z_][A-Za-z0-9_]*$/.`,
645
+ code: "INVALID_KIND_NAME"
646
+ });
647
+ continue;
648
+ }
649
+ if (recorded.has(kindName)) {
650
+ issues.push({
651
+ path,
652
+ message: `Duplicate node kind name "${kindName}".`,
653
+ code: "DUPLICATE_KIND_NAME"
654
+ });
655
+ continue;
656
+ }
657
+ const node = validateNodeDocument(kindName, rawNode, path, issues, strict);
658
+ if (node === void 0) continue;
659
+ result[kindName] = node;
660
+ recorded.add(kindName);
661
+ }
662
+ return result;
663
+ }
664
+ function validateNodeDocument(kindName, raw, path, issues, strict) {
665
+ if (!isPlainObject(raw)) {
666
+ issues.push({
667
+ path,
668
+ message: `Node "${kindName}" must be a plain object.`,
669
+ code: "INVALID_DOCUMENT_SHAPE"
670
+ });
671
+ return void 0;
672
+ }
673
+ if (strict) {
674
+ rejectUnknownObjectKeys(
675
+ raw,
676
+ NODE_BODY_KEYS,
677
+ "node-level",
678
+ path,
679
+ "INVALID_DOCUMENT_SHAPE",
680
+ issues
681
+ );
682
+ }
683
+ const description = validateOptionalString(
684
+ raw.description,
685
+ `${path}/description`,
686
+ "`description`",
687
+ "INVALID_DOCUMENT_SHAPE",
688
+ issues
689
+ );
690
+ const annotations = validateAnnotations(
691
+ raw.annotations,
692
+ `${path}/annotations`,
693
+ `Node "${kindName}"`,
694
+ issues
695
+ );
696
+ const propertiesRaw = raw.properties;
697
+ const properties = validatePropertiesMap(
698
+ propertiesRaw,
699
+ `${path}/properties`,
700
+ "node",
701
+ kindName,
702
+ issues,
703
+ strict
704
+ );
705
+ if (properties === void 0) return void 0;
706
+ const uniqueRaw = raw.unique;
707
+ const unique = validateUniqueConstraints(
708
+ uniqueRaw,
709
+ `${path}/unique`,
710
+ properties,
711
+ issues,
712
+ strict
713
+ );
714
+ return compactUndefined({
715
+ description,
716
+ annotations,
717
+ properties,
718
+ unique
719
+ });
720
+ }
721
+ function validateEdgesSection(rawEdges, issues, strict) {
722
+ if (rawEdges === void 0) return void 0;
723
+ if (!isPlainObject(rawEdges)) {
724
+ issues.push({
725
+ path: "/edges",
726
+ message: "`edges` must be a plain object keyed by edge-kind name.",
727
+ code: "INVALID_DOCUMENT_SHAPE"
728
+ });
729
+ return void 0;
730
+ }
731
+ const result = {};
732
+ const recorded = /* @__PURE__ */ new Set();
733
+ for (const [kindName, rawEdge] of Object.entries(rawEdges)) {
734
+ const path = `/edges/${encodeJsonPointerSegment(kindName)}`;
735
+ if (!isValidKindName(kindName)) {
736
+ issues.push({
737
+ path,
738
+ message: `Edge kind name "${kindName}" must match /^[A-Za-z_][A-Za-z0-9_]*$/.`,
739
+ code: "INVALID_KIND_NAME"
740
+ });
741
+ continue;
742
+ }
743
+ if (recorded.has(kindName)) {
744
+ issues.push({
745
+ path,
746
+ message: `Duplicate edge kind name "${kindName}".`,
747
+ code: "DUPLICATE_KIND_NAME"
748
+ });
749
+ continue;
750
+ }
751
+ const edge = validateEdgeDocument(kindName, rawEdge, path, issues, strict);
752
+ if (edge === void 0) continue;
753
+ result[kindName] = edge;
754
+ recorded.add(kindName);
755
+ }
756
+ return result;
757
+ }
758
+ function validateEdgeDocument(kindName, raw, path, issues, strict) {
759
+ if (!isPlainObject(raw)) {
760
+ issues.push({
761
+ path,
762
+ message: `Edge "${kindName}" must be a plain object.`,
763
+ code: "INVALID_DOCUMENT_SHAPE"
764
+ });
765
+ return void 0;
766
+ }
767
+ if (strict) {
768
+ rejectUnknownObjectKeys(
769
+ raw,
770
+ EDGE_BODY_KEYS,
771
+ "edge-level",
772
+ path,
773
+ "INVALID_DOCUMENT_SHAPE",
774
+ issues
775
+ );
776
+ }
777
+ const description = validateOptionalString(
778
+ raw.description,
779
+ `${path}/description`,
780
+ "`description`",
781
+ "INVALID_DOCUMENT_SHAPE",
782
+ issues
783
+ );
784
+ const annotations = validateAnnotations(
785
+ raw.annotations,
786
+ `${path}/annotations`,
787
+ `Edge "${kindName}"`,
788
+ issues
789
+ );
790
+ const from = validateEndpointList(raw.from, `${path}/from`, issues);
791
+ const to = validateEndpointList(raw.to, `${path}/to`, issues);
792
+ if (from === void 0 || to === void 0) return void 0;
793
+ const propertiesRaw = raw.properties;
794
+ const properties = propertiesRaw === void 0 ? {} : validatePropertiesMap(
795
+ propertiesRaw,
796
+ `${path}/properties`,
797
+ "edge",
798
+ kindName,
799
+ issues,
800
+ strict
801
+ );
802
+ if (properties === void 0) return void 0;
803
+ return compactUndefined({
804
+ description,
805
+ annotations,
806
+ from,
807
+ to,
808
+ properties
809
+ });
810
+ }
811
+ function validateEndpointList(raw, path, issues) {
812
+ if (!Array.isArray(raw)) {
813
+ issues.push({
814
+ path,
815
+ message: "Edge endpoint list must be an array of node-kind names.",
816
+ code: "INVALID_DOCUMENT_SHAPE"
817
+ });
818
+ return void 0;
819
+ }
820
+ if (raw.length === 0) {
821
+ issues.push({
822
+ path,
823
+ message: "Edge endpoint list must contain at least one node-kind name.",
824
+ code: "EMPTY_FROM_OR_TO"
825
+ });
826
+ return void 0;
827
+ }
828
+ const names = [];
829
+ for (const [index, value] of raw.entries()) {
830
+ if (typeof value !== "string" || value.length === 0) {
831
+ issues.push({
832
+ path: `${path}/${index}`,
833
+ message: "Edge endpoint must be a non-empty string node-kind name.",
834
+ code: "INVALID_DOCUMENT_SHAPE"
835
+ });
836
+ return void 0;
837
+ }
838
+ names.push(value);
839
+ }
840
+ return names;
841
+ }
842
+ function validateOntologySection(raw, issues, strict) {
843
+ if (raw === void 0) return void 0;
844
+ if (!Array.isArray(raw)) {
845
+ issues.push({
846
+ path: "/ontology",
847
+ message: "`ontology` must be an array of relation objects.",
848
+ code: "INVALID_DOCUMENT_SHAPE"
849
+ });
850
+ return void 0;
851
+ }
852
+ const result = [];
853
+ for (const [index, entry] of raw.entries()) {
854
+ const path = `/ontology/${index}`;
855
+ if (!isPlainObject(entry)) {
856
+ issues.push({
857
+ path,
858
+ message: "Ontology entry must be a plain object.",
859
+ code: "INVALID_DOCUMENT_SHAPE"
860
+ });
861
+ continue;
862
+ }
863
+ if (strict) {
864
+ rejectUnknownObjectKeys(
865
+ entry,
866
+ ONTOLOGY_ENTRY_KEYS,
867
+ "ontology-entry",
868
+ path,
869
+ "INVALID_DOCUMENT_SHAPE",
870
+ issues
871
+ );
872
+ }
873
+ const metaEdge = entry.metaEdge;
874
+ const from = entry.from;
875
+ const to = entry.to;
876
+ if (typeof metaEdge !== "string" || !META_EDGE_NAME_SET.has(metaEdge)) {
877
+ issues.push({
878
+ path: `${path}/metaEdge`,
879
+ message: `Unknown meta-edge ${describeUnknownValue(metaEdge)}. Allowed: ${[...ALL_META_EDGE_NAMES].join(", ")}.`,
880
+ code: "UNKNOWN_META_EDGE"
881
+ });
882
+ continue;
883
+ }
884
+ if (typeof from !== "string" || from.length === 0) {
885
+ issues.push({
886
+ path: `${path}/from`,
887
+ message: "Ontology relation `from` must be a non-empty string.",
888
+ code: "INVALID_DOCUMENT_SHAPE"
889
+ });
890
+ continue;
891
+ }
892
+ if (typeof to !== "string" || to.length === 0) {
893
+ issues.push({
894
+ path: `${path}/to`,
895
+ message: "Ontology relation `to` must be a non-empty string.",
896
+ code: "INVALID_DOCUMENT_SHAPE"
897
+ });
898
+ continue;
899
+ }
900
+ result.push({ metaEdge, from, to });
901
+ }
902
+ return result;
903
+ }
904
+ function validateOntology(ontology, _nodes, issues) {
905
+ const seenKey = /* @__PURE__ */ new Set();
906
+ for (const [index, relation] of ontology.entries()) {
907
+ const path = `/ontology/${index}`;
908
+ if (relation.from === relation.to && isStrictlyHierarchicalMetaEdge(relation.metaEdge)) {
909
+ issues.push({
910
+ path,
911
+ message: `Hierarchical meta-edge "${relation.metaEdge}" cannot be a self-loop ("${relation.from}" \u2192 "${relation.to}").`,
912
+ code: "ONTOLOGY_SELF_LOOP"
913
+ });
914
+ }
915
+ const key = `${relation.metaEdge}::${relation.from}->${relation.to}`;
916
+ if (seenKey.has(key)) {
917
+ issues.push({
918
+ path,
919
+ message: `Duplicate ontology relation "${relation.metaEdge}" (${relation.from} \u2192 ${relation.to}).`,
920
+ code: "DUPLICATE_ONTOLOGY_RELATION"
921
+ });
922
+ continue;
923
+ }
924
+ seenKey.add(key);
925
+ }
926
+ detectHierarchicalCycles(ontology, issues);
927
+ detectDisjointHierarchyContradictions(ontology, issues);
928
+ }
929
+ var STRICTLY_HIERARCHICAL = /* @__PURE__ */ new Set([
930
+ "subClassOf",
931
+ "broader",
932
+ "narrower",
933
+ "partOf",
934
+ "hasPart"
935
+ ]);
936
+ function isStrictlyHierarchicalMetaEdge(name) {
937
+ return STRICTLY_HIERARCHICAL.has(name);
938
+ }
939
+ var HIERARCHICAL_NORMALIZATION = /* @__PURE__ */ new Map([
940
+ ["subClassOf", { canonical: "subClassOf", flip: false }],
941
+ ["broader", { canonical: "broader", flip: false }],
942
+ ["narrower", { canonical: "broader", flip: true }],
943
+ ["partOf", { canonical: "partOf", flip: false }],
944
+ ["hasPart", { canonical: "partOf", flip: true }]
945
+ ]);
946
+ function buildHierarchicalGroups(ontology, options) {
947
+ const groups = /* @__PURE__ */ new Map();
948
+ for (const [index, relation] of ontology.entries()) {
949
+ const normalization = HIERARCHICAL_NORMALIZATION.get(relation.metaEdge);
950
+ if (normalization === void 0) continue;
951
+ if (options.skipSelfLoops && relation.from === relation.to) continue;
952
+ const from = normalization.flip ? relation.to : relation.from;
953
+ const to = normalization.flip ? relation.from : relation.to;
954
+ const list = groups.get(normalization.canonical) ?? [];
955
+ list.push({ from, to, originalIndex: index });
956
+ groups.set(normalization.canonical, list);
957
+ }
958
+ return groups;
959
+ }
960
+ function detectHierarchicalCycles(ontology, issues) {
961
+ const groups = buildHierarchicalGroups(ontology, { skipSelfLoops: true });
962
+ for (const [name, edges] of groups) {
963
+ const closure = computeTransitiveClosure(
964
+ edges.map((edge) => [edge.from, edge.to])
965
+ );
966
+ const reportedNodes = /* @__PURE__ */ new Set();
967
+ for (const [from, reachable] of closure) {
968
+ if (!reachable.has(from) || reportedNodes.has(from)) continue;
969
+ reportedNodes.add(from);
970
+ const offendingEdge = edges.find((edge) => edge.from === from);
971
+ const path = offendingEdge === void 0 ? "/ontology" : `/ontology/${offendingEdge.originalIndex}`;
972
+ issues.push({
973
+ path,
974
+ message: `Cycle detected in "${name}" relations involving "${from}".`,
975
+ code: "ONTOLOGY_CYCLE"
976
+ });
977
+ }
978
+ }
979
+ }
980
+ function detectDisjointHierarchyContradictions(ontology, issues) {
981
+ const disjointPairs = /* @__PURE__ */ new Set();
982
+ const disjointPath = /* @__PURE__ */ new Map();
983
+ for (const [index, relation] of ontology.entries()) {
984
+ if (relation.metaEdge !== "disjointWith") continue;
985
+ disjointPairs.add(`${relation.from}|${relation.to}`);
986
+ disjointPairs.add(`${relation.to}|${relation.from}`);
987
+ disjointPath.set(`${relation.from}|${relation.to}`, `/ontology/${index}`);
988
+ disjointPath.set(`${relation.to}|${relation.from}`, `/ontology/${index}`);
989
+ }
990
+ if (disjointPairs.size === 0) return;
991
+ const groups = buildHierarchicalGroups(ontology, { skipSelfLoops: false });
992
+ const reported = /* @__PURE__ */ new Set();
993
+ for (const [name, edges] of groups) {
994
+ const closure = computeTransitiveClosure(
995
+ edges.map((edge) => [edge.from, edge.to])
996
+ );
997
+ for (const [from, reachable] of closure) {
998
+ for (const to of reachable) {
999
+ const pairKey = `${from}|${to}`;
1000
+ if (!disjointPairs.has(pairKey)) continue;
1001
+ if (reported.has(pairKey)) continue;
1002
+ reported.add(pairKey);
1003
+ issues.push({
1004
+ path: disjointPath.get(pairKey) ?? "/ontology",
1005
+ message: `Contradiction: "${from}" and "${to}" are declared disjointWith but also related by "${name}" (directly or transitively).`,
1006
+ code: "ONTOLOGY_DISJOINT_CONFLICT"
1007
+ });
1008
+ }
1009
+ }
1010
+ }
1011
+ }
1012
+ var INDEX_SCOPE_VALUES = ["graphAndKind", "graph", "none"];
1013
+ var EDGE_INDEX_DIRECTION_VALUES = ["out", "in", "none"];
1014
+ var RUNTIME_INDEX_WHERE_OPS = /* @__PURE__ */ new Set([
1015
+ "isNull",
1016
+ "isNotNull"
1017
+ ]);
1018
+ function validateIndexesSection(rawIndexes, issues, strict) {
1019
+ if (rawIndexes === void 0) return void 0;
1020
+ if (!Array.isArray(rawIndexes)) {
1021
+ issues.push({
1022
+ path: "/indexes",
1023
+ message: "`indexes` must be an array of index declarations.",
1024
+ code: "INVALID_DOCUMENT_SHAPE"
1025
+ });
1026
+ return void 0;
1027
+ }
1028
+ const result = [];
1029
+ const seenNames = /* @__PURE__ */ new Set();
1030
+ for (const [arrayIndex, entry] of rawIndexes.entries()) {
1031
+ const path = `/indexes/${arrayIndex}`;
1032
+ const validated = validateIndexEntry(entry, path, issues, strict);
1033
+ if (validated === void 0) continue;
1034
+ if (validated.name !== void 0) {
1035
+ if (seenNames.has(validated.name)) {
1036
+ issues.push({
1037
+ path: `${path}/name`,
1038
+ message: `Duplicate index name "${validated.name}".`,
1039
+ code: "DUPLICATE_INDEX_NAME"
1040
+ });
1041
+ continue;
1042
+ }
1043
+ seenNames.add(validated.name);
1044
+ }
1045
+ result.push(validated);
1046
+ }
1047
+ return result;
1048
+ }
1049
+ function validateIndexEntry(raw, path, issues, strict) {
1050
+ if (!isPlainObject(raw)) {
1051
+ issues.push({
1052
+ path,
1053
+ message: "Index entry must be a plain object.",
1054
+ code: "INVALID_INDEX_DECLARATION"
1055
+ });
1056
+ return void 0;
1057
+ }
1058
+ const entity = raw.entity;
1059
+ if (entity !== "node" && entity !== "edge") {
1060
+ issues.push({
1061
+ path: `${path}/entity`,
1062
+ message: `Index \`entity\` must be "node" or "edge"; received ${describeUnknownValue(entity)}.`,
1063
+ code: "INVALID_INDEX_DECLARATION"
1064
+ });
1065
+ return void 0;
1066
+ }
1067
+ if (strict) {
1068
+ rejectUnknownObjectKeys(
1069
+ raw,
1070
+ entity === "node" ? NODE_INDEX_KEYS : EDGE_INDEX_KEYS,
1071
+ `${entity}-index`,
1072
+ path,
1073
+ "INVALID_INDEX_DECLARATION",
1074
+ issues
1075
+ );
1076
+ }
1077
+ if (typeof raw.kind !== "string" || !isValidKindName(raw.kind)) {
1078
+ issues.push({
1079
+ path: `${path}/kind`,
1080
+ message: "Index `kind` must be a non-empty kind name matching /^[A-Za-z_][A-Za-z0-9_]*$/.",
1081
+ code: "INVALID_INDEX_DECLARATION"
1082
+ });
1083
+ return void 0;
1084
+ }
1085
+ const fields = validateStringList(raw.fields, `${path}/fields`, issues, {
1086
+ allowEmpty: false,
1087
+ label: "fields",
1088
+ emptyCode: "EMPTY_INDEX_FIELDS"
1089
+ });
1090
+ if (fields === void 0) return void 0;
1091
+ const coveringFields = validateStringList(
1092
+ raw.coveringFields,
1093
+ `${path}/coveringFields`,
1094
+ issues,
1095
+ { allowEmpty: true, label: "coveringFields" }
1096
+ );
1097
+ if (raw.unique !== void 0 && typeof raw.unique !== "boolean") {
1098
+ issues.push({
1099
+ path: `${path}/unique`,
1100
+ message: "Index `unique` must be a boolean.",
1101
+ code: "INVALID_INDEX_DECLARATION"
1102
+ });
1103
+ return void 0;
1104
+ }
1105
+ let name;
1106
+ if (raw.name !== void 0) {
1107
+ if (typeof raw.name !== "string" || raw.name.length === 0) {
1108
+ issues.push({
1109
+ path: `${path}/name`,
1110
+ message: "Index `name` must be a non-empty string.",
1111
+ code: "INVALID_INDEX_DECLARATION"
1112
+ });
1113
+ return void 0;
1114
+ }
1115
+ name = raw.name;
1116
+ }
1117
+ const scopeResult = validateOptionalLiteral(
1118
+ raw.scope,
1119
+ INDEX_SCOPE_VALUES,
1120
+ `${path}/scope`,
1121
+ "Index `scope`",
1122
+ "INVALID_INDEX_DECLARATION",
1123
+ issues
1124
+ );
1125
+ if (!scopeResult.ok) return void 0;
1126
+ const scope = scopeResult.value;
1127
+ const where = validateGraphExtensionIndexWhere(
1128
+ raw.where,
1129
+ `${path}/where`,
1130
+ issues,
1131
+ strict
1132
+ );
1133
+ if (entity === "node") {
1134
+ return compactUndefined({
1135
+ entity: "node",
1136
+ kind: raw.kind,
1137
+ name,
1138
+ fields,
1139
+ coveringFields,
1140
+ unique: raw.unique,
1141
+ scope,
1142
+ where
1143
+ });
1144
+ }
1145
+ const directionResult = validateOptionalLiteral(
1146
+ raw.direction,
1147
+ EDGE_INDEX_DIRECTION_VALUES,
1148
+ `${path}/direction`,
1149
+ "Edge index `direction`",
1150
+ "INVALID_INDEX_DECLARATION",
1151
+ issues
1152
+ );
1153
+ if (!directionResult.ok) return void 0;
1154
+ const direction = directionResult.value;
1155
+ return compactUndefined({
1156
+ entity: "edge",
1157
+ kind: raw.kind,
1158
+ name,
1159
+ direction,
1160
+ fields,
1161
+ coveringFields,
1162
+ unique: raw.unique,
1163
+ scope,
1164
+ where
1165
+ });
1166
+ }
1167
+ function validateStringList(raw, path, issues, options) {
1168
+ if (raw === void 0) {
1169
+ if (options.allowEmpty) return void 0;
1170
+ issues.push({
1171
+ path,
1172
+ message: `\`${options.label}\` is required and must contain at least one entry.`,
1173
+ code: options.emptyCode ?? "INVALID_INDEX_DECLARATION"
1174
+ });
1175
+ return void 0;
1176
+ }
1177
+ if (!Array.isArray(raw)) {
1178
+ issues.push({
1179
+ path,
1180
+ message: `\`${options.label}\` must be an array of strings.`,
1181
+ code: "INVALID_INDEX_DECLARATION"
1182
+ });
1183
+ return void 0;
1184
+ }
1185
+ const seen = /* @__PURE__ */ new Set();
1186
+ const result = [];
1187
+ for (const [index, value] of raw.entries()) {
1188
+ if (typeof value !== "string" || value.length === 0) {
1189
+ issues.push({
1190
+ path: `${path}/${index}`,
1191
+ message: `\`${options.label}[${index}]\` must be a non-empty string.`,
1192
+ code: "INVALID_INDEX_DECLARATION"
1193
+ });
1194
+ return void 0;
1195
+ }
1196
+ if (seen.has(value)) {
1197
+ issues.push({
1198
+ path: `${path}/${index}`,
1199
+ message: `Duplicate ${options.label} entry "${value}".`,
1200
+ code: "INVALID_INDEX_DECLARATION"
1201
+ });
1202
+ return void 0;
1203
+ }
1204
+ seen.add(value);
1205
+ result.push(value);
1206
+ }
1207
+ if (!options.allowEmpty && result.length === 0) {
1208
+ issues.push({
1209
+ path,
1210
+ message: `\`${options.label}\` must contain at least one entry.`,
1211
+ code: options.emptyCode ?? "INVALID_INDEX_DECLARATION"
1212
+ });
1213
+ return void 0;
1214
+ }
1215
+ return result;
1216
+ }
1217
+ function validateGraphExtensionIndexWhere(raw, path, issues, strict) {
1218
+ if (raw === void 0) return void 0;
1219
+ if (!isPlainObject(raw)) {
1220
+ issues.push({
1221
+ path,
1222
+ message: "Index `where` must be a plain object.",
1223
+ code: "INVALID_INDEX_DECLARATION"
1224
+ });
1225
+ return void 0;
1226
+ }
1227
+ if (strict) {
1228
+ rejectUnknownObjectKeys(
1229
+ raw,
1230
+ WHERE_CLAUSE_KEYS,
1231
+ "where-clause",
1232
+ path,
1233
+ "INVALID_INDEX_DECLARATION",
1234
+ issues
1235
+ );
1236
+ }
1237
+ if (typeof raw.field !== "string" || raw.field.length === 0) {
1238
+ issues.push({
1239
+ path: `${path}/field`,
1240
+ message: "`where.field` must be a non-empty string.",
1241
+ code: "INVALID_INDEX_DECLARATION"
1242
+ });
1243
+ return void 0;
1244
+ }
1245
+ if (typeof raw.op !== "string" || !RUNTIME_INDEX_WHERE_OPS.has(raw.op)) {
1246
+ issues.push({
1247
+ path: `${path}/op`,
1248
+ message: '`where.op` must be "isNull" or "isNotNull" in v1.',
1249
+ code: "INVALID_INDEX_DECLARATION"
1250
+ });
1251
+ return void 0;
1252
+ }
1253
+ return Object.freeze({
1254
+ field: raw.field,
1255
+ op: raw.op
1256
+ });
1257
+ }
1258
+ function validatePropertiesMap(raw, path, ownerType, ownerName, issues, strict) {
1259
+ if (!isPlainObject(raw)) {
1260
+ issues.push({
1261
+ path,
1262
+ message: `\`properties\` must be a plain object.`,
1263
+ code: "INVALID_DOCUMENT_SHAPE"
1264
+ });
1265
+ return void 0;
1266
+ }
1267
+ const reserved = ownerType === "node" ? RESERVED_NODE_KEYS : RESERVED_EDGE_KEYS;
1268
+ const result = {};
1269
+ for (const [propertyName, propertyValue] of Object.entries(raw)) {
1270
+ const propertyPath = `${path}/${encodeJsonPointerSegment(propertyName)}`;
1271
+ if (reserved.has(propertyName)) {
1272
+ issues.push({
1273
+ path: propertyPath,
1274
+ message: `Property name "${propertyName}" is reserved for ${ownerType} "${ownerName}".`,
1275
+ code: "RESERVED_PROPERTY_NAME"
1276
+ });
1277
+ continue;
1278
+ }
1279
+ if (propertyName.startsWith("$")) {
1280
+ issues.push({
1281
+ path: propertyPath,
1282
+ message: `Property name "${propertyName}" uses the reserved "$" prefix.`,
1283
+ code: "RESERVED_PROPERTY_NAME"
1284
+ });
1285
+ continue;
1286
+ }
1287
+ const validated = validateProperty(
1288
+ propertyValue,
1289
+ propertyPath,
1290
+ 0,
1291
+ issues,
1292
+ strict
1293
+ );
1294
+ if (validated === void 0) continue;
1295
+ result[propertyName] = validated;
1296
+ }
1297
+ return result;
1298
+ }
1299
+ function validateProperty(raw, path, depth, issues, strict) {
1300
+ if (!isPlainObject(raw)) {
1301
+ issues.push({
1302
+ path,
1303
+ message: "Property descriptor must be a plain object with a `type` field.",
1304
+ code: "INVALID_DOCUMENT_SHAPE"
1305
+ });
1306
+ return void 0;
1307
+ }
1308
+ const property = raw;
1309
+ const type = property.type;
1310
+ if (typeof type !== "string" || !SUPPORTED_PROPERTY_TYPES.has(type)) {
1311
+ issues.push({
1312
+ path: `${path}/type`,
1313
+ message: `Unsupported property type ${describeUnknownValue(type)}. Supported: ${[...SUPPORTED_PROPERTY_TYPES].join(", ")}.`,
1314
+ code: "UNSUPPORTED_PROPERTY_TYPE"
1315
+ });
1316
+ return void 0;
1317
+ }
1318
+ if (strict) rejectUnknownPropertyKeys(property, type, path, issues);
1319
+ switch (type) {
1320
+ case "string": {
1321
+ return validateStringProperty(property, path, issues, strict);
1322
+ }
1323
+ case "number": {
1324
+ return validateNumberProperty(property, path, issues);
1325
+ }
1326
+ case "boolean": {
1327
+ return validateBooleanProperty(property, path, issues);
1328
+ }
1329
+ case "enum": {
1330
+ return validateEnumProperty(property, path, issues);
1331
+ }
1332
+ case "array": {
1333
+ return validateArrayProperty(property, path, depth, issues, strict);
1334
+ }
1335
+ case "object": {
1336
+ return validateObjectProperty(property, path, depth, issues, strict);
1337
+ }
1338
+ }
1339
+ return void 0;
1340
+ }
1341
+ function validateStringProperty(raw, path, issues, strict) {
1342
+ const minLength = validateNonNegativeInteger(
1343
+ raw.minLength,
1344
+ `${path}/minLength`,
1345
+ "string.minLength",
1346
+ "INVALID_LENGTH_BOUNDS",
1347
+ issues
1348
+ );
1349
+ const maxLength = validateNonNegativeInteger(
1350
+ raw.maxLength,
1351
+ `${path}/maxLength`,
1352
+ "string.maxLength",
1353
+ "INVALID_LENGTH_BOUNDS",
1354
+ issues
1355
+ );
1356
+ if (minLength !== void 0 && maxLength !== void 0 && minLength > maxLength) {
1357
+ issues.push({
1358
+ path,
1359
+ message: `string.minLength (${minLength}) cannot exceed string.maxLength (${maxLength}).`,
1360
+ code: "INVALID_LENGTH_BOUNDS"
1361
+ });
1362
+ }
1363
+ let pattern;
1364
+ if (raw.pattern !== void 0) {
1365
+ if (typeof raw.pattern === "string") {
1366
+ try {
1367
+ new RegExp(raw.pattern);
1368
+ pattern = raw.pattern;
1369
+ } catch (error) {
1370
+ issues.push({
1371
+ path: `${path}/pattern`,
1372
+ message: `Pattern is not a valid regular expression: ${error instanceof Error ? error.message : String(error)}`,
1373
+ code: "INVALID_PATTERN"
1374
+ });
1375
+ }
1376
+ } else {
1377
+ issues.push({
1378
+ path: `${path}/pattern`,
1379
+ message: "`pattern` must be a string regular expression.",
1380
+ code: "INVALID_PATTERN"
1381
+ });
1382
+ }
1383
+ }
1384
+ let format;
1385
+ if (raw.format !== void 0) {
1386
+ if (typeof raw.format !== "string") {
1387
+ issues.push({
1388
+ path: `${path}/format`,
1389
+ message: `String format must be a string; received ${describeUnknownValue(raw.format)}.`,
1390
+ code: "INVALID_PROPERTY_REFINEMENT"
1391
+ });
1392
+ } else if (SUPPORTED_STRING_FORMATS.has(raw.format)) {
1393
+ format = raw.format;
1394
+ } else if (strict) {
1395
+ issues.push({
1396
+ path: `${path}/format`,
1397
+ message: `Unsupported string format "${raw.format}". Supported: ${[...SUPPORTED_STRING_FORMATS].join(", ")}.`,
1398
+ code: "UNSUPPORTED_STRING_FORMAT"
1399
+ });
1400
+ }
1401
+ }
1402
+ const modifiers = validatePropertyModifiers(raw, path, "string", issues);
1403
+ if (modifiers === void 0) return void 0;
1404
+ if (format !== void 0 && modifiers.searchable !== void 0) {
1405
+ issues.push({
1406
+ path,
1407
+ message: "`searchable` cannot be combined with `format` on a string property \u2014 the format-specific schema cannot carry the searchable brand. Drop one, or split into two fields.",
1408
+ code: "INVALID_PROPERTY_REFINEMENT"
1409
+ });
1410
+ return void 0;
1411
+ }
1412
+ if (format !== void 0 && (minLength !== void 0 || maxLength !== void 0 || pattern !== void 0)) {
1413
+ issues.push({
1414
+ path,
1415
+ message: "`format` cannot be combined with `minLength`, `maxLength`, or `pattern` on a string property \u2014 the format-routed schema (`z.iso.datetime` / `z.url`) is not a `z.ZodString` and cannot carry length or pattern refinements. Drop the refinement, or drop the format and validate the shape with `pattern`.",
1416
+ code: "INVALID_PROPERTY_REFINEMENT"
1417
+ });
1418
+ return void 0;
1419
+ }
1420
+ return finalizeProperty(
1421
+ "string",
1422
+ { minLength, maxLength, pattern, format },
1423
+ modifiers
1424
+ );
1425
+ }
1426
+ function validateNumberProperty(raw, path, issues) {
1427
+ const min = validateOptionalFiniteNumber(
1428
+ raw.min,
1429
+ `${path}/min`,
1430
+ "`min`",
1431
+ "INVALID_NUMBER_BOUNDS",
1432
+ issues
1433
+ );
1434
+ const max = validateOptionalFiniteNumber(
1435
+ raw.max,
1436
+ `${path}/max`,
1437
+ "`max`",
1438
+ "INVALID_NUMBER_BOUNDS",
1439
+ issues
1440
+ );
1441
+ if (min !== void 0 && max !== void 0 && min > max) {
1442
+ issues.push({
1443
+ path,
1444
+ message: `number.min (${min}) cannot exceed number.max (${max}).`,
1445
+ code: "INVALID_NUMBER_BOUNDS"
1446
+ });
1447
+ }
1448
+ const int = validateOptionalBoolean(
1449
+ raw.int,
1450
+ `${path}/int`,
1451
+ "`int`",
1452
+ "INVALID_PROPERTY_REFINEMENT",
1453
+ issues
1454
+ );
1455
+ if (int === true) {
1456
+ if (min !== void 0 && !Number.isInteger(min)) {
1457
+ issues.push({
1458
+ path: `${path}/min`,
1459
+ message: "`min` must be an integer when `int: true`.",
1460
+ code: "INVALID_NUMBER_BOUNDS"
1461
+ });
1462
+ }
1463
+ if (max !== void 0 && !Number.isInteger(max)) {
1464
+ issues.push({
1465
+ path: `${path}/max`,
1466
+ message: "`max` must be an integer when `int: true`.",
1467
+ code: "INVALID_NUMBER_BOUNDS"
1468
+ });
1469
+ }
1470
+ }
1471
+ const modifiers = validatePropertyModifiers(raw, path, "number", issues);
1472
+ if (modifiers === void 0) return void 0;
1473
+ return finalizeProperty(
1474
+ "number",
1475
+ { min, max, int },
1476
+ modifiers
1477
+ );
1478
+ }
1479
+ function validateBooleanProperty(raw, path, issues) {
1480
+ const modifiers = validatePropertyModifiers(raw, path, "boolean", issues);
1481
+ if (modifiers === void 0) return void 0;
1482
+ return finalizeProperty("boolean", {}, modifiers);
1483
+ }
1484
+ function validateEnumProperty(raw, path, issues) {
1485
+ const valuesRaw = raw.values;
1486
+ if (!Array.isArray(valuesRaw) || valuesRaw.length === 0) {
1487
+ issues.push({
1488
+ path: `${path}/values`,
1489
+ message: "`enum.values` must be a non-empty array of strings.",
1490
+ code: "INVALID_ENUM_VALUES"
1491
+ });
1492
+ return void 0;
1493
+ }
1494
+ const values = [];
1495
+ const seen = /* @__PURE__ */ new Set();
1496
+ for (const [index, value] of valuesRaw.entries()) {
1497
+ if (typeof value !== "string") {
1498
+ issues.push({
1499
+ path: `${path}/values/${index}`,
1500
+ message: "`enum.values` entries must be strings.",
1501
+ code: "INVALID_ENUM_VALUES"
1502
+ });
1503
+ return void 0;
1504
+ }
1505
+ if (seen.has(value)) {
1506
+ issues.push({
1507
+ path: `${path}/values/${index}`,
1508
+ message: `Duplicate enum value "${value}".`,
1509
+ code: "INVALID_ENUM_VALUES"
1510
+ });
1511
+ return void 0;
1512
+ }
1513
+ seen.add(value);
1514
+ values.push(value);
1515
+ }
1516
+ const modifiers = validatePropertyModifiers(raw, path, "enum", issues);
1517
+ if (modifiers === void 0) return void 0;
1518
+ return finalizeProperty("enum", { values }, modifiers);
1519
+ }
1520
+ function validateArrayProperty(raw, path, depth, issues, strict) {
1521
+ const itemsRaw = raw.items;
1522
+ if (itemsRaw === void 0) {
1523
+ issues.push({
1524
+ path: `${path}/items`,
1525
+ message: "`array.items` is required.",
1526
+ code: "INVALID_PROPERTY_REFINEMENT"
1527
+ });
1528
+ return void 0;
1529
+ }
1530
+ if (!isPlainObject(itemsRaw)) {
1531
+ issues.push({
1532
+ path: `${path}/items`,
1533
+ message: "`array.items` must be a plain property descriptor.",
1534
+ code: "INVALID_DOCUMENT_SHAPE"
1535
+ });
1536
+ return void 0;
1537
+ }
1538
+ const itemType = itemsRaw.type;
1539
+ if (itemType === "array") {
1540
+ issues.push({
1541
+ path: `${path}/items`,
1542
+ message: "Nested arrays are not supported in v1.",
1543
+ code: "NESTED_ARRAY"
1544
+ });
1545
+ return void 0;
1546
+ }
1547
+ const items = validateProperty(
1548
+ itemsRaw,
1549
+ `${path}/items`,
1550
+ depth + 1,
1551
+ issues,
1552
+ strict
1553
+ );
1554
+ if (items === void 0) return void 0;
1555
+ if (items.type === "array") {
1556
+ return void 0;
1557
+ }
1558
+ const modifiers = validatePropertyModifiers(raw, path, "array", issues);
1559
+ if (modifiers === void 0) return void 0;
1560
+ return finalizeProperty(
1561
+ "array",
1562
+ { items },
1563
+ modifiers
1564
+ );
1565
+ }
1566
+ function validateObjectProperty(raw, path, depth, issues, strict) {
1567
+ if (depth >= 1) {
1568
+ issues.push({
1569
+ path,
1570
+ message: "Nested objects are limited to a single level in v1.",
1571
+ code: "NESTED_OBJECT_TOO_DEEP"
1572
+ });
1573
+ return void 0;
1574
+ }
1575
+ const propertiesRaw = raw.properties;
1576
+ if (!isPlainObject(propertiesRaw)) {
1577
+ issues.push({
1578
+ path: `${path}/properties`,
1579
+ message: "`object.properties` must be a plain object.",
1580
+ code: "INVALID_DOCUMENT_SHAPE"
1581
+ });
1582
+ return void 0;
1583
+ }
1584
+ const propertiesEntries = Object.entries(propertiesRaw);
1585
+ if (propertiesEntries.length === 0) {
1586
+ issues.push({
1587
+ path: `${path}/properties`,
1588
+ message: "`object.properties` must declare at least one field.",
1589
+ code: "EMPTY_PROPERTIES"
1590
+ });
1591
+ return void 0;
1592
+ }
1593
+ const fields = {};
1594
+ for (const [name, value] of propertiesEntries) {
1595
+ const fieldPath = `${path}/properties/${encodeJsonPointerSegment(name)}`;
1596
+ if (!isPlainObject(value)) {
1597
+ issues.push({
1598
+ path: fieldPath,
1599
+ message: "Object field descriptor must be a plain object.",
1600
+ code: "INVALID_DOCUMENT_SHAPE"
1601
+ });
1602
+ continue;
1603
+ }
1604
+ const fieldType = value.type;
1605
+ if (fieldType === "object") {
1606
+ issues.push({
1607
+ path: fieldPath,
1608
+ message: "Nested objects are limited to a single level in v1.",
1609
+ code: "NESTED_OBJECT_TOO_DEEP"
1610
+ });
1611
+ continue;
1612
+ }
1613
+ const field = validateProperty(value, fieldPath, depth + 1, issues, strict);
1614
+ if (field === void 0) continue;
1615
+ fields[name] = field;
1616
+ }
1617
+ const modifiers = validatePropertyModifiers(raw, path, "object", issues);
1618
+ if (modifiers === void 0) return void 0;
1619
+ return finalizeProperty(
1620
+ "object",
1621
+ { properties: fields },
1622
+ modifiers
1623
+ );
1624
+ }
1625
+ function finalizeProperty(type, specific, modifiers) {
1626
+ return compactUndefined({
1627
+ type,
1628
+ ...specific,
1629
+ optional: modifiers.optional,
1630
+ searchable: modifiers.searchable,
1631
+ embedding: modifiers.embedding,
1632
+ description: modifiers.description
1633
+ });
1634
+ }
1635
+ function validatePropertyModifiers(raw, path, propertyType, issues) {
1636
+ const issuesAtEntry = issues.length;
1637
+ const optional = validateOptionalBoolean(
1638
+ raw.optional,
1639
+ `${path}/optional`,
1640
+ "`optional`",
1641
+ "INVALID_PROPERTY_REFINEMENT",
1642
+ issues
1643
+ );
1644
+ let searchable;
1645
+ if (raw.searchable !== void 0) {
1646
+ if (propertyType !== "string") {
1647
+ issues.push({
1648
+ path: `${path}/searchable`,
1649
+ message: `\`searchable\` is only valid on string properties (got "${propertyType}").`,
1650
+ code: "INVALID_MODIFIER_TARGET"
1651
+ });
1652
+ } else if (isPlainObject(raw.searchable)) {
1653
+ const searchableRaw = raw.searchable;
1654
+ const language = searchableRaw.language;
1655
+ if (language === void 0) {
1656
+ searchable = {};
1657
+ } else {
1658
+ if (typeof language !== "string" || language.length === 0) {
1659
+ issues.push({
1660
+ path: `${path}/searchable/language`,
1661
+ message: "`searchable.language` must be a non-empty string.",
1662
+ code: "INVALID_SEARCHABLE_LANGUAGE"
1663
+ });
1664
+ } else {
1665
+ searchable = { language };
1666
+ }
1667
+ }
1668
+ } else {
1669
+ issues.push({
1670
+ path: `${path}/searchable`,
1671
+ message: "`searchable` must be a plain object (use `{}` for defaults).",
1672
+ code: "INVALID_PROPERTY_REFINEMENT"
1673
+ });
1674
+ }
1675
+ }
1676
+ let embedding;
1677
+ if (raw.embedding !== void 0) {
1678
+ if (propertyType === "array") {
1679
+ const itemsRaw = raw.items;
1680
+ const itemType = isPlainObject(itemsRaw) ? itemsRaw.type : void 0;
1681
+ if (itemType !== "number") {
1682
+ issues.push({
1683
+ path: `${path}/embedding`,
1684
+ message: '`embedding` requires `array.items.type === "number"`.',
1685
+ code: "INVALID_MODIFIER_TARGET"
1686
+ });
1687
+ } else if (isPlainObject(raw.embedding)) {
1688
+ const dim = raw.embedding.dimensions;
1689
+ if (typeof dim !== "number" || !Number.isInteger(dim) || dim <= 0) {
1690
+ issues.push({
1691
+ path: `${path}/embedding/dimensions`,
1692
+ message: "`embedding.dimensions` must be a positive integer.",
1693
+ code: "INVALID_EMBEDDING_DIMENSIONS"
1694
+ });
1695
+ } else {
1696
+ const itemsObject = isPlainObject(itemsRaw) ? itemsRaw : {};
1697
+ const extraneous = Object.keys(itemsObject).filter(
1698
+ (key) => key !== "type"
1699
+ );
1700
+ if (extraneous.length > 0) {
1701
+ issues.push({
1702
+ path: `${path}/items`,
1703
+ message: `\`embedding\` arrays must declare items as \`{ type: "number" }\` only \u2014 additional refinements (${extraneous.join(", ")}) are silently dropped by the embedding schema. Drop the refinements, or drop \`embedding\` and use a plain array.`,
1704
+ code: "INVALID_PROPERTY_REFINEMENT"
1705
+ });
1706
+ } else {
1707
+ embedding = { dimensions: dim };
1708
+ }
1709
+ }
1710
+ } else {
1711
+ issues.push({
1712
+ path: `${path}/embedding`,
1713
+ message: "`embedding` must be a plain object with a `dimensions` field.",
1714
+ code: "INVALID_PROPERTY_REFINEMENT"
1715
+ });
1716
+ }
1717
+ } else {
1718
+ issues.push({
1719
+ path: `${path}/embedding`,
1720
+ message: `\`embedding\` is only valid on array-of-number properties (got "${propertyType}").`,
1721
+ code: "INVALID_MODIFIER_TARGET"
1722
+ });
1723
+ }
1724
+ }
1725
+ let description;
1726
+ if (raw.description !== void 0) {
1727
+ if (typeof raw.description === "string") {
1728
+ description = raw.description;
1729
+ } else {
1730
+ issues.push({
1731
+ path: `${path}/description`,
1732
+ message: "`description` must be a string.",
1733
+ code: "INVALID_PROPERTY_REFINEMENT"
1734
+ });
1735
+ }
1736
+ }
1737
+ if (issues.length > issuesAtEntry) return void 0;
1738
+ return compactUndefined({
1739
+ optional,
1740
+ searchable,
1741
+ embedding,
1742
+ description
1743
+ });
1744
+ }
1745
+ function validateUniqueConstraints(raw, path, properties, issues, strict) {
1746
+ if (raw === void 0) return void 0;
1747
+ if (!Array.isArray(raw)) {
1748
+ issues.push({
1749
+ path,
1750
+ message: "`unique` must be an array of constraint declarations.",
1751
+ code: "INVALID_DOCUMENT_SHAPE"
1752
+ });
1753
+ return void 0;
1754
+ }
1755
+ const result = [];
1756
+ const names = /* @__PURE__ */ new Set();
1757
+ for (const [index, entry] of raw.entries()) {
1758
+ const constraintPath = `${path}/${index}`;
1759
+ if (!isPlainObject(entry)) {
1760
+ issues.push({
1761
+ path: constraintPath,
1762
+ message: "Unique constraint must be a plain object.",
1763
+ code: "INVALID_DOCUMENT_SHAPE"
1764
+ });
1765
+ continue;
1766
+ }
1767
+ if (strict) {
1768
+ rejectUnknownObjectKeys(
1769
+ entry,
1770
+ UNIQUE_CONSTRAINT_KEYS,
1771
+ "unique-constraint",
1772
+ constraintPath,
1773
+ "INVALID_DOCUMENT_SHAPE",
1774
+ issues
1775
+ );
1776
+ }
1777
+ const constraint = entry;
1778
+ const name = constraint.name;
1779
+ if (typeof name !== "string" || name.length === 0) {
1780
+ issues.push({
1781
+ path: `${constraintPath}/name`,
1782
+ message: "Unique constraint `name` must be a non-empty string.",
1783
+ code: "INVALID_DOCUMENT_SHAPE"
1784
+ });
1785
+ continue;
1786
+ }
1787
+ if (names.has(name)) {
1788
+ issues.push({
1789
+ path: `${constraintPath}/name`,
1790
+ message: `Duplicate unique constraint name "${name}".`,
1791
+ code: "DUPLICATE_UNIQUE_CONSTRAINT"
1792
+ });
1793
+ continue;
1794
+ }
1795
+ names.add(name);
1796
+ const fieldsRaw = constraint.fields;
1797
+ if (!Array.isArray(fieldsRaw) || fieldsRaw.length === 0) {
1798
+ issues.push({
1799
+ path: `${constraintPath}/fields`,
1800
+ message: "Unique constraint `fields` must be a non-empty array of property names.",
1801
+ code: "EMPTY_UNIQUE_FIELDS"
1802
+ });
1803
+ continue;
1804
+ }
1805
+ const fields = [];
1806
+ const seenFields = /* @__PURE__ */ new Set();
1807
+ let fieldsValid = true;
1808
+ for (const [fieldIndex, field] of fieldsRaw.entries()) {
1809
+ if (typeof field !== "string" || field.length === 0) {
1810
+ issues.push({
1811
+ path: `${constraintPath}/fields/${fieldIndex}`,
1812
+ message: "Unique constraint field must be a non-empty string.",
1813
+ code: "INVALID_DOCUMENT_SHAPE"
1814
+ });
1815
+ fieldsValid = false;
1816
+ break;
1817
+ }
1818
+ if (seenFields.has(field)) {
1819
+ issues.push({
1820
+ path: `${constraintPath}/fields/${fieldIndex}`,
1821
+ message: `Duplicate field "${field}" in unique constraint.`,
1822
+ code: "DUPLICATE_UNIQUE_FIELD"
1823
+ });
1824
+ fieldsValid = false;
1825
+ break;
1826
+ }
1827
+ if (!(field in properties)) {
1828
+ issues.push({
1829
+ path: `${constraintPath}/fields/${fieldIndex}`,
1830
+ message: `Unique constraint field "${field}" is not declared on this kind.`,
1831
+ code: "UNKNOWN_UNIQUE_FIELD"
1832
+ });
1833
+ fieldsValid = false;
1834
+ break;
1835
+ }
1836
+ seenFields.add(field);
1837
+ fields.push(field);
1838
+ }
1839
+ if (!fieldsValid) continue;
1840
+ const scopeResult = validateOptionalLiteral(
1841
+ constraint.scope,
1842
+ ["kind", "kindWithSubClasses"],
1843
+ `${constraintPath}/scope`,
1844
+ "Unique constraint `scope`",
1845
+ "INVALID_DOCUMENT_SHAPE",
1846
+ issues
1847
+ );
1848
+ if (!scopeResult.ok) continue;
1849
+ const scope = scopeResult.value;
1850
+ const collationResult = validateOptionalLiteral(
1851
+ constraint.collation,
1852
+ ["binary", "caseInsensitive"],
1853
+ `${constraintPath}/collation`,
1854
+ "Unique constraint `collation`",
1855
+ "INVALID_DOCUMENT_SHAPE",
1856
+ issues
1857
+ );
1858
+ if (!collationResult.ok) continue;
1859
+ const collation = collationResult.value;
1860
+ const where = constraint.where === void 0 ? void 0 : validateUniqueWhere(
1861
+ constraint.where,
1862
+ `${constraintPath}/where`,
1863
+ properties,
1864
+ issues,
1865
+ strict
1866
+ );
1867
+ if (constraint.where !== void 0 && where === void 0) continue;
1868
+ result.push(
1869
+ compactUndefined({
1870
+ name,
1871
+ fields,
1872
+ scope,
1873
+ collation,
1874
+ where
1875
+ })
1876
+ );
1877
+ }
1878
+ return result;
1879
+ }
1880
+ function validateUniqueWhere(raw, path, properties, issues, strict) {
1881
+ if (!isPlainObject(raw)) {
1882
+ issues.push({
1883
+ path,
1884
+ message: "Unique constraint `where` must be `{ field, op }`.",
1885
+ code: "INVALID_DOCUMENT_SHAPE"
1886
+ });
1887
+ return void 0;
1888
+ }
1889
+ if (strict) {
1890
+ rejectUnknownObjectKeys(
1891
+ raw,
1892
+ WHERE_CLAUSE_KEYS,
1893
+ "where-clause",
1894
+ path,
1895
+ "INVALID_DOCUMENT_SHAPE",
1896
+ issues
1897
+ );
1898
+ }
1899
+ const field = raw.field;
1900
+ const op = raw.op;
1901
+ if (typeof field !== "string" || field.length === 0) {
1902
+ issues.push({
1903
+ path: `${path}/field`,
1904
+ message: "Unique `where.field` must be a non-empty string.",
1905
+ code: "INVALID_DOCUMENT_SHAPE"
1906
+ });
1907
+ return void 0;
1908
+ }
1909
+ if (!(field in properties)) {
1910
+ issues.push({
1911
+ path: `${path}/field`,
1912
+ message: `Unique \`where.field\` "${field}" is not declared on this kind.`,
1913
+ code: "UNKNOWN_UNIQUE_WHERE_FIELD"
1914
+ });
1915
+ return void 0;
1916
+ }
1917
+ if (op !== "isNull" && op !== "isNotNull") {
1918
+ issues.push({
1919
+ path: `${path}/op`,
1920
+ message: `Unique \`where.op\` must be "isNull" or "isNotNull" (got ${describeUnknownValue(op)}).`,
1921
+ code: "INVALID_UNIQUE_WHERE_OP"
1922
+ });
1923
+ return void 0;
1924
+ }
1925
+ return { field, op };
1926
+ }
1927
+ function validateAnnotations(raw, path, ownerLabel, issues) {
1928
+ if (raw === void 0) return void 0;
1929
+ if (!isPlainObject(raw)) {
1930
+ issues.push({
1931
+ path,
1932
+ message: `Annotations must be a plain JSON object.`,
1933
+ code: "INVALID_ANNOTATION"
1934
+ });
1935
+ return void 0;
1936
+ }
1937
+ try {
1938
+ assertJsonValue(raw, "annotations", ownerLabel);
1939
+ } catch (error) {
1940
+ if (error instanceof ConfigurationError) {
1941
+ const detailPath = error.details.path;
1942
+ const annotationPath = typeof detailPath === "string" ? `${path}/${detailPath.replace(/^annotations\.?/, "").replaceAll(".", "/")}` : path;
1943
+ issues.push({
1944
+ path: annotationPath,
1945
+ message: error.message,
1946
+ code: "INVALID_ANNOTATION"
1947
+ });
1948
+ return void 0;
1949
+ }
1950
+ throw error;
1951
+ }
1952
+ return raw;
1953
+ }
1954
+ function validateNonNegativeInteger(value, path, label, code, issues) {
1955
+ if (value === void 0) return void 0;
1956
+ if (typeof value !== "number" || !Number.isInteger(value) || value < 0) {
1957
+ issues.push({
1958
+ path,
1959
+ message: `${label} must be a non-negative integer.`,
1960
+ code
1961
+ });
1962
+ return void 0;
1963
+ }
1964
+ return value;
1965
+ }
1966
+ function validateOptionalString(value, path, label, code, issues) {
1967
+ if (value === void 0) return void 0;
1968
+ if (typeof value !== "string") {
1969
+ issues.push({ path, message: `${label} must be a string.`, code });
1970
+ return void 0;
1971
+ }
1972
+ return value;
1973
+ }
1974
+ function validateOptionalBoolean(value, path, label, code, issues) {
1975
+ if (value === void 0) return void 0;
1976
+ if (typeof value !== "boolean") {
1977
+ issues.push({ path, message: `${label} must be a boolean.`, code });
1978
+ return void 0;
1979
+ }
1980
+ return value;
1981
+ }
1982
+ function validateOptionalFiniteNumber(value, path, label, code, issues) {
1983
+ if (value === void 0) return void 0;
1984
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1985
+ issues.push({ path, message: `${label} must be a finite number.`, code });
1986
+ return void 0;
1987
+ }
1988
+ return value;
1989
+ }
1990
+ function validateOptionalLiteral(value, allowed, path, label, code, issues) {
1991
+ if (value === void 0) return { ok: true, value: void 0 };
1992
+ if (typeof value !== "string" || !allowed.includes(value)) {
1993
+ const list = allowed.map((entry) => `"${entry}"`).join(", ");
1994
+ issues.push({ path, message: `${label} must be one of: ${list}.`, code });
1995
+ return { ok: false };
1996
+ }
1997
+ return { ok: true, value };
1998
+ }
1999
+ function isValidKindName(name) {
2000
+ return /^[A-Za-z_][A-Za-z0-9_]*$/.test(name);
2001
+ }
2002
+ function describeUnknownValue(value) {
2003
+ if (typeof value === "string") return `"${value}"`;
2004
+ if (value === null || typeof value === "number" || typeof value === "boolean") {
2005
+ return JSON.stringify(value);
2006
+ }
2007
+ try {
2008
+ return JSON.stringify(value);
2009
+ } catch {
2010
+ return `(${typeof value})`;
2011
+ }
2012
+ }
2013
+ function freezeDocument(input) {
2014
+ return Object.freeze(
2015
+ compactUndefined({
2016
+ version: input.version,
2017
+ nodes: input.nodes === void 0 ? void 0 : freezeDeep(input.nodes),
2018
+ edges: input.edges === void 0 ? void 0 : freezeDeep(input.edges),
2019
+ ontology: input.ontology === void 0 ? void 0 : Object.freeze(
2020
+ input.ontology.map((entry) => Object.freeze({ ...entry }))
2021
+ ),
2022
+ indexes: input.indexes === void 0 ? void 0 : Object.freeze(input.indexes.map((entry) => freezeDeep({ ...entry })))
2023
+ })
2024
+ );
2025
+ }
2026
+
2027
+ export { ALL_META_EDGE_NAMES, CURRENT_GRAPH_EXTENSION_VERSION, GRAPH_EXTENSION_ISSUE_CODES, GRAPH_EXTENSION_TOP_LEVEL_KEYS, GraphExtensionError, GraphExtensionUnresolvedEndpointError, GraphExtensionValidationError, GraphExtensionVersionUnsupportedError, INCOMPATIBLE_CHANGE_TYPES, IncompatibleChangeError, KindCollisionError, KindHasReferentsError, LEGACY_GRAPH_EXTENSION_VERSION, META_EDGE_BROADER, META_EDGE_DIFFERENT_FROM, META_EDGE_DISJOINT_WITH, META_EDGE_EQUIVALENT_TO, META_EDGE_HAS_PART, META_EDGE_IMPLIES, META_EDGE_INVERSE_OF, META_EDGE_NARROWER, META_EDGE_PART_OF, META_EDGE_RELATED_TO, META_EDGE_SAME_AS, META_EDGE_SUB_CLASS_OF, RESERVED_EDGE_KEYS, RESERVED_NODE_KEYS, RemoveCompileTimeKindError, assertJsonValue, assertSchemaKeysAreFree, compactUndefined, computeTransitiveClosure, filterReservedKeys, freezeDeep, invertClosure, isReachable, unwrap, validateGraphExtension, validateProjectionField };
2028
+ //# sourceMappingURL=chunk-4BOWVUGQ.js.map
2029
+ //# sourceMappingURL=chunk-4BOWVUGQ.js.map