@nicia-ai/typegraph 0.24.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backend/postgres/index.cjs +178 -55
- package/dist/backend/postgres/index.cjs.map +1 -1
- package/dist/backend/postgres/index.d.cts +6 -7
- package/dist/backend/postgres/index.d.ts +6 -7
- package/dist/backend/postgres/index.js +151 -28
- package/dist/backend/postgres/index.js.map +1 -1
- package/dist/backend/sqlite/index.cjs +19 -17
- package/dist/backend/sqlite/index.d.cts +6 -7
- package/dist/backend/sqlite/index.d.ts +6 -7
- package/dist/backend/sqlite/index.js +9 -7
- package/dist/backend/sqlite/libsql.cjs +12 -10
- package/dist/backend/sqlite/libsql.cjs.map +1 -1
- package/dist/backend/sqlite/libsql.d.cts +4 -5
- package/dist/backend/sqlite/libsql.d.ts +4 -5
- package/dist/backend/sqlite/libsql.js +9 -7
- package/dist/backend/sqlite/libsql.js.map +1 -1
- package/dist/backend/sqlite/local.cjs +14 -12
- package/dist/backend/sqlite/local.cjs.map +1 -1
- package/dist/backend/sqlite/local.d.cts +4 -5
- package/dist/backend/sqlite/local.d.ts +4 -5
- package/dist/backend/sqlite/local.js +9 -7
- package/dist/backend/sqlite/local.js.map +1 -1
- package/dist/chunk-4BOWVUGQ.js +2029 -0
- package/dist/chunk-4BOWVUGQ.js.map +1 -0
- package/dist/{chunk-SUHW2UYL.cjs → chunk-7CDXX3QZ.cjs} +2 -2
- package/dist/chunk-7CDXX3QZ.cjs.map +1 -0
- package/dist/chunk-7V335GHG.cjs +112 -0
- package/dist/chunk-7V335GHG.cjs.map +1 -0
- package/dist/chunk-7W3KAMVQ.cjs +2678 -0
- package/dist/chunk-7W3KAMVQ.cjs.map +1 -0
- package/dist/chunk-CTHNVU6K.cjs +551 -0
- package/dist/chunk-CTHNVU6K.cjs.map +1 -0
- package/dist/chunk-D2GO3RBL.cjs +12 -0
- package/dist/chunk-D2GO3RBL.cjs.map +1 -0
- package/dist/{chunk-WFMTAEEN.js → chunk-DOTEN2FG.js} +81 -7
- package/dist/chunk-DOTEN2FG.js.map +1 -0
- package/dist/chunk-E5JZSEGJ.cjs +301 -0
- package/dist/chunk-E5JZSEGJ.cjs.map +1 -0
- package/dist/chunk-EG6TJ3I5.js +286 -0
- package/dist/chunk-EG6TJ3I5.js.map +1 -0
- package/dist/chunk-EJCDOA37.js +537 -0
- package/dist/chunk-EJCDOA37.js.map +1 -0
- package/dist/{chunk-UVZHTBZ5.cjs → chunk-EK6RJ373.cjs} +7 -150
- package/dist/chunk-EK6RJ373.cjs.map +1 -0
- package/dist/{chunk-GUTQTMOV.js → chunk-FPCOUJSL.js} +2 -2
- package/dist/chunk-FPCOUJSL.js.map +1 -0
- package/dist/{chunk-3JK66E3I.cjs → chunk-JTI5WT4P.cjs} +630 -59
- package/dist/chunk-JTI5WT4P.cjs.map +1 -0
- package/dist/chunk-LVCSL6A3.js +2626 -0
- package/dist/chunk-LVCSL6A3.js.map +1 -0
- package/dist/chunk-NHQWXSTS.js +10 -0
- package/dist/chunk-NHQWXSTS.js.map +1 -0
- package/dist/chunk-NZ5PMBTG.js +105 -0
- package/dist/chunk-NZ5PMBTG.js.map +1 -0
- package/dist/{chunk-5HVDOPZU.cjs → chunk-O2P3KAUT.cjs} +416 -93
- package/dist/chunk-O2P3KAUT.cjs.map +1 -0
- package/dist/{chunk-QOLTW7OZ.js → chunk-OJ6IQVZG.js} +151 -13
- package/dist/chunk-OJ6IQVZG.js.map +1 -0
- package/dist/{chunk-EYELZHKY.js → chunk-OOS2RKRH.js} +5 -140
- package/dist/chunk-OOS2RKRH.js.map +1 -0
- package/dist/chunk-RCAJZG67.cjs +2069 -0
- package/dist/chunk-RCAJZG67.cjs.map +1 -0
- package/dist/{chunk-O5PST6TB.cjs → chunk-W5MTHMTE.cjs} +83 -6
- package/dist/chunk-W5MTHMTE.cjs.map +1 -0
- package/dist/{chunk-EY4LBX3F.js → chunk-WI5VRP3X.js} +337 -26
- package/dist/chunk-WI5VRP3X.js.map +1 -0
- package/dist/chunk-WVQXAFP3.js +79 -0
- package/dist/chunk-WVQXAFP3.js.map +1 -0
- package/dist/chunk-X3244EKP.cjs +84 -0
- package/dist/chunk-X3244EKP.cjs.map +1 -0
- package/dist/{chunk-753F33CV.js → chunk-YZZBDCLV.js} +617 -44
- package/dist/chunk-YZZBDCLV.js.map +1 -0
- package/dist/{chunk-OVJ2BE5Q.cjs → chunk-ZEMNF3QK.cjs} +171 -33
- package/dist/chunk-ZEMNF3QK.cjs.map +1 -0
- package/dist/{ddl-B_H8JM-E.d.cts → ddl-B2jjLjpR.d.cts} +762 -99
- package/dist/{ddl-1tFIvt9V.d.ts → ddl-DpaeAKPp.d.ts} +762 -99
- package/dist/extension-types-D07e_NTw.d.cts +543 -0
- package/dist/extension-types-D07e_NTw.d.ts +543 -0
- package/dist/graph-extension/index.cjs +71 -0
- package/dist/graph-extension/index.cjs.map +1 -0
- package/dist/graph-extension/index.d.cts +278 -0
- package/dist/graph-extension/index.d.ts +278 -0
- package/dist/graph-extension/index.js +6 -0
- package/dist/graph-extension/index.js.map +1 -0
- package/dist/{index-DXkEXpNz.d.ts → index-CT6nvtxL.d.cts} +114 -5
- package/dist/{index-DXkEXpNz.d.cts → index-DQheiVQV.d.ts} +114 -5
- package/dist/index.cjs +2672 -901
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +29 -10
- package/dist/index.d.ts +29 -10
- package/dist/index.js +2248 -598
- package/dist/index.js.map +1 -1
- package/dist/indexes/index.cjs +25 -23
- package/dist/indexes/index.d.cts +40 -22
- package/dist/indexes/index.d.ts +40 -22
- package/dist/indexes/index.js +5 -3
- package/dist/interchange/index.cjs +8 -7
- package/dist/interchange/index.cjs.map +1 -1
- package/dist/interchange/index.d.cts +5 -4
- package/dist/interchange/index.d.ts +5 -4
- package/dist/interchange/index.js +3 -2
- package/dist/interchange/index.js.map +1 -1
- package/dist/{manager-nIhmttD-.d.ts → manager-BKaPQbns.d.ts} +189 -6
- package/dist/{manager-Vkz28eAC.d.cts → manager-BpjBW1JK.d.cts} +189 -6
- package/dist/materialize-indexes-BB9N5uQl.d.cts +68 -0
- package/dist/materialize-indexes-BYpU2RKh.d.ts +68 -0
- package/dist/profiler/index.cjs +10 -8
- package/dist/profiler/index.cjs.map +1 -1
- package/dist/profiler/index.d.cts +8 -7
- package/dist/profiler/index.d.ts +8 -7
- package/dist/profiler/index.js +3 -1
- package/dist/profiler/index.js.map +1 -1
- package/dist/schema/index.cjs +43 -22
- package/dist/schema/index.cjs.map +1 -1
- package/dist/schema/index.d.cts +17 -8
- package/dist/schema/index.d.ts +17 -8
- package/dist/schema/index.js +9 -4
- package/dist/schema/index.js.map +1 -1
- package/dist/{sqlite-D0aceIe0.d.cts → sqlite-CU3j6rc7.d.ts} +837 -120
- package/dist/{sqlite-SUxbZqfy.d.ts → sqlite-Myo8e3Lr.d.cts} +837 -120
- package/dist/{store-U3i2tUch.d.ts → store-BAGuOHDh.d.cts} +659 -23
- package/dist/{store-CpvwTMxV.d.cts → store-De98Lod4.d.ts} +659 -23
- package/dist/types--0EslBx5.d.cts +2123 -0
- package/dist/{types-qYFZ9SqD.d.ts → types-BLmJq4wI.d.ts} +3 -2
- package/dist/types-BNWYZ-0-.d.ts +2123 -0
- package/dist/{types-yH4_p0XB.d.cts → types-CYga3lZp.d.cts} +3 -2
- package/dist/where-CluR73rs.d.ts +11 -0
- package/dist/where-m1uWsdua.d.cts +11 -0
- package/package.json +33 -73
- package/dist/ast-C8S37w1A.d.ts +0 -497
- package/dist/ast-dgLhLOg0.d.cts +0 -497
- package/dist/chunk-3JK66E3I.cjs.map +0 -1
- package/dist/chunk-3PPEHLGK.js +0 -1116
- package/dist/chunk-3PPEHLGK.js.map +0 -1
- package/dist/chunk-5HVDOPZU.cjs.map +0 -1
- package/dist/chunk-753F33CV.js.map +0 -1
- package/dist/chunk-EY4LBX3F.js.map +0 -1
- package/dist/chunk-EYELZHKY.js.map +0 -1
- package/dist/chunk-GFGVREHH.js +0 -1381
- package/dist/chunk-GFGVREHH.js.map +0 -1
- package/dist/chunk-GUTQTMOV.js.map +0 -1
- package/dist/chunk-LBORRUNJ.js +0 -135
- package/dist/chunk-LBORRUNJ.js.map +0 -1
- package/dist/chunk-O5PST6TB.cjs.map +0 -1
- package/dist/chunk-OLROXS2S.cjs +0 -147
- package/dist/chunk-OLROXS2S.cjs.map +0 -1
- package/dist/chunk-OVJ2BE5Q.cjs.map +0 -1
- package/dist/chunk-PAUN562Y.cjs +0 -1417
- package/dist/chunk-PAUN562Y.cjs.map +0 -1
- package/dist/chunk-PEE4EWXZ.cjs +0 -1131
- package/dist/chunk-PEE4EWXZ.cjs.map +0 -1
- package/dist/chunk-QOLTW7OZ.js.map +0 -1
- package/dist/chunk-SUHW2UYL.cjs.map +0 -1
- package/dist/chunk-UVZHTBZ5.cjs.map +0 -1
- package/dist/chunk-WFMTAEEN.js.map +0 -1
- package/dist/types-C-zfYDJB.d.ts +0 -159
- package/dist/types-CQx4iW5Y.d.cts +0 -159
- package/dist/types-Qk2l3IkQ.d.cts +0 -1254
- package/dist/types-Qk2l3IkQ.d.ts +0 -1254
package/dist/index.js
CHANGED
|
@@ -1,148 +1,28 @@
|
|
|
1
1
|
import { quoteIdentifier, EMPTY_REQUIRED_COLUMNS, addRequiredColumn, markSelectiveFieldAsRequired, markFieldRefAsRequired, NODE_COLUMNS, shouldProjectColumn, mapSelectiveSystemFieldToColumn, isIdFieldRef, isAggregateExpr, EDGE_COLUMNS } from './chunk-3EUKJEQ3.js';
|
|
2
|
-
import { META_EDGE_BRAND,
|
|
3
|
-
export { isMetaEdge } from './chunk-
|
|
4
|
-
import {
|
|
5
|
-
export { defineGraph, getEdgeKinds, getNodeKinds, isEdgeType, isEdgeTypeWithEndpoints, isGraphDef, isNodeType } from './chunk-
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
export {
|
|
9
|
-
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
2
|
+
import { META_EDGE_BRAND, loadAndMergeGraphExtensionDocument, ensureSchema, mergeGraphExtension, commitNewSchemaVersion, applyDeprecatedKinds, loadActiveSchemaWithBootstrap, parseSerializedSchema, computeClosuresFromOntology, createEmptyClosures, KindRegistry, extensionKindNames, serializeSchemaProperties, buildGraphExtensionOntologyKeySet, compileTimeOntologyKey, getTypeName, validateNodeProps, validateEdgeProps, canonicalEqual, sortedReplacer } from './chunk-LVCSL6A3.js';
|
|
3
|
+
export { broader, core, defineEdge, defineNode, differentFrom, disjointWith, equivalentTo, hasPart, implies, inverseOf, isMetaEdge, narrower, partOf, relatedTo, sameAs, subClassOf } from './chunk-LVCSL6A3.js';
|
|
4
|
+
import { nowIso, isKnownKind, validateOptionalIsoDate, getNodeKinds, getEdgeKinds } from './chunk-EG6TJ3I5.js';
|
|
5
|
+
export { defineGraph, getEdgeKinds, getNodeKinds, isEdgeType, isEdgeTypeWithEndpoints, isGraphDef, isNodeType } from './chunk-EG6TJ3I5.js';
|
|
6
|
+
export { defineGraphExtension } from './chunk-NHQWXSTS.js';
|
|
7
|
+
import { IncompatibleChangeError, filterReservedKeys, RESERVED_NODE_KEYS, RESERVED_EDGE_KEYS, RemoveCompileTimeKindError, KindHasReferentsError, validateProjectionField } from './chunk-4BOWVUGQ.js';
|
|
8
|
+
export { CURRENT_GRAPH_EXTENSION_VERSION, GraphExtensionError, GraphExtensionUnresolvedEndpointError, GraphExtensionValidationError, GraphExtensionVersionUnsupportedError, IncompatibleChangeError, KindCollisionError, KindHasReferentsError, LEGACY_GRAPH_EXTENSION_VERSION, RemoveCompileTimeKindError, validateGraphExtension } from './chunk-4BOWVUGQ.js';
|
|
9
|
+
import { runOptionallyInTransaction } from './chunk-FPCOUJSL.js';
|
|
10
|
+
import { getDialect, generateIndexDDL } from './chunk-YZZBDCLV.js';
|
|
11
|
+
export { andWhere, fts5Strategy, notWhere, orWhere, tsvectorStrategy } from './chunk-YZZBDCLV.js';
|
|
12
|
+
import { MAX_PG_IDENTIFIER_LENGTH, DEFAULT_PAGINATION_LIMIT, DEFAULT_STREAM_BATCH_SIZE, NODE_META_KEYS, EDGE_META_KEYS, SQLITE_PATH_DELIMITER, PG_ARRAY_START, PG_ARRAY_END, PG_PATH_ELEMENT_SEPARATOR, fnv1aBase36, sha256Hex } from './chunk-EJCDOA37.js';
|
|
13
|
+
export { defineEdgeIndex, defineNodeIndex } from './chunk-EJCDOA37.js';
|
|
14
|
+
import { createSchemaIntrospector, resolveFieldTypeInfoAtJsonPointer, getSearchableMetadata, DEFAULT_SEARCHABLE_LANGUAGE } from './chunk-OOS2RKRH.js';
|
|
15
|
+
export { DEFAULT_SEARCHABLE_LANGUAGE, getSearchableMetadata, isSearchableSchema, searchable } from './chunk-OOS2RKRH.js';
|
|
16
|
+
import { getEmbeddingDimensions, isEmbeddingSchema } from './chunk-WVQXAFP3.js';
|
|
17
|
+
export { embedding, getEmbeddingDimensions, isEmbeddingSchema } from './chunk-WVQXAFP3.js';
|
|
18
|
+
import { ConfigurationError, UnsupportedPredicateError, ValidationError, KindNotFoundError, EndpointError, EagerMaterializationError, CompilerInvariantError, DatabaseOperationError, EdgeNotFoundError, NodeConstraintNotFoundError, RestrictedDeleteError, NodeNotFoundError, EndpointNotFoundError, UniquenessError, CardinalityError, DisjointError } from './chunk-DOTEN2FG.js';
|
|
19
|
+
export { BackendDisposedError, CardinalityError, CompilerInvariantError, ConfigurationError, DatabaseOperationError, DisjointError, EagerMaterializationError, EdgeNotFoundError, EndpointError, EndpointNotFoundError, KindNotFoundError, MigrationError, NodeConstraintNotFoundError, NodeNotFoundError, RestrictedDeleteError, SchemaContentConflictError, SchemaMismatchError, StaleVersionError, TypeGraphError, UniquenessError, UnsupportedPredicateError, ValidationError, VersionConflictError, getErrorSuggestion, isConstraintError, isSystemError, isTypeGraphError, isUserRecoverable } from './chunk-DOTEN2FG.js';
|
|
20
|
+
import { jsonPointer, parseJsonPointer, normalizeJsonPointer, joinJsonPointers } from './chunk-NZ5PMBTG.js';
|
|
21
|
+
export { MAX_JSON_POINTER_DEPTH, joinJsonPointers, jsonPointer, normalizeJsonPointer, parseJsonPointer } from './chunk-NZ5PMBTG.js';
|
|
13
22
|
import { z } from 'zod';
|
|
14
23
|
import { sql, Placeholder } from 'drizzle-orm';
|
|
15
24
|
import { nanoid } from 'nanoid';
|
|
16
25
|
|
|
17
|
-
// src/store/reserved-keys.ts
|
|
18
|
-
var RESERVED_NODE_KEYS = /* @__PURE__ */ new Set([
|
|
19
|
-
"id",
|
|
20
|
-
"kind",
|
|
21
|
-
"meta"
|
|
22
|
-
]);
|
|
23
|
-
var RESERVED_EDGE_KEYS = /* @__PURE__ */ new Set([
|
|
24
|
-
"id",
|
|
25
|
-
"kind",
|
|
26
|
-
"meta",
|
|
27
|
-
"fromKind",
|
|
28
|
-
"fromId",
|
|
29
|
-
"toKind",
|
|
30
|
-
"toId"
|
|
31
|
-
]);
|
|
32
|
-
var PROTOTYPE_POLLUTION_KEYS = /* @__PURE__ */ new Set([
|
|
33
|
-
"__proto__",
|
|
34
|
-
"constructor",
|
|
35
|
-
"prototype"
|
|
36
|
-
]);
|
|
37
|
-
var RESERVED_PROPERTY_PREFIX = "$";
|
|
38
|
-
function isReservedPropertyName(name) {
|
|
39
|
-
return name.startsWith(RESERVED_PROPERTY_PREFIX);
|
|
40
|
-
}
|
|
41
|
-
function assertSchemaKeysAreFree(entityKind, name, keys, reservedStructuralKeys) {
|
|
42
|
-
const structuralConflicts = keys.filter(
|
|
43
|
-
(key) => reservedStructuralKeys.has(key)
|
|
44
|
-
);
|
|
45
|
-
if (structuralConflicts.length > 0) {
|
|
46
|
-
const label = entityKind.toLowerCase();
|
|
47
|
-
throw new ConfigurationError(
|
|
48
|
-
`${entityKind} "${name}" schema contains reserved property names: ${structuralConflicts.join(", ")}`,
|
|
49
|
-
{
|
|
50
|
-
[`${label}Type`]: name,
|
|
51
|
-
conflicts: structuralConflicts,
|
|
52
|
-
reservedKeys: [...reservedStructuralKeys]
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
suggestion: `Rename the conflicting properties. Reserved names (${[...reservedStructuralKeys].join(", ")}) are added automatically to all ${label}s.`
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
const prefixConflicts = keys.filter((key) => isReservedPropertyName(key));
|
|
60
|
-
if (prefixConflicts.length > 0) {
|
|
61
|
-
const label = entityKind.toLowerCase();
|
|
62
|
-
throw new ConfigurationError(
|
|
63
|
-
`${entityKind} "${name}" schema uses the reserved "${RESERVED_PROPERTY_PREFIX}" prefix on: ${prefixConflicts.join(", ")}`,
|
|
64
|
-
{
|
|
65
|
-
[`${label}Type`]: name,
|
|
66
|
-
conflicts: prefixConflicts,
|
|
67
|
-
reservedPrefix: RESERVED_PROPERTY_PREFIX
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
suggestion: `Property names starting with "${RESERVED_PROPERTY_PREFIX}" are reserved for TypeGraph accessors (e.g. $fulltext). Rename each field.`
|
|
71
|
-
}
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
function validateProjectionField(field2, entityType, kind) {
|
|
76
|
-
const reserved = entityType === "node" ? RESERVED_NODE_KEYS : RESERVED_EDGE_KEYS;
|
|
77
|
-
if (reserved.has(field2)) {
|
|
78
|
-
throw new ConfigurationError(
|
|
79
|
-
`Projection field "${field2}" on ${entityType} kind "${kind}" conflicts with a reserved structural key`,
|
|
80
|
-
{ field: field2, kind, entityType, reservedKeys: [...reserved] },
|
|
81
|
-
{
|
|
82
|
-
suggestion: `Remove "${field2}" from the projection. Structural fields (${[...reserved].join(", ")}) are included automatically when relevant.`
|
|
83
|
-
}
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
if (PROTOTYPE_POLLUTION_KEYS.has(field2)) {
|
|
87
|
-
throw new ConfigurationError(
|
|
88
|
-
`Projection field "${field2}" on ${entityType} kind "${kind}" is not allowed`,
|
|
89
|
-
{ field: field2, kind, entityType },
|
|
90
|
-
{
|
|
91
|
-
suggestion: `"${field2}" cannot be used as a projection field name.`
|
|
92
|
-
}
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
function filterReservedKeys(props, reservedKeys) {
|
|
97
|
-
const filtered = {};
|
|
98
|
-
for (const [key, value] of Object.entries(props)) {
|
|
99
|
-
if (!reservedKeys.has(key)) {
|
|
100
|
-
filtered[key] = value;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
return filtered;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// src/core/node.ts
|
|
107
|
-
function validateSchemaKeys(schema, name) {
|
|
108
|
-
assertSchemaKeysAreFree(
|
|
109
|
-
"Node",
|
|
110
|
-
name,
|
|
111
|
-
Object.keys(schema.shape),
|
|
112
|
-
RESERVED_NODE_KEYS
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
function defineNode(name, options) {
|
|
116
|
-
validateSchemaKeys(options.schema, name);
|
|
117
|
-
return Object.freeze({
|
|
118
|
-
[NODE_TYPE_BRAND]: true,
|
|
119
|
-
kind: name,
|
|
120
|
-
schema: options.schema,
|
|
121
|
-
description: options.description
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
var EMPTY_SCHEMA = z.object({});
|
|
125
|
-
function validateSchemaKeys2(schema, name) {
|
|
126
|
-
assertSchemaKeysAreFree(
|
|
127
|
-
"Edge",
|
|
128
|
-
name,
|
|
129
|
-
Object.keys(schema.shape),
|
|
130
|
-
RESERVED_EDGE_KEYS
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
function defineEdge(name, options) {
|
|
134
|
-
const schema = options?.schema ?? EMPTY_SCHEMA;
|
|
135
|
-
validateSchemaKeys2(schema, name);
|
|
136
|
-
return Object.freeze({
|
|
137
|
-
[EDGE_TYPE_BRAND]: true,
|
|
138
|
-
kind: name,
|
|
139
|
-
schema,
|
|
140
|
-
description: options?.description,
|
|
141
|
-
from: options?.from,
|
|
142
|
-
to: options?.to
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
|
|
146
26
|
// src/core/meta-edge.ts
|
|
147
27
|
function metaEdge(name, options = {}) {
|
|
148
28
|
const properties = {
|
|
@@ -187,200 +67,6 @@ function createExternalRef(table) {
|
|
|
187
67
|
return (id) => ({ table, id });
|
|
188
68
|
}
|
|
189
69
|
|
|
190
|
-
// src/ontology/core-meta-edges.ts
|
|
191
|
-
function createMetaEdge(name, properties) {
|
|
192
|
-
return Object.freeze({
|
|
193
|
-
[META_EDGE_BRAND]: true,
|
|
194
|
-
name,
|
|
195
|
-
properties: {
|
|
196
|
-
transitive: properties.transitive ?? false,
|
|
197
|
-
symmetric: properties.symmetric ?? false,
|
|
198
|
-
reflexive: properties.reflexive ?? false,
|
|
199
|
-
inverse: properties.inverse,
|
|
200
|
-
inference: properties.inference,
|
|
201
|
-
description: properties.description
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
var subClassOfMetaEdge = createMetaEdge(META_EDGE_SUB_CLASS_OF, {
|
|
206
|
-
transitive: true,
|
|
207
|
-
inference: "subsumption",
|
|
208
|
-
description: "Type inheritance (Podcast subClassOf Media)"
|
|
209
|
-
});
|
|
210
|
-
function subClassOf(child, parent) {
|
|
211
|
-
return {
|
|
212
|
-
metaEdge: subClassOfMetaEdge,
|
|
213
|
-
from: child,
|
|
214
|
-
to: parent
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
var broaderMetaEdge = createMetaEdge(META_EDGE_BROADER, {
|
|
218
|
-
transitive: true,
|
|
219
|
-
inverse: META_EDGE_NARROWER,
|
|
220
|
-
inference: "hierarchy",
|
|
221
|
-
description: "Broader concept (ML broader AI)"
|
|
222
|
-
});
|
|
223
|
-
function broader(narrowerConcept, broaderConcept) {
|
|
224
|
-
return {
|
|
225
|
-
metaEdge: broaderMetaEdge,
|
|
226
|
-
from: narrowerConcept,
|
|
227
|
-
to: broaderConcept
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
var narrowerMetaEdge = createMetaEdge(META_EDGE_NARROWER, {
|
|
231
|
-
transitive: true,
|
|
232
|
-
inverse: META_EDGE_BROADER,
|
|
233
|
-
inference: "hierarchy",
|
|
234
|
-
description: "Narrower concept (AI narrower ML)"
|
|
235
|
-
});
|
|
236
|
-
function narrower(broaderConcept, narrowerConcept) {
|
|
237
|
-
return {
|
|
238
|
-
metaEdge: narrowerMetaEdge,
|
|
239
|
-
from: broaderConcept,
|
|
240
|
-
to: narrowerConcept
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
var relatedToMetaEdge = createMetaEdge(META_EDGE_RELATED_TO, {
|
|
244
|
-
symmetric: true,
|
|
245
|
-
inference: "association",
|
|
246
|
-
description: "Non-hierarchical association"
|
|
247
|
-
});
|
|
248
|
-
function relatedTo(conceptA, conceptB) {
|
|
249
|
-
return {
|
|
250
|
-
metaEdge: relatedToMetaEdge,
|
|
251
|
-
from: conceptA,
|
|
252
|
-
to: conceptB
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
var equivalentToMetaEdge = createMetaEdge(META_EDGE_EQUIVALENT_TO, {
|
|
256
|
-
symmetric: true,
|
|
257
|
-
transitive: true,
|
|
258
|
-
inference: "substitution",
|
|
259
|
-
description: "Same class, different representation"
|
|
260
|
-
});
|
|
261
|
-
function equivalentTo(kindA, kindBOrIri) {
|
|
262
|
-
return {
|
|
263
|
-
metaEdge: equivalentToMetaEdge,
|
|
264
|
-
from: kindA,
|
|
265
|
-
to: kindBOrIri
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
var sameAsMetaEdge = createMetaEdge(META_EDGE_SAME_AS, {
|
|
269
|
-
symmetric: true,
|
|
270
|
-
transitive: true,
|
|
271
|
-
inference: "substitution",
|
|
272
|
-
description: "Same individual (for deduplication)"
|
|
273
|
-
});
|
|
274
|
-
function sameAs(kindA, kindBOrIri) {
|
|
275
|
-
return {
|
|
276
|
-
metaEdge: sameAsMetaEdge,
|
|
277
|
-
from: kindA,
|
|
278
|
-
to: kindBOrIri
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
var differentFromMetaEdge = createMetaEdge(META_EDGE_DIFFERENT_FROM, {
|
|
282
|
-
symmetric: true,
|
|
283
|
-
inference: "constraint",
|
|
284
|
-
description: "Explicitly different individuals"
|
|
285
|
-
});
|
|
286
|
-
function differentFrom(kindA, kindB) {
|
|
287
|
-
return {
|
|
288
|
-
metaEdge: differentFromMetaEdge,
|
|
289
|
-
from: kindA,
|
|
290
|
-
to: kindB
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
var disjointWithMetaEdge = createMetaEdge(META_EDGE_DISJOINT_WITH, {
|
|
294
|
-
symmetric: true,
|
|
295
|
-
inference: "constraint",
|
|
296
|
-
description: "Mutually exclusive types"
|
|
297
|
-
});
|
|
298
|
-
function disjointWith(kindA, kindB) {
|
|
299
|
-
return {
|
|
300
|
-
metaEdge: disjointWithMetaEdge,
|
|
301
|
-
from: kindA,
|
|
302
|
-
to: kindB
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
var partOfMetaEdge = createMetaEdge(META_EDGE_PART_OF, {
|
|
306
|
-
transitive: true,
|
|
307
|
-
inverse: META_EDGE_HAS_PART,
|
|
308
|
-
inference: "composition",
|
|
309
|
-
description: "X is part of Y"
|
|
310
|
-
});
|
|
311
|
-
function partOf(part, whole) {
|
|
312
|
-
return {
|
|
313
|
-
metaEdge: partOfMetaEdge,
|
|
314
|
-
from: part,
|
|
315
|
-
to: whole
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
var hasPartMetaEdge = createMetaEdge(META_EDGE_HAS_PART, {
|
|
319
|
-
transitive: true,
|
|
320
|
-
inverse: META_EDGE_PART_OF,
|
|
321
|
-
inference: "composition",
|
|
322
|
-
description: "Y has part X"
|
|
323
|
-
});
|
|
324
|
-
function hasPart(whole, part) {
|
|
325
|
-
return {
|
|
326
|
-
metaEdge: hasPartMetaEdge,
|
|
327
|
-
from: whole,
|
|
328
|
-
to: part
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
var inverseOfMetaEdge = createMetaEdge(META_EDGE_INVERSE_OF, {
|
|
332
|
-
symmetric: true,
|
|
333
|
-
inference: "none",
|
|
334
|
-
description: "Edge A is inverse of edge B"
|
|
335
|
-
});
|
|
336
|
-
var impliesMetaEdge = createMetaEdge(META_EDGE_IMPLIES, {
|
|
337
|
-
transitive: true,
|
|
338
|
-
inference: "none",
|
|
339
|
-
description: "Edge A implies edge B exists"
|
|
340
|
-
});
|
|
341
|
-
function inverseOf(edgeA, edgeB) {
|
|
342
|
-
return {
|
|
343
|
-
metaEdge: inverseOfMetaEdge,
|
|
344
|
-
from: edgeA,
|
|
345
|
-
to: edgeB
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
function implies(edgeA, edgeB) {
|
|
349
|
-
return {
|
|
350
|
-
metaEdge: impliesMetaEdge,
|
|
351
|
-
from: edgeA,
|
|
352
|
-
to: edgeB
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
var core = {
|
|
356
|
-
// Meta-edges
|
|
357
|
-
subClassOfMetaEdge,
|
|
358
|
-
broaderMetaEdge,
|
|
359
|
-
narrowerMetaEdge,
|
|
360
|
-
relatedToMetaEdge,
|
|
361
|
-
equivalentToMetaEdge,
|
|
362
|
-
sameAsMetaEdge,
|
|
363
|
-
differentFromMetaEdge,
|
|
364
|
-
disjointWithMetaEdge,
|
|
365
|
-
partOfMetaEdge,
|
|
366
|
-
hasPartMetaEdge,
|
|
367
|
-
inverseOfMetaEdge,
|
|
368
|
-
impliesMetaEdge,
|
|
369
|
-
// Relation factories
|
|
370
|
-
subClassOf,
|
|
371
|
-
broader,
|
|
372
|
-
narrower,
|
|
373
|
-
relatedTo,
|
|
374
|
-
equivalentTo,
|
|
375
|
-
sameAs,
|
|
376
|
-
differentFrom,
|
|
377
|
-
disjointWith,
|
|
378
|
-
partOf,
|
|
379
|
-
hasPart,
|
|
380
|
-
inverseOf,
|
|
381
|
-
implies
|
|
382
|
-
};
|
|
383
|
-
|
|
384
70
|
// src/query/subquery-utils.ts
|
|
385
71
|
function normalizeValueType(valueType) {
|
|
386
72
|
if (valueType === void 0 || valueType === "unknown") {
|
|
@@ -1024,8 +710,8 @@ function compileFieldJsonValue(field2, dialect, cteAlias, pointerOverride, cteCo
|
|
|
1024
710
|
}
|
|
1025
711
|
function resolveLiteralValueTypes(literals) {
|
|
1026
712
|
const resolved = /* @__PURE__ */ new Set();
|
|
1027
|
-
for (const
|
|
1028
|
-
const valueType = normalizeValueType2(
|
|
713
|
+
for (const literal3 of literals) {
|
|
714
|
+
const valueType = normalizeValueType2(literal3.valueType);
|
|
1029
715
|
if (valueType) {
|
|
1030
716
|
resolved.add(valueType);
|
|
1031
717
|
}
|
|
@@ -1053,12 +739,12 @@ function resolveComparisonValueType(field2, right) {
|
|
|
1053
739
|
}
|
|
1054
740
|
return fieldType;
|
|
1055
741
|
}
|
|
1056
|
-
function resolvePredicateValueType(preferred,
|
|
742
|
+
function resolvePredicateValueType(preferred, literal3) {
|
|
1057
743
|
const normalizedPreferred = normalizeValueType2(preferred);
|
|
1058
744
|
if (normalizedPreferred) {
|
|
1059
745
|
return normalizedPreferred;
|
|
1060
746
|
}
|
|
1061
|
-
return
|
|
747
|
+
return literal3 ? normalizeValueType2(literal3.valueType) : void 0;
|
|
1062
748
|
}
|
|
1063
749
|
function convertValueForSql(value, dialect) {
|
|
1064
750
|
return dialect.bindValue(value);
|
|
@@ -1928,7 +1614,8 @@ var DEFAULT_TABLE_NAMES = {
|
|
|
1928
1614
|
nodes: "typegraph_nodes",
|
|
1929
1615
|
edges: "typegraph_edges",
|
|
1930
1616
|
embeddings: "typegraph_node_embeddings",
|
|
1931
|
-
fulltext: "typegraph_node_fulltext"
|
|
1617
|
+
fulltext: "typegraph_node_fulltext",
|
|
1618
|
+
uniques: "typegraph_node_uniques"
|
|
1932
1619
|
};
|
|
1933
1620
|
var VALID_IDENTIFIER_PATTERN = /^[a-z_][a-z0-9_$]*$/i;
|
|
1934
1621
|
function validateTableName(name, label) {
|
|
@@ -1955,6 +1642,7 @@ function createSqlSchema(names = {}) {
|
|
|
1955
1642
|
validateTableName(tables.edges, "edges");
|
|
1956
1643
|
validateTableName(tables.embeddings, "embeddings");
|
|
1957
1644
|
validateTableName(tables.fulltext, "fulltext");
|
|
1645
|
+
validateTableName(tables.uniques, "uniques");
|
|
1958
1646
|
return {
|
|
1959
1647
|
tables,
|
|
1960
1648
|
nodesTable: sql.raw(quoteIdentifier2(tables.nodes)),
|
|
@@ -4256,147 +3944,676 @@ function mapSubgraphEdgeRow(row, projectionPlan) {
|
|
|
4256
3944
|
applyProjectedFields(projectedEdge, row, kindPlan);
|
|
4257
3945
|
return projectedEdge;
|
|
4258
3946
|
}
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4266
|
-
return compileSetOperationStandard(
|
|
4267
|
-
op,
|
|
4268
|
-
graphId,
|
|
4269
|
-
dialect,
|
|
4270
|
-
logicalPlan,
|
|
4271
|
-
compileQuery2
|
|
4272
|
-
);
|
|
4273
|
-
}
|
|
4274
|
-
function compileSetOperationWithSqliteCompoundStrategy(op, graphId, dialect, logicalPlan, schema, _compileQuery) {
|
|
4275
|
-
return compileSetOperationForSqlite(
|
|
4276
|
-
op,
|
|
4277
|
-
graphId,
|
|
4278
|
-
dialect,
|
|
4279
|
-
logicalPlan,
|
|
4280
|
-
schema
|
|
4281
|
-
);
|
|
4282
|
-
}
|
|
4283
|
-
var SET_OPERATION_STRATEGY_HANDLERS = {
|
|
4284
|
-
standard_parenthesized: compileSetOperationWithStandardParenthesizedStrategy,
|
|
4285
|
-
sqlite_compound: compileSetOperationWithSqliteCompoundStrategy
|
|
4286
|
-
};
|
|
4287
|
-
function runSetOperationPassPipeline(op, graphId, dialect) {
|
|
4288
|
-
let state = {
|
|
4289
|
-
dialect,
|
|
4290
|
-
graphId,
|
|
4291
|
-
logicalPlan: void 0,
|
|
4292
|
-
op
|
|
3947
|
+
|
|
3948
|
+
// src/graph-extension/classify.ts
|
|
3949
|
+
function classifyModifications(existing, next) {
|
|
3950
|
+
const incompatible = [];
|
|
3951
|
+
const requireEmptyMutable = /* @__PURE__ */ new Map();
|
|
3952
|
+
const recordIncompatible = (entry2) => {
|
|
3953
|
+
incompatible.push(entry2);
|
|
4293
3954
|
};
|
|
4294
|
-
const
|
|
4295
|
-
|
|
4296
|
-
|
|
4297
|
-
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
3955
|
+
const recordRequireEmpty = (entity, entry2) => {
|
|
3956
|
+
const key = `${entity}:${entry2.kind}`;
|
|
3957
|
+
const existingEntry = requireEmptyMutable.get(key);
|
|
3958
|
+
if (existingEntry === void 0) {
|
|
3959
|
+
requireEmptyMutable.set(key, {
|
|
3960
|
+
entity,
|
|
3961
|
+
kindName: entry2.kind,
|
|
3962
|
+
changes: [entry2]
|
|
4301
3963
|
});
|
|
4302
|
-
}
|
|
4303
|
-
|
|
4304
|
-
return {
|
|
4305
|
-
...currentState,
|
|
4306
|
-
logicalPlan
|
|
4307
|
-
};
|
|
3964
|
+
} else {
|
|
3965
|
+
existingEntry.changes.push(entry2);
|
|
4308
3966
|
}
|
|
4309
|
-
}
|
|
4310
|
-
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
3967
|
+
};
|
|
3968
|
+
for (const [name, nextNode] of Object.entries(next.nodes ?? {})) {
|
|
3969
|
+
const existingNode = existing.nodes?.[name];
|
|
3970
|
+
if (existingNode === void 0) continue;
|
|
3971
|
+
classifyNode(name, existingNode, nextNode, {
|
|
3972
|
+
recordIncompatible,
|
|
3973
|
+
recordRequireEmpty: (entry2) => {
|
|
3974
|
+
recordRequireEmpty("node", entry2);
|
|
3975
|
+
}
|
|
3976
|
+
});
|
|
3977
|
+
}
|
|
3978
|
+
for (const [name, nextEdge] of Object.entries(next.edges ?? {})) {
|
|
3979
|
+
const existingEdge = existing.edges?.[name];
|
|
3980
|
+
if (existingEdge === void 0) continue;
|
|
3981
|
+
classifyEdge(name, existingEdge, nextEdge, {
|
|
3982
|
+
recordIncompatible,
|
|
3983
|
+
recordRequireEmpty: (entry2) => {
|
|
3984
|
+
recordRequireEmpty("edge", entry2);
|
|
3985
|
+
}
|
|
3986
|
+
});
|
|
3987
|
+
}
|
|
3988
|
+
const requireEmpty = [];
|
|
3989
|
+
for (const value of requireEmptyMutable.values()) {
|
|
3990
|
+
requireEmpty.push(
|
|
3991
|
+
Object.freeze({
|
|
3992
|
+
entity: value.entity,
|
|
3993
|
+
kindName: value.kindName,
|
|
3994
|
+
changes: Object.freeze([...value.changes])
|
|
3995
|
+
})
|
|
4319
3996
|
);
|
|
4320
3997
|
}
|
|
4321
|
-
|
|
4322
|
-
const handler = SET_OPERATION_STRATEGY_HANDLERS[strategy];
|
|
4323
|
-
return handler(op, graphId, dialect, logicalPlan, schema, compileQuery2);
|
|
3998
|
+
return { incompatible, requireEmpty };
|
|
4324
3999
|
}
|
|
4325
|
-
function
|
|
4326
|
-
const
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
);
|
|
4332
|
-
|
|
4333
|
-
return emitSetOperationQuerySql({
|
|
4334
|
-
baseQuery: coreSql,
|
|
4335
|
-
logicalPlan,
|
|
4336
|
-
...suffixClauses.length === 0 ? {} : { suffixClauses }
|
|
4337
|
-
});
|
|
4000
|
+
function buildIncompatibleChangeError(classification, nonEmpty, graphId) {
|
|
4001
|
+
const promoted = [];
|
|
4002
|
+
for (const entry2 of nonEmpty) {
|
|
4003
|
+
promoted.push(...entry2.changes);
|
|
4004
|
+
}
|
|
4005
|
+
const all = [...classification.incompatible, ...promoted];
|
|
4006
|
+
if (all.length === 0) return void 0;
|
|
4007
|
+
return new IncompatibleChangeError(all, graphId);
|
|
4338
4008
|
}
|
|
4339
|
-
function
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
graphId,
|
|
4343
|
-
dialect,
|
|
4344
|
-
compileQuery2
|
|
4345
|
-
);
|
|
4346
|
-
const right = compileComposableQueryStandard(
|
|
4347
|
-
op.right,
|
|
4348
|
-
graphId,
|
|
4349
|
-
dialect,
|
|
4350
|
-
compileQuery2
|
|
4351
|
-
);
|
|
4352
|
-
const opSql = sql.raw(OPERATOR_MAP[op.operator]);
|
|
4353
|
-
return sql`(${left}) ${opSql} (${right})`;
|
|
4009
|
+
function classifyNode(kind, existing, next, recorders) {
|
|
4010
|
+
classifyProperties(kind, existing.properties, next.properties, recorders);
|
|
4011
|
+
classifyUnique(kind, existing.unique ?? [], next.unique ?? [], recorders);
|
|
4354
4012
|
}
|
|
4355
|
-
function
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4013
|
+
function classifyEdge(kind, existing, next, recorders) {
|
|
4014
|
+
classifyProperties(
|
|
4015
|
+
kind,
|
|
4016
|
+
existing.properties ?? {},
|
|
4017
|
+
next.properties ?? {},
|
|
4018
|
+
recorders
|
|
4019
|
+
);
|
|
4020
|
+
classifyEdgeEndpoints(kind, existing, next, recorders);
|
|
4021
|
+
}
|
|
4022
|
+
function classifyEdgeEndpoints(kind, existing, next, recorders) {
|
|
4023
|
+
const fromExisting = new Set(existing.from);
|
|
4024
|
+
const toExisting = new Set(existing.to);
|
|
4025
|
+
const fromNext = new Set(next.from);
|
|
4026
|
+
const toNext = new Set(next.to);
|
|
4027
|
+
for (const removed of [...fromExisting].filter(
|
|
4028
|
+
(entry2) => !fromNext.has(entry2)
|
|
4029
|
+
)) {
|
|
4030
|
+
recorders.recordRequireEmpty({
|
|
4031
|
+
kind,
|
|
4032
|
+
type: "TIGHTEN_EDGE_ENDPOINTS",
|
|
4033
|
+
detail: `removed "${removed}" from \`from\``
|
|
4034
|
+
});
|
|
4363
4035
|
}
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
|
|
4370
|
-
validateSqliteSetOpLeaf(leaf.ast);
|
|
4036
|
+
for (const removed of [...toExisting].filter((entry2) => !toNext.has(entry2))) {
|
|
4037
|
+
recorders.recordRequireEmpty({
|
|
4038
|
+
kind,
|
|
4039
|
+
type: "TIGHTEN_EDGE_ENDPOINTS",
|
|
4040
|
+
detail: `removed "${removed}" from \`to\``
|
|
4041
|
+
});
|
|
4371
4042
|
}
|
|
4372
|
-
return { leaves };
|
|
4373
4043
|
}
|
|
4374
|
-
function
|
|
4375
|
-
const
|
|
4376
|
-
const
|
|
4377
|
-
const
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
);
|
|
4044
|
+
function classifyProperties(kind, existing, next, recorders) {
|
|
4045
|
+
const existingNames = new Set(Object.keys(existing));
|
|
4046
|
+
const nextNames = new Set(Object.keys(next));
|
|
4047
|
+
for (const name of existingNames) {
|
|
4048
|
+
if (!nextNames.has(name)) {
|
|
4049
|
+
recorders.recordIncompatible({
|
|
4050
|
+
kind,
|
|
4051
|
+
field: name,
|
|
4052
|
+
type: "REMOVE_PROPERTY"
|
|
4053
|
+
});
|
|
4384
4054
|
}
|
|
4385
|
-
};
|
|
4386
|
-
for (const leaf of leaves) {
|
|
4387
|
-
const cte = compilePrefixedStartCte(leaf.ast, leaf.prefix, graphId, ctx);
|
|
4388
|
-
allCtes.push(cte);
|
|
4389
4055
|
}
|
|
4390
|
-
const
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4056
|
+
for (const name of nextNames) {
|
|
4057
|
+
if (!existingNames.has(name)) {
|
|
4058
|
+
const property = next[name];
|
|
4059
|
+
if (property.optional === true) continue;
|
|
4060
|
+
recorders.recordRequireEmpty({
|
|
4061
|
+
kind,
|
|
4062
|
+
field: name,
|
|
4063
|
+
type: "ADD_REQUIRED_PROPERTY"
|
|
4064
|
+
});
|
|
4065
|
+
}
|
|
4394
4066
|
}
|
|
4395
|
-
const
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
|
|
4067
|
+
for (const name of existingNames) {
|
|
4068
|
+
if (!nextNames.has(name)) continue;
|
|
4069
|
+
classifyProperty(kind, name, existing[name], next[name], recorders);
|
|
4070
|
+
}
|
|
4071
|
+
}
|
|
4072
|
+
function classifyProperty(kind, field2, existing, next, recorders) {
|
|
4073
|
+
if (existing.type !== next.type) {
|
|
4074
|
+
recorders.recordIncompatible({
|
|
4075
|
+
kind,
|
|
4076
|
+
field: field2,
|
|
4077
|
+
type: "TYPE_CHANGE",
|
|
4078
|
+
detail: `${existing.type} \u2192 ${next.type}`
|
|
4079
|
+
});
|
|
4080
|
+
return;
|
|
4081
|
+
}
|
|
4082
|
+
const wasOptional = existing.optional === true;
|
|
4083
|
+
const isOptional = next.optional === true;
|
|
4084
|
+
if (!wasOptional && isOptional) ; else if (wasOptional && !isOptional) {
|
|
4085
|
+
recorders.recordRequireEmpty({
|
|
4086
|
+
kind,
|
|
4087
|
+
field: field2,
|
|
4088
|
+
type: "TIGHTEN_OPTIONALITY",
|
|
4089
|
+
detail: "optional: true \u2192 false"
|
|
4090
|
+
});
|
|
4091
|
+
}
|
|
4092
|
+
switch (existing.type) {
|
|
4093
|
+
case "string": {
|
|
4094
|
+
classifyString(
|
|
4095
|
+
kind,
|
|
4096
|
+
field2,
|
|
4097
|
+
existing,
|
|
4098
|
+
next,
|
|
4099
|
+
recorders
|
|
4100
|
+
);
|
|
4101
|
+
break;
|
|
4102
|
+
}
|
|
4103
|
+
case "number": {
|
|
4104
|
+
classifyNumber(
|
|
4105
|
+
kind,
|
|
4106
|
+
field2,
|
|
4107
|
+
existing,
|
|
4108
|
+
next,
|
|
4109
|
+
recorders
|
|
4110
|
+
);
|
|
4111
|
+
break;
|
|
4112
|
+
}
|
|
4113
|
+
case "enum": {
|
|
4114
|
+
classifyEnum(
|
|
4115
|
+
kind,
|
|
4116
|
+
field2,
|
|
4117
|
+
existing,
|
|
4118
|
+
next,
|
|
4119
|
+
recorders
|
|
4120
|
+
);
|
|
4121
|
+
break;
|
|
4122
|
+
}
|
|
4123
|
+
case "array": {
|
|
4124
|
+
classifyArray(
|
|
4125
|
+
kind,
|
|
4126
|
+
field2,
|
|
4127
|
+
existing,
|
|
4128
|
+
next,
|
|
4129
|
+
recorders
|
|
4130
|
+
);
|
|
4131
|
+
break;
|
|
4132
|
+
}
|
|
4133
|
+
case "object": {
|
|
4134
|
+
classifyObject(
|
|
4135
|
+
kind,
|
|
4136
|
+
field2,
|
|
4137
|
+
existing,
|
|
4138
|
+
next,
|
|
4139
|
+
recorders
|
|
4140
|
+
);
|
|
4141
|
+
break;
|
|
4142
|
+
}
|
|
4143
|
+
}
|
|
4144
|
+
}
|
|
4145
|
+
function classifyString(kind, field2, existing, next, recorders) {
|
|
4146
|
+
if (existing.minLength !== next.minLength && next.minLength !== void 0 && (existing.minLength === void 0 || next.minLength > existing.minLength)) {
|
|
4147
|
+
recorders.recordRequireEmpty({
|
|
4148
|
+
kind,
|
|
4149
|
+
field: field2,
|
|
4150
|
+
type: "TIGHTEN_CONSTRAINT",
|
|
4151
|
+
detail: `minLength: ${existing.minLength ?? "(none)"} \u2192 ${next.minLength}`
|
|
4152
|
+
});
|
|
4153
|
+
}
|
|
4154
|
+
if (existing.maxLength !== next.maxLength && next.maxLength !== void 0 && (existing.maxLength === void 0 || next.maxLength < existing.maxLength)) {
|
|
4155
|
+
recorders.recordRequireEmpty({
|
|
4156
|
+
kind,
|
|
4157
|
+
field: field2,
|
|
4158
|
+
type: "TIGHTEN_CONSTRAINT",
|
|
4159
|
+
detail: `maxLength: ${existing.maxLength ?? "(none)"} \u2192 ${next.maxLength}`
|
|
4160
|
+
});
|
|
4161
|
+
}
|
|
4162
|
+
if (existing.pattern !== next.pattern) {
|
|
4163
|
+
if (existing.pattern === void 0 && next.pattern !== void 0) {
|
|
4164
|
+
recorders.recordRequireEmpty({
|
|
4165
|
+
kind,
|
|
4166
|
+
field: field2,
|
|
4167
|
+
type: "ADD_PATTERN",
|
|
4168
|
+
detail: next.pattern
|
|
4169
|
+
});
|
|
4170
|
+
} else if (existing.pattern !== void 0 && next.pattern === void 0) ; else if (existing.pattern !== void 0 && next.pattern !== void 0 && existing.pattern !== next.pattern) {
|
|
4171
|
+
recorders.recordRequireEmpty({
|
|
4172
|
+
kind,
|
|
4173
|
+
field: field2,
|
|
4174
|
+
type: "CHANGE_PATTERN",
|
|
4175
|
+
detail: `${existing.pattern} \u2192 ${next.pattern}`
|
|
4176
|
+
});
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
if (existing.format !== next.format) {
|
|
4180
|
+
if (existing.format === void 0 && next.format !== void 0) {
|
|
4181
|
+
recorders.recordRequireEmpty({
|
|
4182
|
+
kind,
|
|
4183
|
+
field: field2,
|
|
4184
|
+
type: "ADD_FORMAT",
|
|
4185
|
+
detail: next.format
|
|
4186
|
+
});
|
|
4187
|
+
} else if (existing.format !== void 0 && next.format === void 0) ; else {
|
|
4188
|
+
recorders.recordRequireEmpty({
|
|
4189
|
+
kind,
|
|
4190
|
+
field: field2,
|
|
4191
|
+
type: "CHANGE_FORMAT",
|
|
4192
|
+
detail: `${existing.format} \u2192 ${next.format}`
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
}
|
|
4196
|
+
}
|
|
4197
|
+
function classifyNumber(kind, field2, existing, next, recorders) {
|
|
4198
|
+
if (next.min !== void 0 && (existing.min === void 0 || next.min > existing.min)) {
|
|
4199
|
+
recorders.recordRequireEmpty({
|
|
4200
|
+
kind,
|
|
4201
|
+
field: field2,
|
|
4202
|
+
type: "TIGHTEN_CONSTRAINT",
|
|
4203
|
+
detail: `min: ${existing.min ?? "(none)"} \u2192 ${next.min}`
|
|
4204
|
+
});
|
|
4205
|
+
}
|
|
4206
|
+
if (next.max !== void 0 && (existing.max === void 0 || next.max < existing.max)) {
|
|
4207
|
+
recorders.recordRequireEmpty({
|
|
4208
|
+
kind,
|
|
4209
|
+
field: field2,
|
|
4210
|
+
type: "TIGHTEN_CONSTRAINT",
|
|
4211
|
+
detail: `max: ${existing.max ?? "(none)"} \u2192 ${next.max}`
|
|
4212
|
+
});
|
|
4213
|
+
}
|
|
4214
|
+
if (existing.int !== next.int && next.int === true && existing.int !== true) {
|
|
4215
|
+
recorders.recordRequireEmpty({
|
|
4216
|
+
kind,
|
|
4217
|
+
field: field2,
|
|
4218
|
+
type: "TIGHTEN_INT",
|
|
4219
|
+
detail: "int: false \u2192 true"
|
|
4220
|
+
});
|
|
4221
|
+
}
|
|
4222
|
+
}
|
|
4223
|
+
function classifyEnum(kind, field2, existing, next, recorders) {
|
|
4224
|
+
const nextValues = new Set(next.values);
|
|
4225
|
+
const removed = existing.values.filter((value) => !nextValues.has(value));
|
|
4226
|
+
if (removed.length > 0) {
|
|
4227
|
+
recorders.recordRequireEmpty({
|
|
4228
|
+
kind,
|
|
4229
|
+
field: field2,
|
|
4230
|
+
type: "TIGHTEN_ENUM",
|
|
4231
|
+
detail: `removed values: ${removed.join(", ")}`
|
|
4232
|
+
});
|
|
4233
|
+
}
|
|
4234
|
+
}
|
|
4235
|
+
function classifyArray(kind, field2, existing, next, recorders) {
|
|
4236
|
+
if (existing.items.type !== next.items.type) {
|
|
4237
|
+
recorders.recordIncompatible({
|
|
4238
|
+
kind,
|
|
4239
|
+
field: `${field2}.items`,
|
|
4240
|
+
type: "TYPE_CHANGE",
|
|
4241
|
+
detail: `${existing.items.type} \u2192 ${next.items.type}`
|
|
4242
|
+
});
|
|
4243
|
+
return;
|
|
4244
|
+
}
|
|
4245
|
+
classifyProperty(
|
|
4246
|
+
kind,
|
|
4247
|
+
`${field2}.items`,
|
|
4248
|
+
existing.items,
|
|
4249
|
+
next.items,
|
|
4250
|
+
recorders
|
|
4251
|
+
);
|
|
4252
|
+
}
|
|
4253
|
+
function classifyObject(kind, field2, existing, next, recorders) {
|
|
4254
|
+
classifyProperties(kind, existing.properties, next.properties, {
|
|
4255
|
+
recordIncompatible: (entry2) => {
|
|
4256
|
+
recorders.recordIncompatible({
|
|
4257
|
+
...entry2,
|
|
4258
|
+
field: `${field2}.${entry2.field ?? ""}`
|
|
4259
|
+
});
|
|
4260
|
+
},
|
|
4261
|
+
recordRequireEmpty: (entry2) => {
|
|
4262
|
+
recorders.recordRequireEmpty({
|
|
4263
|
+
...entry2,
|
|
4264
|
+
field: `${field2}.${entry2.field ?? ""}`
|
|
4265
|
+
});
|
|
4266
|
+
}
|
|
4267
|
+
});
|
|
4268
|
+
}
|
|
4269
|
+
function classifyUnique(kind, existing, next, recorders) {
|
|
4270
|
+
const existingByName = new Map(
|
|
4271
|
+
existing.map((constraint) => [constraint.name, constraint])
|
|
4272
|
+
);
|
|
4273
|
+
const nextByName = new Map(
|
|
4274
|
+
next.map((constraint) => [constraint.name, constraint])
|
|
4275
|
+
);
|
|
4276
|
+
for (const [name, constraint] of nextByName) {
|
|
4277
|
+
if (!existingByName.has(name)) {
|
|
4278
|
+
recorders.recordRequireEmpty({
|
|
4279
|
+
kind,
|
|
4280
|
+
type: "ADD_UNIQUE_ON_POPULATED",
|
|
4281
|
+
detail: `unique constraint "${name}" on [${constraint.fields.join(", ")}]`
|
|
4282
|
+
});
|
|
4283
|
+
}
|
|
4284
|
+
}
|
|
4285
|
+
for (const [name, constraint] of existingByName) {
|
|
4286
|
+
const newOne = nextByName.get(name);
|
|
4287
|
+
if (newOne === void 0) continue;
|
|
4288
|
+
const { name: _existingName, ...existingShape } = constraint;
|
|
4289
|
+
const { name: _nextName, ...nextShape } = newOne;
|
|
4290
|
+
if (!canonicalEqual(existingShape, nextShape)) {
|
|
4291
|
+
recorders.recordRequireEmpty({
|
|
4292
|
+
kind,
|
|
4293
|
+
type: "ADD_UNIQUE_ON_POPULATED",
|
|
4294
|
+
detail: `unique constraint "${name}" reshaped`
|
|
4295
|
+
});
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
}
|
|
4299
|
+
|
|
4300
|
+
// src/graph-extension/remove.ts
|
|
4301
|
+
function planRemovals(graph, names) {
|
|
4302
|
+
const document = graph.extension;
|
|
4303
|
+
const namesSet = new Set(names);
|
|
4304
|
+
if (namesSet.size === 0) {
|
|
4305
|
+
return { document, removedNodeKinds: [], removedEdgeKinds: [] };
|
|
4306
|
+
}
|
|
4307
|
+
const { nodes: runtimeNodeNames, edges: runtimeEdgeNames } = extensionKindNames(document);
|
|
4308
|
+
const compileTimeNodeNames = new Set(
|
|
4309
|
+
Object.keys(graph.nodes).filter((name) => !runtimeNodeNames.has(name))
|
|
4310
|
+
);
|
|
4311
|
+
const compileTimeEdgeNames = new Set(
|
|
4312
|
+
Object.keys(graph.edges).filter((name) => !runtimeEdgeNames.has(name))
|
|
4313
|
+
);
|
|
4314
|
+
for (const name of namesSet) {
|
|
4315
|
+
if (compileTimeNodeNames.has(name)) {
|
|
4316
|
+
throw new RemoveCompileTimeKindError(name, "node", graph.id);
|
|
4317
|
+
}
|
|
4318
|
+
if (compileTimeEdgeNames.has(name)) {
|
|
4319
|
+
throw new RemoveCompileTimeKindError(name, "edge", graph.id);
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
const removedNodeKinds = [...namesSet].filter(
|
|
4323
|
+
(name) => runtimeNodeNames.has(name)
|
|
4324
|
+
);
|
|
4325
|
+
const explicitlyRemovedEdgeKinds = [...namesSet].filter(
|
|
4326
|
+
(name) => runtimeEdgeNames.has(name)
|
|
4327
|
+
);
|
|
4328
|
+
const referentsByKind = buildCompileTimeReferentIndex(
|
|
4329
|
+
graph,
|
|
4330
|
+
runtimeEdgeNames
|
|
4331
|
+
);
|
|
4332
|
+
for (const kindName of removedNodeKinds) {
|
|
4333
|
+
const referents = referentsByKind.get(kindName);
|
|
4334
|
+
if (referents !== void 0 && referents.length > 0) {
|
|
4335
|
+
throw new KindHasReferentsError(kindName, referents, graph.id);
|
|
4336
|
+
}
|
|
4337
|
+
}
|
|
4338
|
+
if (document === void 0) {
|
|
4339
|
+
return {
|
|
4340
|
+
document: void 0,
|
|
4341
|
+
removedNodeKinds: [],
|
|
4342
|
+
removedEdgeKinds: []
|
|
4343
|
+
};
|
|
4344
|
+
}
|
|
4345
|
+
const removedNodeKindsSet = new Set(removedNodeKinds);
|
|
4346
|
+
const explicitlyRemovedEdgeKindsSet = new Set(explicitlyRemovedEdgeKinds);
|
|
4347
|
+
const cascadeEdges = /* @__PURE__ */ new Set();
|
|
4348
|
+
const updatedEdges = {};
|
|
4349
|
+
for (const [edgeName, edgeDocument] of Object.entries(document.edges ?? {})) {
|
|
4350
|
+
if (explicitlyRemovedEdgeKindsSet.has(edgeName)) {
|
|
4351
|
+
cascadeEdges.add(edgeName);
|
|
4352
|
+
continue;
|
|
4353
|
+
}
|
|
4354
|
+
const newFrom = edgeDocument.from.filter(
|
|
4355
|
+
(kind) => !removedNodeKindsSet.has(kind)
|
|
4356
|
+
);
|
|
4357
|
+
const newTo = edgeDocument.to.filter(
|
|
4358
|
+
(kind) => !removedNodeKindsSet.has(kind)
|
|
4359
|
+
);
|
|
4360
|
+
if (newFrom.length === 0 || newTo.length === 0) {
|
|
4361
|
+
cascadeEdges.add(edgeName);
|
|
4362
|
+
continue;
|
|
4363
|
+
}
|
|
4364
|
+
updatedEdges[edgeName] = {
|
|
4365
|
+
...edgeDocument,
|
|
4366
|
+
from: newFrom,
|
|
4367
|
+
to: newTo
|
|
4368
|
+
};
|
|
4369
|
+
}
|
|
4370
|
+
const updatedNodes = {};
|
|
4371
|
+
for (const [nodeName, nodeDocument] of Object.entries(document.nodes ?? {})) {
|
|
4372
|
+
if (removedNodeKindsSet.has(nodeName)) continue;
|
|
4373
|
+
updatedNodes[nodeName] = nodeDocument;
|
|
4374
|
+
}
|
|
4375
|
+
const survivingOntology = (document.ontology ?? []).filter(
|
|
4376
|
+
(relation) => !removedNodeKindsSet.has(relation.from) && !removedNodeKindsSet.has(relation.to)
|
|
4377
|
+
);
|
|
4378
|
+
const allRemovedKinds = /* @__PURE__ */ new Set([
|
|
4379
|
+
...removedNodeKindsSet,
|
|
4380
|
+
...cascadeEdges,
|
|
4381
|
+
...explicitlyRemovedEdgeKindsSet
|
|
4382
|
+
]);
|
|
4383
|
+
const survivingIndexes = (document.indexes ?? []).filter(
|
|
4384
|
+
(index) => !allRemovedKinds.has(index.kind)
|
|
4385
|
+
);
|
|
4386
|
+
const newDocument = Object.freeze({
|
|
4387
|
+
...document.version === void 0 ? {} : { version: document.version },
|
|
4388
|
+
...Object.keys(updatedNodes).length === 0 ? {} : { nodes: updatedNodes },
|
|
4389
|
+
...Object.keys(updatedEdges).length === 0 ? {} : { edges: updatedEdges },
|
|
4390
|
+
...survivingOntology.length === 0 ? {} : { ontology: survivingOntology },
|
|
4391
|
+
...survivingIndexes.length === 0 ? {} : { indexes: survivingIndexes }
|
|
4392
|
+
});
|
|
4393
|
+
return {
|
|
4394
|
+
document: newDocument,
|
|
4395
|
+
removedNodeKinds,
|
|
4396
|
+
removedEdgeKinds: [
|
|
4397
|
+
...explicitlyRemovedEdgeKinds,
|
|
4398
|
+
...[...cascadeEdges].filter(
|
|
4399
|
+
(name) => !explicitlyRemovedEdgeKindsSet.has(name)
|
|
4400
|
+
)
|
|
4401
|
+
]
|
|
4402
|
+
};
|
|
4403
|
+
}
|
|
4404
|
+
function stripGraphExtension(graph) {
|
|
4405
|
+
const document = graph.extension;
|
|
4406
|
+
if (document === void 0) return graph;
|
|
4407
|
+
const { nodes: runtimeNodeNames, edges: runtimeEdgeNames } = extensionKindNames(document);
|
|
4408
|
+
const runtimeOntologyKeys = buildGraphExtensionOntologyKeySet(document);
|
|
4409
|
+
const compileNodes = {};
|
|
4410
|
+
for (const [name, registration] of Object.entries(graph.nodes)) {
|
|
4411
|
+
if (runtimeNodeNames.has(name)) continue;
|
|
4412
|
+
compileNodes[name] = registration;
|
|
4413
|
+
}
|
|
4414
|
+
const compileEdges = {};
|
|
4415
|
+
for (const [name, registration] of Object.entries(graph.edges)) {
|
|
4416
|
+
if (runtimeEdgeNames.has(name)) continue;
|
|
4417
|
+
compileEdges[name] = registration;
|
|
4418
|
+
}
|
|
4419
|
+
const compileOntology = graph.ontology.filter(
|
|
4420
|
+
(relation) => !runtimeOntologyKeys.has(compileTimeOntologyKey(relation))
|
|
4421
|
+
);
|
|
4422
|
+
const compileIndexes = (graph.indexes ?? []).filter(
|
|
4423
|
+
(index) => index.origin !== "runtime"
|
|
4424
|
+
);
|
|
4425
|
+
return Object.freeze({
|
|
4426
|
+
...graph,
|
|
4427
|
+
nodes: compileNodes,
|
|
4428
|
+
edges: compileEdges,
|
|
4429
|
+
ontology: compileOntology,
|
|
4430
|
+
indexes: compileIndexes.length === 0 ? void 0 : compileIndexes,
|
|
4431
|
+
extension: void 0
|
|
4432
|
+
});
|
|
4433
|
+
}
|
|
4434
|
+
function buildCompileTimeReferentIndex(graph, runtimeEdgeNames) {
|
|
4435
|
+
const index = /* @__PURE__ */ new Map();
|
|
4436
|
+
const append = (kindName, referent) => {
|
|
4437
|
+
const existing = index.get(kindName);
|
|
4438
|
+
if (existing === void 0) index.set(kindName, [referent]);
|
|
4439
|
+
else existing.push(referent);
|
|
4440
|
+
};
|
|
4441
|
+
for (const [edgeName, registration] of Object.entries(graph.edges)) {
|
|
4442
|
+
if (runtimeEdgeNames.has(edgeName)) continue;
|
|
4443
|
+
const reg = registration;
|
|
4444
|
+
const referent = {
|
|
4445
|
+
type: "compile-time-edge",
|
|
4446
|
+
name: edgeName
|
|
4447
|
+
};
|
|
4448
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4449
|
+
for (const endpoint of reg.from ?? []) {
|
|
4450
|
+
if (seen.has(endpoint.kind)) continue;
|
|
4451
|
+
seen.add(endpoint.kind);
|
|
4452
|
+
append(endpoint.kind, referent);
|
|
4453
|
+
}
|
|
4454
|
+
for (const endpoint of reg.to ?? []) {
|
|
4455
|
+
if (seen.has(endpoint.kind)) continue;
|
|
4456
|
+
seen.add(endpoint.kind);
|
|
4457
|
+
append(endpoint.kind, referent);
|
|
4458
|
+
}
|
|
4459
|
+
}
|
|
4460
|
+
const runtimeOntologyKeys = buildGraphExtensionOntologyKeySet(
|
|
4461
|
+
graph.extension
|
|
4462
|
+
);
|
|
4463
|
+
for (const relation of graph.ontology) {
|
|
4464
|
+
if (runtimeOntologyKeys.has(compileTimeOntologyKey(relation))) continue;
|
|
4465
|
+
const fromName = getTypeName(relation.from);
|
|
4466
|
+
const toName = getTypeName(relation.to);
|
|
4467
|
+
const referent = {
|
|
4468
|
+
type: "compile-time-ontology",
|
|
4469
|
+
name: `${relation.metaEdge.name}(${fromName}, ${toName})`
|
|
4470
|
+
};
|
|
4471
|
+
append(fromName, referent);
|
|
4472
|
+
if (toName !== fromName) append(toName, referent);
|
|
4473
|
+
}
|
|
4474
|
+
return index;
|
|
4475
|
+
}
|
|
4476
|
+
var OPERATOR_MAP = {
|
|
4477
|
+
union: "UNION",
|
|
4478
|
+
unionAll: "UNION ALL",
|
|
4479
|
+
intersect: "INTERSECT",
|
|
4480
|
+
except: "EXCEPT"
|
|
4481
|
+
};
|
|
4482
|
+
function compileSetOperationWithStandardParenthesizedStrategy(op, graphId, dialect, logicalPlan, _schema, compileQuery2) {
|
|
4483
|
+
return compileSetOperationStandard(
|
|
4484
|
+
op,
|
|
4485
|
+
graphId,
|
|
4486
|
+
dialect,
|
|
4487
|
+
logicalPlan,
|
|
4488
|
+
compileQuery2
|
|
4489
|
+
);
|
|
4490
|
+
}
|
|
4491
|
+
function compileSetOperationWithSqliteCompoundStrategy(op, graphId, dialect, logicalPlan, schema, _compileQuery) {
|
|
4492
|
+
return compileSetOperationForSqlite(
|
|
4493
|
+
op,
|
|
4494
|
+
graphId,
|
|
4495
|
+
dialect,
|
|
4496
|
+
logicalPlan,
|
|
4497
|
+
schema
|
|
4498
|
+
);
|
|
4499
|
+
}
|
|
4500
|
+
var SET_OPERATION_STRATEGY_HANDLERS = {
|
|
4501
|
+
standard_parenthesized: compileSetOperationWithStandardParenthesizedStrategy,
|
|
4502
|
+
sqlite_compound: compileSetOperationWithSqliteCompoundStrategy
|
|
4503
|
+
};
|
|
4504
|
+
function runSetOperationPassPipeline(op, graphId, dialect) {
|
|
4505
|
+
let state = {
|
|
4506
|
+
dialect,
|
|
4507
|
+
graphId,
|
|
4508
|
+
logicalPlan: void 0,
|
|
4509
|
+
op
|
|
4510
|
+
};
|
|
4511
|
+
const logicalPlanPass = runCompilerPass(state, {
|
|
4512
|
+
name: "logical_plan",
|
|
4513
|
+
execute(currentState) {
|
|
4514
|
+
return lowerSetOperationToLogicalPlan({
|
|
4515
|
+
dialect: currentState.dialect.name,
|
|
4516
|
+
graphId: currentState.graphId,
|
|
4517
|
+
op: currentState.op
|
|
4518
|
+
});
|
|
4519
|
+
},
|
|
4520
|
+
update(currentState, logicalPlan) {
|
|
4521
|
+
return {
|
|
4522
|
+
...currentState,
|
|
4523
|
+
logicalPlan
|
|
4524
|
+
};
|
|
4525
|
+
}
|
|
4526
|
+
});
|
|
4527
|
+
state = logicalPlanPass.state;
|
|
4528
|
+
return state;
|
|
4529
|
+
}
|
|
4530
|
+
function compileSetOperation(op, graphId, dialect, schema, compileQuery2) {
|
|
4531
|
+
const passState = runSetOperationPassPipeline(op, graphId, dialect);
|
|
4532
|
+
const { logicalPlan } = passState;
|
|
4533
|
+
if (logicalPlan === void 0) {
|
|
4534
|
+
throw new CompilerInvariantError(
|
|
4535
|
+
"Logical plan pass did not initialize plan state"
|
|
4536
|
+
);
|
|
4537
|
+
}
|
|
4538
|
+
const strategy = dialect.capabilities.setOperationStrategy;
|
|
4539
|
+
const handler = SET_OPERATION_STRATEGY_HANDLERS[strategy];
|
|
4540
|
+
return handler(op, graphId, dialect, logicalPlan, schema, compileQuery2);
|
|
4541
|
+
}
|
|
4542
|
+
function compileSetOperationStandard(op, graphId, dialect, logicalPlan, compileQuery2) {
|
|
4543
|
+
const coreSql = compileSetOperationCoreStandard(
|
|
4544
|
+
op,
|
|
4545
|
+
graphId,
|
|
4546
|
+
dialect,
|
|
4547
|
+
compileQuery2
|
|
4548
|
+
);
|
|
4549
|
+
const suffixClauses = buildSetOperationSuffixClauses(op, dialect);
|
|
4550
|
+
return emitSetOperationQuerySql({
|
|
4551
|
+
baseQuery: coreSql,
|
|
4552
|
+
logicalPlan,
|
|
4553
|
+
...suffixClauses.length === 0 ? {} : { suffixClauses }
|
|
4554
|
+
});
|
|
4555
|
+
}
|
|
4556
|
+
function compileSetOperationCoreStandard(op, graphId, dialect, compileQuery2) {
|
|
4557
|
+
const left = compileComposableQueryStandard(
|
|
4558
|
+
op.left,
|
|
4559
|
+
graphId,
|
|
4560
|
+
dialect,
|
|
4561
|
+
compileQuery2
|
|
4562
|
+
);
|
|
4563
|
+
const right = compileComposableQueryStandard(
|
|
4564
|
+
op.right,
|
|
4565
|
+
graphId,
|
|
4566
|
+
dialect,
|
|
4567
|
+
compileQuery2
|
|
4568
|
+
);
|
|
4569
|
+
const opSql = sql.raw(OPERATOR_MAP[op.operator]);
|
|
4570
|
+
return sql`(${left}) ${opSql} (${right})`;
|
|
4571
|
+
}
|
|
4572
|
+
function compileComposableQueryStandard(query, graphId, dialect, compileQuery2) {
|
|
4573
|
+
if ("__type" in query) {
|
|
4574
|
+
return compileSetOperationCoreStandard(
|
|
4575
|
+
query,
|
|
4576
|
+
graphId,
|
|
4577
|
+
dialect,
|
|
4578
|
+
compileQuery2
|
|
4579
|
+
);
|
|
4580
|
+
}
|
|
4581
|
+
return compileQuery2(query, graphId);
|
|
4582
|
+
}
|
|
4583
|
+
function runSqliteSetOperationValidationPass(op) {
|
|
4584
|
+
const leaves = [];
|
|
4585
|
+
collectLeafQueries(op, leaves, "q");
|
|
4586
|
+
for (const leaf of leaves) {
|
|
4587
|
+
validateSqliteSetOpLeaf(leaf.ast);
|
|
4588
|
+
}
|
|
4589
|
+
return { leaves };
|
|
4590
|
+
}
|
|
4591
|
+
function compileSetOperationForSqlite(op, graphId, dialect, logicalPlan, schema) {
|
|
4592
|
+
const { leaves } = runSqliteSetOperationValidationPass(op);
|
|
4593
|
+
const allCtes = [];
|
|
4594
|
+
const ctx = {
|
|
4595
|
+
dialect,
|
|
4596
|
+
schema,
|
|
4597
|
+
compileQuery: () => {
|
|
4598
|
+
throw new CompilerInvariantError(
|
|
4599
|
+
"compileQuery is not available in set-operation CTE compilation"
|
|
4600
|
+
);
|
|
4601
|
+
}
|
|
4602
|
+
};
|
|
4603
|
+
for (const leaf of leaves) {
|
|
4604
|
+
const cte = compilePrefixedStartCte(leaf.ast, leaf.prefix, graphId, ctx);
|
|
4605
|
+
allCtes.push(cte);
|
|
4606
|
+
}
|
|
4607
|
+
const selectStatements = [];
|
|
4608
|
+
for (const leaf of leaves) {
|
|
4609
|
+
const select = compilePrefixedSelect(leaf.ast, leaf.prefix, dialect);
|
|
4610
|
+
selectStatements.push(select);
|
|
4611
|
+
}
|
|
4612
|
+
const compoundSelect = buildCompoundSelect(op, leaves, selectStatements);
|
|
4613
|
+
const suffixClauses = buildSetOperationSuffixClauses(op, dialect);
|
|
4614
|
+
return emitSetOperationQuerySql({
|
|
4615
|
+
baseQuery: compoundSelect,
|
|
4616
|
+
...allCtes.length === 0 ? {} : { ctes: allCtes },
|
|
4400
4617
|
logicalPlan,
|
|
4401
4618
|
...suffixClauses.length === 0 ? {} : { suffixClauses }
|
|
4402
4619
|
});
|
|
@@ -8148,6 +8365,45 @@ var ExecutableQuery = class _ExecutableQuery {
|
|
|
8148
8365
|
}
|
|
8149
8366
|
};
|
|
8150
8367
|
|
|
8368
|
+
// src/query/builder/dynamic.ts
|
|
8369
|
+
function describeKindContext(entity, kindNames) {
|
|
8370
|
+
if (kindNames === void 0 || kindNames.length === 0) {
|
|
8371
|
+
return `${entity} (kind unresolved)`;
|
|
8372
|
+
}
|
|
8373
|
+
const list = kindNames.map((k) => `"${k}"`).join(" | ");
|
|
8374
|
+
return `${entity} kind${kindNames.length > 1 ? "s" : ""} ${list}`;
|
|
8375
|
+
}
|
|
8376
|
+
function createDynamicFieldBuilder(introspector, alias, name, kindNames, entity) {
|
|
8377
|
+
const typeInfo = kindNames === void 0 ? void 0 : entity === "node" ? introspector.getSharedFieldTypeInfo(kindNames, name) : introspector.getSharedEdgeFieldTypeInfo(kindNames, name);
|
|
8378
|
+
const where = describeKindContext(entity, kindNames);
|
|
8379
|
+
if (typeInfo === void 0) {
|
|
8380
|
+
throw new Error(`Property "${name}" is not declared on ${where}.`);
|
|
8381
|
+
}
|
|
8382
|
+
const ref = fieldRef(alias, ["props"], {
|
|
8383
|
+
jsonPointer: jsonPointer([name]),
|
|
8384
|
+
valueType: typeInfo.valueType,
|
|
8385
|
+
elementType: typeInfo.elementType
|
|
8386
|
+
});
|
|
8387
|
+
const base = buildFieldBuilderForTypeInfo(ref, typeInfo);
|
|
8388
|
+
const expect = (asserted) => {
|
|
8389
|
+
if (typeInfo.valueType !== asserted) {
|
|
8390
|
+
throw new TypeError(
|
|
8391
|
+
`Property "${name}" on ${where} is ${typeInfo.valueType}, not ${asserted}.`
|
|
8392
|
+
);
|
|
8393
|
+
}
|
|
8394
|
+
return base;
|
|
8395
|
+
};
|
|
8396
|
+
return {
|
|
8397
|
+
...base,
|
|
8398
|
+
string: () => expect("string"),
|
|
8399
|
+
number: () => expect("number"),
|
|
8400
|
+
date: () => expect("date"),
|
|
8401
|
+
array: () => expect("array"),
|
|
8402
|
+
object: () => expect("object"),
|
|
8403
|
+
embedding: () => expect("embedding")
|
|
8404
|
+
};
|
|
8405
|
+
}
|
|
8406
|
+
|
|
8151
8407
|
// src/query/builder/executable-aggregate-query.ts
|
|
8152
8408
|
var NOT_COMPUTED2 = /* @__PURE__ */ Symbol("NOT_COMPUTED");
|
|
8153
8409
|
var ExecutableAggregateQuery = class _ExecutableAggregateQuery {
|
|
@@ -8478,6 +8734,21 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8478
8734
|
}
|
|
8479
8735
|
}
|
|
8480
8736
|
};
|
|
8737
|
+
if (this.#state.dynamicEdgeAliases.has(alias)) {
|
|
8738
|
+
return {
|
|
8739
|
+
id: idAccessor,
|
|
8740
|
+
kind: kindAccessor,
|
|
8741
|
+
fromId: fromIdAccessor,
|
|
8742
|
+
toId: toIdAccessor,
|
|
8743
|
+
field: (name) => createDynamicFieldBuilder(
|
|
8744
|
+
this.#config.schemaIntrospector,
|
|
8745
|
+
alias,
|
|
8746
|
+
name,
|
|
8747
|
+
allEdgeKinds,
|
|
8748
|
+
"edge"
|
|
8749
|
+
)
|
|
8750
|
+
};
|
|
8751
|
+
}
|
|
8481
8752
|
return new Proxy({}, {
|
|
8482
8753
|
get: (_, property) => {
|
|
8483
8754
|
if (typeof property === "symbol") return;
|
|
@@ -8495,6 +8766,35 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8495
8766
|
validateSqlIdentifier(alias);
|
|
8496
8767
|
const includeSubClasses = options?.includeSubClasses ?? false;
|
|
8497
8768
|
const kinds = includeSubClasses ? this.#config.registry.expandSubClasses(kind) : [kind];
|
|
8769
|
+
const newState = this.#stateWithTraversal(alias, kinds);
|
|
8770
|
+
return new QueryBuilderClass(this.#config, newState);
|
|
8771
|
+
}
|
|
8772
|
+
/**
|
|
8773
|
+
* String-keyed sibling of `to` for runtime-declared target kinds.
|
|
8774
|
+
* Throws `KindNotFoundError` if the kind is not registered.
|
|
8775
|
+
*/
|
|
8776
|
+
toDynamic(kind, alias, options) {
|
|
8777
|
+
validateSqlIdentifier(alias);
|
|
8778
|
+
if (!this.#config.registry.hasNodeType(kind)) {
|
|
8779
|
+
throw new KindNotFoundError(kind, "node", {
|
|
8780
|
+
graphId: this.#config.graphId
|
|
8781
|
+
});
|
|
8782
|
+
}
|
|
8783
|
+
this.#assertValidEndpoint(kind);
|
|
8784
|
+
const includeSubClasses = options?.includeSubClasses ?? false;
|
|
8785
|
+
const kinds = includeSubClasses ? this.#config.registry.expandSubClasses(kind) : [kind];
|
|
8786
|
+
const baseState = this.#stateWithTraversal(alias, kinds);
|
|
8787
|
+
const newState = {
|
|
8788
|
+
...baseState,
|
|
8789
|
+
dynamicNodeAliases: /* @__PURE__ */ new Set([...baseState.dynamicNodeAliases, alias])
|
|
8790
|
+
};
|
|
8791
|
+
return new QueryBuilderClass(this.#config, newState);
|
|
8792
|
+
}
|
|
8793
|
+
/**
|
|
8794
|
+
* Builds the next `QueryBuilderState` after appending a traversal
|
|
8795
|
+
* targeting `alias` with `kinds`. Shared by `to` and `toDynamic`.
|
|
8796
|
+
*/
|
|
8797
|
+
#stateWithTraversal(alias, kinds) {
|
|
8498
8798
|
const traversalBase = {
|
|
8499
8799
|
edgeAlias: this.#edgeAlias,
|
|
8500
8800
|
edgeKinds: this.#edgeKinds,
|
|
@@ -8520,14 +8820,53 @@ var TraversalBuilder = class _TraversalBuilder {
|
|
|
8520
8820
|
}
|
|
8521
8821
|
}
|
|
8522
8822
|
} : baseTraversal;
|
|
8523
|
-
|
|
8823
|
+
return {
|
|
8524
8824
|
...this.#state,
|
|
8525
8825
|
traversals: [...this.#state.traversals, traversal],
|
|
8526
8826
|
predicates: [...this.#state.predicates, ...this.#pendingEdgePredicates],
|
|
8527
8827
|
currentAlias: alias
|
|
8528
|
-
// Update current alias to this traversal's target
|
|
8529
8828
|
};
|
|
8530
|
-
|
|
8829
|
+
}
|
|
8830
|
+
/**
|
|
8831
|
+
* Asserts a runtime-declared target kind is a valid endpoint for the
|
|
8832
|
+
* current traversal. Mirrors the compile-time `ValidEdgeTargets`
|
|
8833
|
+
* constraint enforced by `to(...)` — without this check `toDynamic`
|
|
8834
|
+
* would silently accept e.g. `traverseDynamic("authoredBy") +
|
|
8835
|
+
* toDynamic("Document")` where authoredBy.to=[Author] and produce an
|
|
8836
|
+
* empty result set.
|
|
8837
|
+
*
|
|
8838
|
+
* Permissive on unions: if the target is assignable to any valid
|
|
8839
|
+
* endpoint across the edges being traversed, accept. Subclass
|
|
8840
|
+
* relationships are honored via `registry.isAssignableTo`.
|
|
8841
|
+
*/
|
|
8842
|
+
#assertValidEndpoint(targetKind) {
|
|
8843
|
+
const expectedKinds = /* @__PURE__ */ new Set();
|
|
8844
|
+
const collectFromEdge = (edgeName, side) => {
|
|
8845
|
+
const edgeType = this.#config.registry.getEdgeType(edgeName);
|
|
8846
|
+
const endpoints = edgeType?.[side];
|
|
8847
|
+
if (!endpoints) return;
|
|
8848
|
+
for (const endpoint of endpoints) {
|
|
8849
|
+
expectedKinds.add(endpoint.kind);
|
|
8850
|
+
}
|
|
8851
|
+
};
|
|
8852
|
+
const forwardSide = this.#direction === "out" ? "to" : "from";
|
|
8853
|
+
const inverseSide = this.#direction === "out" ? "from" : "to";
|
|
8854
|
+
for (const edgeName of this.#edgeKinds) {
|
|
8855
|
+
collectFromEdge(edgeName, forwardSide);
|
|
8856
|
+
}
|
|
8857
|
+
for (const edgeName of this.#inverseEdgeKinds) {
|
|
8858
|
+
collectFromEdge(edgeName, inverseSide);
|
|
8859
|
+
}
|
|
8860
|
+
if (expectedKinds.size === 0) return;
|
|
8861
|
+
for (const expected of expectedKinds) {
|
|
8862
|
+
if (this.#config.registry.isAssignableTo(targetKind, expected)) return;
|
|
8863
|
+
}
|
|
8864
|
+
throw new EndpointError({
|
|
8865
|
+
edgeKind: this.#edgeKinds[0] ?? "(unknown)",
|
|
8866
|
+
endpoint: forwardSide,
|
|
8867
|
+
actualKind: targetKind,
|
|
8868
|
+
expectedKinds: [...expectedKinds]
|
|
8869
|
+
});
|
|
8531
8870
|
}
|
|
8532
8871
|
};
|
|
8533
8872
|
|
|
@@ -8647,6 +8986,30 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8647
8986
|
};
|
|
8648
8987
|
return new _QueryBuilder(this.#config, newState);
|
|
8649
8988
|
}
|
|
8989
|
+
/**
|
|
8990
|
+
* String-keyed sibling of `from` for runtime-declared kinds. Throws
|
|
8991
|
+
* `KindNotFoundError` if the kind is not registered. Predicates use
|
|
8992
|
+
* the `n.field("name").number().gte(...)` discriminator.
|
|
8993
|
+
*/
|
|
8994
|
+
fromDynamic(kind, alias, options) {
|
|
8995
|
+
validateSqlIdentifier(alias);
|
|
8996
|
+
if (!this.#config.registry.hasNodeType(kind)) {
|
|
8997
|
+
throw new KindNotFoundError(kind, "node", {
|
|
8998
|
+
graphId: this.#config.graphId
|
|
8999
|
+
});
|
|
9000
|
+
}
|
|
9001
|
+
const includeSubClasses = options?.includeSubClasses ?? false;
|
|
9002
|
+
const kinds = includeSubClasses ? this.#config.registry.expandSubClasses(kind) : [kind];
|
|
9003
|
+
const newState = {
|
|
9004
|
+
...this.#state,
|
|
9005
|
+
startAlias: alias,
|
|
9006
|
+
currentAlias: alias,
|
|
9007
|
+
startKinds: kinds,
|
|
9008
|
+
includeSubClasses,
|
|
9009
|
+
dynamicNodeAliases: /* @__PURE__ */ new Set([...this.#state.dynamicNodeAliases, alias])
|
|
9010
|
+
};
|
|
9011
|
+
return new _QueryBuilder(this.#config, newState);
|
|
9012
|
+
}
|
|
8650
9013
|
/**
|
|
8651
9014
|
* Adds a WHERE clause for a node.
|
|
8652
9015
|
*/
|
|
@@ -8714,6 +9077,13 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8714
9077
|
false
|
|
8715
9078
|
);
|
|
8716
9079
|
}
|
|
9080
|
+
/**
|
|
9081
|
+
* String-keyed sibling of `traverse` for runtime-declared edge kinds.
|
|
9082
|
+
* Throws `KindNotFoundError` if the edge kind is not registered.
|
|
9083
|
+
*/
|
|
9084
|
+
traverseDynamic(edgeKind, edgeAlias, options) {
|
|
9085
|
+
return this.#beginDynamicTraversal(edgeKind, edgeAlias, false, options);
|
|
9086
|
+
}
|
|
8717
9087
|
optionalTraverse(edgeKind, edgeAlias, options) {
|
|
8718
9088
|
validateSqlIdentifier(edgeAlias);
|
|
8719
9089
|
const direction = options?.direction ?? "out";
|
|
@@ -8737,6 +9107,55 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
8737
9107
|
true
|
|
8738
9108
|
);
|
|
8739
9109
|
}
|
|
9110
|
+
/**
|
|
9111
|
+
* String-keyed sibling of `optionalTraverse` for runtime-declared edge
|
|
9112
|
+
* kinds. LEFT JOIN semantics — non-matching rows produce a null edge
|
|
9113
|
+
* alias instead of dropping. Throws `KindNotFoundError` if the edge
|
|
9114
|
+
* kind is not registered.
|
|
9115
|
+
*/
|
|
9116
|
+
optionalTraverseDynamic(edgeKind, edgeAlias, options) {
|
|
9117
|
+
return this.#beginDynamicTraversal(edgeKind, edgeAlias, true, options);
|
|
9118
|
+
}
|
|
9119
|
+
/**
|
|
9120
|
+
* Shared body for `traverseDynamic` and `optionalTraverseDynamic`.
|
|
9121
|
+
* The only difference is the `optional` flag passed to the
|
|
9122
|
+
* `TraversalBuilder` constructor.
|
|
9123
|
+
*/
|
|
9124
|
+
#beginDynamicTraversal(edgeKind, edgeAlias, optional, options) {
|
|
9125
|
+
validateSqlIdentifier(edgeAlias);
|
|
9126
|
+
if (!this.#config.registry.hasEdgeType(edgeKind)) {
|
|
9127
|
+
throw new KindNotFoundError(edgeKind, "edge", {
|
|
9128
|
+
graphId: this.#config.graphId
|
|
9129
|
+
});
|
|
9130
|
+
}
|
|
9131
|
+
const direction = options?.direction ?? "out";
|
|
9132
|
+
const expansion = options?.expand ?? this.#config.defaultTraversalExpansion;
|
|
9133
|
+
const includeImplyingEdges = expansion === "implying" || expansion === "all";
|
|
9134
|
+
const includeInverseEdges = expansion === "inverse" || expansion === "all";
|
|
9135
|
+
const fromAlias = options?.from ?? this.#state.currentAlias;
|
|
9136
|
+
const edgeKinds = this.#expandTraversalEdgeKinds(
|
|
9137
|
+
edgeKind,
|
|
9138
|
+
includeImplyingEdges
|
|
9139
|
+
);
|
|
9140
|
+
const inverseEdgeKinds = includeInverseEdges ? this.#expandInverseTraversalEdgeKinds(edgeKinds, includeImplyingEdges) : [];
|
|
9141
|
+
const newState = {
|
|
9142
|
+
...this.#state,
|
|
9143
|
+
dynamicEdgeAliases: /* @__PURE__ */ new Set([
|
|
9144
|
+
...this.#state.dynamicEdgeAliases,
|
|
9145
|
+
edgeAlias
|
|
9146
|
+
])
|
|
9147
|
+
};
|
|
9148
|
+
return new TraversalBuilder(
|
|
9149
|
+
this.#config,
|
|
9150
|
+
newState,
|
|
9151
|
+
edgeKinds,
|
|
9152
|
+
edgeAlias,
|
|
9153
|
+
direction,
|
|
9154
|
+
fromAlias,
|
|
9155
|
+
inverseEdgeKinds,
|
|
9156
|
+
optional
|
|
9157
|
+
);
|
|
9158
|
+
}
|
|
8740
9159
|
/**
|
|
8741
9160
|
* Selects fields to return.
|
|
8742
9161
|
*/
|
|
@@ -9046,6 +9465,20 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
9046
9465
|
alias,
|
|
9047
9466
|
() => this.#hasSearchableField(kindNames)
|
|
9048
9467
|
);
|
|
9468
|
+
if (this.#state.dynamicNodeAliases.has(alias)) {
|
|
9469
|
+
return {
|
|
9470
|
+
id: idAccessor,
|
|
9471
|
+
kind: kindAccessor,
|
|
9472
|
+
$fulltext: fulltextAccessor,
|
|
9473
|
+
field: (name) => createDynamicFieldBuilder(
|
|
9474
|
+
this.#config.schemaIntrospector,
|
|
9475
|
+
alias,
|
|
9476
|
+
name,
|
|
9477
|
+
kindNames,
|
|
9478
|
+
"node"
|
|
9479
|
+
)
|
|
9480
|
+
};
|
|
9481
|
+
}
|
|
9049
9482
|
return new Proxy({}, {
|
|
9050
9483
|
get: (_, property) => {
|
|
9051
9484
|
if (typeof property === "symbol") return;
|
|
@@ -9124,6 +9557,21 @@ var QueryBuilder = class _QueryBuilder {
|
|
|
9124
9557
|
const toIdAccessor = stringField(
|
|
9125
9558
|
fieldRef(alias, ["to_id"], { valueType: "string" })
|
|
9126
9559
|
);
|
|
9560
|
+
if (this.#state.dynamicEdgeAliases.has(alias)) {
|
|
9561
|
+
return {
|
|
9562
|
+
id: idAccessor,
|
|
9563
|
+
kind: kindAccessor,
|
|
9564
|
+
fromId: fromIdAccessor,
|
|
9565
|
+
toId: toIdAccessor,
|
|
9566
|
+
field: (name) => createDynamicFieldBuilder(
|
|
9567
|
+
this.#config.schemaIntrospector,
|
|
9568
|
+
alias,
|
|
9569
|
+
name,
|
|
9570
|
+
edgeKindNames,
|
|
9571
|
+
"edge"
|
|
9572
|
+
)
|
|
9573
|
+
};
|
|
9574
|
+
}
|
|
9127
9575
|
return new Proxy({}, {
|
|
9128
9576
|
get: (_, property) => {
|
|
9129
9577
|
if (typeof property === "symbol") return;
|
|
@@ -9543,7 +9991,9 @@ function createQueryBuilder(graphId, registry, options) {
|
|
|
9543
9991
|
asOf: void 0,
|
|
9544
9992
|
groupBy: void 0,
|
|
9545
9993
|
having: void 0,
|
|
9546
|
-
fusion: void 0
|
|
9994
|
+
fusion: void 0,
|
|
9995
|
+
dynamicNodeAliases: /* @__PURE__ */ new Set(),
|
|
9996
|
+
dynamicEdgeAliases: /* @__PURE__ */ new Set()
|
|
9547
9997
|
};
|
|
9548
9998
|
return new QueryBuilder(config, initialState);
|
|
9549
9999
|
}
|
|
@@ -10071,17 +10521,17 @@ function createEdgeCollection(config) {
|
|
|
10071
10521
|
}
|
|
10072
10522
|
const results = Array.from({ length: items.length });
|
|
10073
10523
|
if (toCreate.length > 0) {
|
|
10074
|
-
const createInputs = toCreate.map((
|
|
10524
|
+
const createInputs = toCreate.map((entry2) => entry2.input);
|
|
10075
10525
|
const created = await executeEdgeCreateBatch2(createInputs, target);
|
|
10076
|
-
for (const [index,
|
|
10077
|
-
results[
|
|
10526
|
+
for (const [index, entry2] of toCreate.entries()) {
|
|
10527
|
+
results[entry2.index] = narrowEdge(created[index]);
|
|
10078
10528
|
}
|
|
10079
10529
|
}
|
|
10080
|
-
for (const
|
|
10081
|
-
const result = await executeEdgeUpsertUpdate2(
|
|
10082
|
-
clearDeleted:
|
|
10530
|
+
for (const entry2 of toUpdate) {
|
|
10531
|
+
const result = await executeEdgeUpsertUpdate2(entry2.input, target, {
|
|
10532
|
+
clearDeleted: entry2.clearDeleted
|
|
10083
10533
|
});
|
|
10084
|
-
results[
|
|
10534
|
+
results[entry2.index] = narrowEdge(result);
|
|
10085
10535
|
}
|
|
10086
10536
|
return results;
|
|
10087
10537
|
};
|
|
@@ -10392,17 +10842,17 @@ function createNodeCollection(config) {
|
|
|
10392
10842
|
}
|
|
10393
10843
|
const results = Array.from({ length: items.length });
|
|
10394
10844
|
if (toCreate.length > 0) {
|
|
10395
|
-
const createInputs = toCreate.map((
|
|
10845
|
+
const createInputs = toCreate.map((entry2) => entry2.input);
|
|
10396
10846
|
const created = await executeNodeCreateBatch2(createInputs, target);
|
|
10397
|
-
for (const [index,
|
|
10398
|
-
results[
|
|
10847
|
+
for (const [index, entry2] of toCreate.entries()) {
|
|
10848
|
+
results[entry2.index] = narrowNode(created[index]);
|
|
10399
10849
|
}
|
|
10400
10850
|
}
|
|
10401
|
-
for (const
|
|
10402
|
-
const result = await executeNodeUpsertUpdate2(
|
|
10403
|
-
clearDeleted:
|
|
10851
|
+
for (const entry2 of toUpdate) {
|
|
10852
|
+
const result = await executeNodeUpsertUpdate2(entry2.input, target, {
|
|
10853
|
+
clearDeleted: entry2.clearDeleted
|
|
10404
10854
|
});
|
|
10405
|
-
results[
|
|
10855
|
+
results[entry2.index] = narrowNode(result);
|
|
10406
10856
|
}
|
|
10407
10857
|
return results;
|
|
10408
10858
|
};
|
|
@@ -10498,51 +10948,614 @@ function createNodeCollection(config) {
|
|
|
10498
10948
|
}
|
|
10499
10949
|
};
|
|
10500
10950
|
}
|
|
10501
|
-
|
|
10502
|
-
// src/store/collection-factory.ts
|
|
10503
|
-
function createNodeCollectionsProxy(graph, graphId, registry, backend, operations) {
|
|
10504
|
-
const collectionCache = /* @__PURE__ */ new Map();
|
|
10505
|
-
return new Proxy({}, {
|
|
10506
|
-
get: (_, kind) => {
|
|
10507
|
-
if (!Object.hasOwn(graph.nodes, kind)) {
|
|
10508
|
-
throw new KindNotFoundError(kind, "node");
|
|
10509
|
-
}
|
|
10510
|
-
const cached = collectionCache.get(kind);
|
|
10511
|
-
if (cached !== void 0) {
|
|
10512
|
-
return cached;
|
|
10951
|
+
|
|
10952
|
+
// src/store/collection-factory.ts
|
|
10953
|
+
function createNodeCollectionsProxy(graph, graphId, registry, backend, operations) {
|
|
10954
|
+
const collectionCache = /* @__PURE__ */ new Map();
|
|
10955
|
+
return new Proxy({}, {
|
|
10956
|
+
get: (_, kind) => {
|
|
10957
|
+
if (!Object.hasOwn(graph.nodes, kind)) {
|
|
10958
|
+
throw new KindNotFoundError(kind, "node");
|
|
10959
|
+
}
|
|
10960
|
+
const cached = collectionCache.get(kind);
|
|
10961
|
+
if (cached !== void 0) {
|
|
10962
|
+
return cached;
|
|
10963
|
+
}
|
|
10964
|
+
const collection = createNodeCollection({
|
|
10965
|
+
graphId,
|
|
10966
|
+
kind,
|
|
10967
|
+
backend,
|
|
10968
|
+
...operations
|
|
10969
|
+
});
|
|
10970
|
+
collectionCache.set(kind, collection);
|
|
10971
|
+
return collection;
|
|
10972
|
+
}
|
|
10973
|
+
});
|
|
10974
|
+
}
|
|
10975
|
+
function createEdgeCollectionsProxy(graph, graphId, registry, backend, operations) {
|
|
10976
|
+
const collectionCache = /* @__PURE__ */ new Map();
|
|
10977
|
+
return new Proxy({}, {
|
|
10978
|
+
get: (_, kind) => {
|
|
10979
|
+
if (!Object.hasOwn(graph.edges, kind)) {
|
|
10980
|
+
throw new KindNotFoundError(kind, "edge");
|
|
10981
|
+
}
|
|
10982
|
+
const cached = collectionCache.get(kind);
|
|
10983
|
+
if (cached !== void 0) {
|
|
10984
|
+
return cached;
|
|
10985
|
+
}
|
|
10986
|
+
const collection = createEdgeCollection({
|
|
10987
|
+
graphId,
|
|
10988
|
+
kind,
|
|
10989
|
+
backend,
|
|
10990
|
+
...operations
|
|
10991
|
+
});
|
|
10992
|
+
collectionCache.set(kind, collection);
|
|
10993
|
+
return collection;
|
|
10994
|
+
}
|
|
10995
|
+
});
|
|
10996
|
+
}
|
|
10997
|
+
|
|
10998
|
+
// src/store/introspect.ts
|
|
10999
|
+
function introspectSchema(graph, context) {
|
|
11000
|
+
const extension = graph.extension;
|
|
11001
|
+
const { nodes: runtimeNodeNames, edges: runtimeEdgeNames } = extensionKindNames(extension);
|
|
11002
|
+
const deprecated = graph.deprecatedKinds;
|
|
11003
|
+
const kinds = [];
|
|
11004
|
+
for (const [name, registration] of Object.entries(graph.nodes)) {
|
|
11005
|
+
const reg = registration;
|
|
11006
|
+
kinds.push({
|
|
11007
|
+
name,
|
|
11008
|
+
origin: runtimeNodeNames.has(name) ? "runtime" : "compile-time",
|
|
11009
|
+
description: reg.type.description,
|
|
11010
|
+
annotations: reg.type.annotations,
|
|
11011
|
+
deprecated: deprecated.has(name),
|
|
11012
|
+
properties: serializeSchemaProperties(reg.type.schema),
|
|
11013
|
+
unique: (reg.unique ?? []).map((constraint) => ({
|
|
11014
|
+
name: constraint.name,
|
|
11015
|
+
fields: [...constraint.fields],
|
|
11016
|
+
scope: constraint.scope,
|
|
11017
|
+
collation: constraint.collation
|
|
11018
|
+
}))
|
|
11019
|
+
});
|
|
11020
|
+
}
|
|
11021
|
+
const edges = [];
|
|
11022
|
+
for (const [name, registration] of Object.entries(graph.edges)) {
|
|
11023
|
+
const reg = registration;
|
|
11024
|
+
const edgeType = reg.type;
|
|
11025
|
+
edges.push({
|
|
11026
|
+
name,
|
|
11027
|
+
origin: runtimeEdgeNames.has(name) ? "runtime" : "compile-time",
|
|
11028
|
+
description: edgeType.description,
|
|
11029
|
+
from: reg.from.map((entry2) => entry2.kind),
|
|
11030
|
+
to: reg.to.map((entry2) => entry2.kind),
|
|
11031
|
+
cardinality: reg.cardinality ?? "many",
|
|
11032
|
+
endpointExistence: reg.endpointExistence ?? "notDeleted",
|
|
11033
|
+
properties: serializeSchemaProperties(edgeType.schema),
|
|
11034
|
+
annotations: edgeType.annotations,
|
|
11035
|
+
deprecated: deprecated.has(name)
|
|
11036
|
+
});
|
|
11037
|
+
}
|
|
11038
|
+
const runtimeOntologyKeys = buildGraphExtensionOntologyKeySet(extension);
|
|
11039
|
+
const ontology = graph.ontology.map((relation) => ({
|
|
11040
|
+
metaEdge: relation.metaEdge.name,
|
|
11041
|
+
from: getTypeName(relation.from),
|
|
11042
|
+
to: getTypeName(relation.to),
|
|
11043
|
+
origin: runtimeOntologyKeys.has(compileTimeOntologyKey(relation)) ? "runtime" : "compile-time"
|
|
11044
|
+
}));
|
|
11045
|
+
return {
|
|
11046
|
+
graphId: context.graphId,
|
|
11047
|
+
schemaVersion: context.schemaVersion,
|
|
11048
|
+
schemaHash: context.schemaHash,
|
|
11049
|
+
kinds,
|
|
11050
|
+
edges,
|
|
11051
|
+
ontology,
|
|
11052
|
+
deprecatedKinds: new Set(deprecated),
|
|
11053
|
+
extension
|
|
11054
|
+
};
|
|
11055
|
+
}
|
|
11056
|
+
|
|
11057
|
+
// src/store/materialize-shared.ts
|
|
11058
|
+
async function ensureFocusedStatusTable(backend, ensureFocused) {
|
|
11059
|
+
if (ensureFocused !== void 0) {
|
|
11060
|
+
await ensureFocused();
|
|
11061
|
+
return;
|
|
11062
|
+
}
|
|
11063
|
+
await backend.bootstrapTables?.();
|
|
11064
|
+
}
|
|
11065
|
+
async function runMaterialization(items, options, runOne) {
|
|
11066
|
+
if (options.stopOnError === true) {
|
|
11067
|
+
const results = [];
|
|
11068
|
+
for (const item of items) {
|
|
11069
|
+
const entry2 = await runOne(item);
|
|
11070
|
+
results.push(entry2);
|
|
11071
|
+
if (entry2.status === "failed") break;
|
|
11072
|
+
}
|
|
11073
|
+
return results;
|
|
11074
|
+
}
|
|
11075
|
+
return Promise.all(items.map((item) => runOne(item)));
|
|
11076
|
+
}
|
|
11077
|
+
|
|
11078
|
+
// src/store/materialize-indexes.ts
|
|
11079
|
+
async function materializeIndexes(context, options = {}) {
|
|
11080
|
+
const { graph, graphId, backend, schemaVersion } = context;
|
|
11081
|
+
if (backend.executeDdl === void 0) {
|
|
11082
|
+
throw new ConfigurationError(
|
|
11083
|
+
"store.materializeIndexes() requires a backend with `executeDdl` support. The bundled SQLite and Postgres backends provide it; a custom backend without `executeDdl` cannot run index DDL.",
|
|
11084
|
+
{ code: "MATERIALIZE_BACKEND_UNSUPPORTED" }
|
|
11085
|
+
);
|
|
11086
|
+
}
|
|
11087
|
+
if (backend.getIndexMaterialization === void 0 || backend.recordIndexMaterialization === void 0) {
|
|
11088
|
+
throw new ConfigurationError(
|
|
11089
|
+
"store.materializeIndexes() requires a backend with index materialization status primitives. The bundled SQLite and Postgres backends provide them; a custom backend must implement `getIndexMaterialization` and `recordIndexMaterialization`.",
|
|
11090
|
+
{ code: "MATERIALIZE_BACKEND_UNSUPPORTED" }
|
|
11091
|
+
);
|
|
11092
|
+
}
|
|
11093
|
+
const declared = graph.indexes ?? [];
|
|
11094
|
+
const kindFilter = options.kinds === void 0 ? void 0 : new Set(options.kinds);
|
|
11095
|
+
if (kindFilter !== void 0) {
|
|
11096
|
+
for (const name of kindFilter) {
|
|
11097
|
+
if (!isKnownKind(graph, name)) {
|
|
11098
|
+
throw new KindNotFoundError(
|
|
11099
|
+
name,
|
|
11100
|
+
Object.hasOwn(graph.edges, name) ? "edge" : "node",
|
|
11101
|
+
{
|
|
11102
|
+
graphId,
|
|
11103
|
+
suggestion: "Only kinds declared on the graph (compile-time or runtime) can be passed to materializeIndexes."
|
|
11104
|
+
}
|
|
11105
|
+
);
|
|
11106
|
+
}
|
|
11107
|
+
}
|
|
11108
|
+
}
|
|
11109
|
+
const candidates = declared.filter(
|
|
11110
|
+
(index) => kindFilter === void 0 ? true : kindFilter.has(index.kind)
|
|
11111
|
+
);
|
|
11112
|
+
if (candidates.length === 0) {
|
|
11113
|
+
return { results: [] };
|
|
11114
|
+
}
|
|
11115
|
+
await ensureFocusedStatusTable(
|
|
11116
|
+
backend,
|
|
11117
|
+
backend.ensureIndexMaterializationsTable
|
|
11118
|
+
);
|
|
11119
|
+
const dialect = backend.dialect;
|
|
11120
|
+
const tableNames = backend.tableNames;
|
|
11121
|
+
const ddlOptions = {
|
|
11122
|
+
ifNotExists: true,
|
|
11123
|
+
concurrent: dialect === "postgres",
|
|
11124
|
+
...tableNames?.nodes === void 0 ? {} : { nodesTableName: tableNames.nodes },
|
|
11125
|
+
...tableNames?.edges === void 0 ? {} : { edgesTableName: tableNames.edges }
|
|
11126
|
+
};
|
|
11127
|
+
const statusKeys = candidates.map(
|
|
11128
|
+
(declaration) => statusKeyFor(declaration, graphId)
|
|
11129
|
+
);
|
|
11130
|
+
const existingByStatusKey = await preloadMaterializations(
|
|
11131
|
+
backend,
|
|
11132
|
+
statusKeys
|
|
11133
|
+
);
|
|
11134
|
+
const materializeEntry = (declaration) => declaration.entity === "vector" ? materializeVectorIndex(
|
|
11135
|
+
declaration,
|
|
11136
|
+
backend,
|
|
11137
|
+
graphId,
|
|
11138
|
+
schemaVersion,
|
|
11139
|
+
existingByStatusKey
|
|
11140
|
+
) : materializeRelationalIndex(
|
|
11141
|
+
declaration,
|
|
11142
|
+
backend,
|
|
11143
|
+
dialect,
|
|
11144
|
+
ddlOptions,
|
|
11145
|
+
graphId,
|
|
11146
|
+
schemaVersion,
|
|
11147
|
+
existingByStatusKey
|
|
11148
|
+
);
|
|
11149
|
+
if (options.stopOnError === true) {
|
|
11150
|
+
const results = [];
|
|
11151
|
+
for (const declaration of candidates) {
|
|
11152
|
+
const entry2 = await materializeEntry(declaration);
|
|
11153
|
+
results.push(entry2);
|
|
11154
|
+
if (entry2.status === "failed") break;
|
|
11155
|
+
}
|
|
11156
|
+
return { results };
|
|
11157
|
+
}
|
|
11158
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
11159
|
+
for (const declaration of candidates) {
|
|
11160
|
+
const key = parallelBucketKey(declaration);
|
|
11161
|
+
const list = buckets.get(key);
|
|
11162
|
+
if (list === void 0) buckets.set(key, [declaration]);
|
|
11163
|
+
else list.push(declaration);
|
|
11164
|
+
}
|
|
11165
|
+
const bucketResults = await Promise.all(
|
|
11166
|
+
[...buckets.values()].map(async (group) => {
|
|
11167
|
+
const out = [];
|
|
11168
|
+
for (const declaration of group) {
|
|
11169
|
+
out.push(await materializeEntry(declaration));
|
|
11170
|
+
}
|
|
11171
|
+
return out;
|
|
11172
|
+
})
|
|
11173
|
+
);
|
|
11174
|
+
const byDeclaration = /* @__PURE__ */ new Map();
|
|
11175
|
+
let bucketIndex = 0;
|
|
11176
|
+
for (const group of buckets.values()) {
|
|
11177
|
+
const entries = bucketResults[bucketIndex];
|
|
11178
|
+
for (const [declarationIndex, declaration] of group.entries()) {
|
|
11179
|
+
byDeclaration.set(declaration, entries[declarationIndex]);
|
|
11180
|
+
}
|
|
11181
|
+
bucketIndex += 1;
|
|
11182
|
+
}
|
|
11183
|
+
return {
|
|
11184
|
+
results: candidates.map((declaration) => byDeclaration.get(declaration))
|
|
11185
|
+
};
|
|
11186
|
+
}
|
|
11187
|
+
function parallelBucketKey(declaration) {
|
|
11188
|
+
return declaration.entity;
|
|
11189
|
+
}
|
|
11190
|
+
async function materializeRelationalIndex(declaration, backend, dialect, ddlOptions, graphId, schemaVersion, existingByStatusKey) {
|
|
11191
|
+
const ddl = generateIndexDDL(declaration, dialect, ddlOptions);
|
|
11192
|
+
const targetTable = declaration.entity === "node" ? ddlOptions.nodesTableName ?? "typegraph_nodes" : ddlOptions.edgesTableName ?? "typegraph_edges";
|
|
11193
|
+
const signature = await computeIndexSignature(
|
|
11194
|
+
dialect,
|
|
11195
|
+
targetTable,
|
|
11196
|
+
declaration
|
|
11197
|
+
);
|
|
11198
|
+
return materializeOne(declaration, backend, graphId, schemaVersion, {
|
|
11199
|
+
statusKey: declaration.name,
|
|
11200
|
+
signature,
|
|
11201
|
+
driftLabel: "Index",
|
|
11202
|
+
run: () => backend.executeDdl(ddl),
|
|
11203
|
+
existingByStatusKey
|
|
11204
|
+
});
|
|
11205
|
+
}
|
|
11206
|
+
async function materializeVectorIndex(declaration, backend, graphId, schemaVersion, existingByStatusKey) {
|
|
11207
|
+
if (declaration.indexType === "none") {
|
|
11208
|
+
return skippedEntry(
|
|
11209
|
+
declaration,
|
|
11210
|
+
"indexType: 'none' opts out of automatic materialization"
|
|
11211
|
+
);
|
|
11212
|
+
}
|
|
11213
|
+
const vectorCapability = backend.capabilities.vector;
|
|
11214
|
+
if (vectorCapability?.supported !== true || backend.createVectorIndex === void 0) {
|
|
11215
|
+
return skippedEntry(
|
|
11216
|
+
declaration,
|
|
11217
|
+
`Backend (${backend.dialect}) does not support vector indexes in its current configuration`
|
|
11218
|
+
);
|
|
11219
|
+
}
|
|
11220
|
+
if (!vectorCapability.indexTypes.includes(declaration.indexType)) {
|
|
11221
|
+
return skippedEntry(
|
|
11222
|
+
declaration,
|
|
11223
|
+
`Backend (${backend.dialect}) does not support index type "${declaration.indexType}" for vector indexes; supported: ${vectorCapability.indexTypes.join(", ") || "(none)"}`
|
|
11224
|
+
);
|
|
11225
|
+
}
|
|
11226
|
+
const embeddingsTable = backend.tableNames?.embeddings ?? "typegraph_node_embeddings";
|
|
11227
|
+
const signature = await computeIndexSignature(
|
|
11228
|
+
backend.dialect,
|
|
11229
|
+
embeddingsTable,
|
|
11230
|
+
declaration
|
|
11231
|
+
);
|
|
11232
|
+
const params = {
|
|
11233
|
+
graphId,
|
|
11234
|
+
nodeKind: declaration.kind,
|
|
11235
|
+
fieldPath: declaration.fieldPath,
|
|
11236
|
+
dimensions: declaration.dimensions,
|
|
11237
|
+
metric: declaration.metric,
|
|
11238
|
+
indexType: declaration.indexType,
|
|
11239
|
+
indexParams: {
|
|
11240
|
+
m: declaration.indexParams.m,
|
|
11241
|
+
efConstruction: declaration.indexParams.efConstruction,
|
|
11242
|
+
...declaration.indexParams.lists === void 0 ? {} : { lists: declaration.indexParams.lists }
|
|
11243
|
+
},
|
|
11244
|
+
concurrent: backend.dialect === "postgres"
|
|
11245
|
+
};
|
|
11246
|
+
return materializeOne(declaration, backend, graphId, schemaVersion, {
|
|
11247
|
+
// Compound status-table key for vector entries. Pgvector creates
|
|
11248
|
+
// one physical index per (graphId, kind, field) — so the
|
|
11249
|
+
// per-deployment status table needs to disambiguate entries that
|
|
11250
|
+
// SHARE a declaration name but belong to different graphs.
|
|
11251
|
+
// Applied uniformly to auto-derived AND explicit declarations.
|
|
11252
|
+
statusKey: vectorStatusKey(graphId, declaration.name),
|
|
11253
|
+
signature,
|
|
11254
|
+
driftLabel: "Vector index",
|
|
11255
|
+
run: () => backend.createVectorIndex(params),
|
|
11256
|
+
existingByStatusKey
|
|
11257
|
+
});
|
|
11258
|
+
}
|
|
11259
|
+
async function materializeOne(declaration, backend, graphId, schemaVersion, action) {
|
|
11260
|
+
const recordIndexMaterialization = backend.recordIndexMaterialization;
|
|
11261
|
+
const { statusKey, signature, driftLabel, run, existingByStatusKey } = action;
|
|
11262
|
+
const statusOverride = statusKey === declaration.name ? void 0 : { statusName: statusKey };
|
|
11263
|
+
const existing = existingByStatusKey.get(statusKey);
|
|
11264
|
+
if (existing?.materializedAt !== void 0) {
|
|
11265
|
+
if (existing.signature === signature) {
|
|
11266
|
+
return entry(declaration, "alreadyMaterialized");
|
|
11267
|
+
}
|
|
11268
|
+
const error = new Error(
|
|
11269
|
+
`${driftLabel} "${declaration.name}" already materialized with a different signature (recorded by graph "${existing.graphId}" at version ${existing.schemaVersion}). Drop the index manually and retry, or rename the new declaration.`
|
|
11270
|
+
);
|
|
11271
|
+
await recordIndexMaterialization(
|
|
11272
|
+
buildAttempt({
|
|
11273
|
+
declaration,
|
|
11274
|
+
graphId,
|
|
11275
|
+
signature,
|
|
11276
|
+
schemaVersion,
|
|
11277
|
+
materializedAt: void 0,
|
|
11278
|
+
error,
|
|
11279
|
+
...statusOverride
|
|
11280
|
+
})
|
|
11281
|
+
);
|
|
11282
|
+
return entry(declaration, "failed", error);
|
|
11283
|
+
}
|
|
11284
|
+
try {
|
|
11285
|
+
await run();
|
|
11286
|
+
const attemptedAt = nowIso();
|
|
11287
|
+
await recordIndexMaterialization(
|
|
11288
|
+
buildAttempt({
|
|
11289
|
+
declaration,
|
|
11290
|
+
graphId,
|
|
11291
|
+
signature,
|
|
11292
|
+
schemaVersion,
|
|
11293
|
+
materializedAt: attemptedAt,
|
|
11294
|
+
error: void 0,
|
|
11295
|
+
attemptedAt,
|
|
11296
|
+
...statusOverride
|
|
11297
|
+
})
|
|
11298
|
+
);
|
|
11299
|
+
return entry(declaration, "created");
|
|
11300
|
+
} catch (error_) {
|
|
11301
|
+
const error = error_ instanceof Error ? error_ : new Error(String(error_));
|
|
11302
|
+
await recordIndexMaterialization(
|
|
11303
|
+
buildAttempt({
|
|
11304
|
+
declaration,
|
|
11305
|
+
graphId,
|
|
11306
|
+
signature,
|
|
11307
|
+
schemaVersion,
|
|
11308
|
+
materializedAt: void 0,
|
|
11309
|
+
error,
|
|
11310
|
+
...statusOverride
|
|
11311
|
+
})
|
|
11312
|
+
);
|
|
11313
|
+
return entry(declaration, "failed", error);
|
|
11314
|
+
}
|
|
11315
|
+
}
|
|
11316
|
+
function entry(declaration, status, error) {
|
|
11317
|
+
return {
|
|
11318
|
+
indexName: declaration.name,
|
|
11319
|
+
entity: declaration.entity,
|
|
11320
|
+
kind: declaration.kind,
|
|
11321
|
+
status,
|
|
11322
|
+
...error === void 0 ? {} : { error }
|
|
11323
|
+
};
|
|
11324
|
+
}
|
|
11325
|
+
function skippedEntry(declaration, reason) {
|
|
11326
|
+
return {
|
|
11327
|
+
indexName: declaration.name,
|
|
11328
|
+
entity: "vector",
|
|
11329
|
+
kind: declaration.kind,
|
|
11330
|
+
status: "skipped",
|
|
11331
|
+
reason
|
|
11332
|
+
};
|
|
11333
|
+
}
|
|
11334
|
+
function vectorStatusKey(graphId, declarationName) {
|
|
11335
|
+
return `${graphId}::${declarationName}`;
|
|
11336
|
+
}
|
|
11337
|
+
function statusKeyFor(declaration, graphId) {
|
|
11338
|
+
return declaration.entity === "vector" ? vectorStatusKey(graphId, declaration.name) : declaration.name;
|
|
11339
|
+
}
|
|
11340
|
+
async function preloadMaterializations(backend, statusKeys) {
|
|
11341
|
+
const map = /* @__PURE__ */ new Map();
|
|
11342
|
+
if (statusKeys.length === 0) return map;
|
|
11343
|
+
if (backend.getIndexMaterializations !== void 0) {
|
|
11344
|
+
const rows2 = await backend.getIndexMaterializations(statusKeys);
|
|
11345
|
+
for (const row of rows2) map.set(row.indexName, row);
|
|
11346
|
+
return map;
|
|
11347
|
+
}
|
|
11348
|
+
const getOne = backend.getIndexMaterialization;
|
|
11349
|
+
const rows = await Promise.all(statusKeys.map((key) => getOne(key)));
|
|
11350
|
+
for (const [index, row] of rows.entries()) {
|
|
11351
|
+
if (row !== void 0) map.set(statusKeys[index], row);
|
|
11352
|
+
}
|
|
11353
|
+
return map;
|
|
11354
|
+
}
|
|
11355
|
+
function buildAttempt(args) {
|
|
11356
|
+
return {
|
|
11357
|
+
indexName: args.statusName ?? args.declaration.name,
|
|
11358
|
+
graphId: args.graphId,
|
|
11359
|
+
entity: args.declaration.entity,
|
|
11360
|
+
kind: args.declaration.kind,
|
|
11361
|
+
signature: args.signature,
|
|
11362
|
+
schemaVersion: args.schemaVersion,
|
|
11363
|
+
attemptedAt: args.attemptedAt ?? nowIso(),
|
|
11364
|
+
materializedAt: args.materializedAt,
|
|
11365
|
+
error: args.error?.message
|
|
11366
|
+
};
|
|
11367
|
+
}
|
|
11368
|
+
async function computeIndexSignature(dialect, targetTableName, declaration) {
|
|
11369
|
+
const hashable = { dialect, targetTableName, declaration };
|
|
11370
|
+
const json = JSON.stringify(hashable, sortedReplacer);
|
|
11371
|
+
return sha256Hex(json, 16);
|
|
11372
|
+
}
|
|
11373
|
+
|
|
11374
|
+
// src/store/materialize-removals.ts
|
|
11375
|
+
var ENTITY_KINDS = ["node", "edge"];
|
|
11376
|
+
function buildPendingKindRemoval(args) {
|
|
11377
|
+
return { ...args, removedAt: void 0, error: void 0 };
|
|
11378
|
+
}
|
|
11379
|
+
async function materializeRemovals(context, options = {}) {
|
|
11380
|
+
const { backend, graphId } = context;
|
|
11381
|
+
if (backend.executeDdl === void 0 || backend.recordKindRemoval === void 0 || backend.getPendingKindRemovals === void 0) {
|
|
11382
|
+
throw new ConfigurationError(
|
|
11383
|
+
"store.materializeRemovals() requires a backend with kind-removal primitives (executeDdl, recordKindRemoval, getPendingKindRemovals). The bundled SQLite and Postgres backends provide them.",
|
|
11384
|
+
{ code: "MATERIALIZE_REMOVALS_BACKEND_UNSUPPORTED" }
|
|
11385
|
+
);
|
|
11386
|
+
}
|
|
11387
|
+
await ensureFocusedStatusTable(backend, backend.ensureKindRemovalsTable);
|
|
11388
|
+
await reconcilePendingRemovals(context);
|
|
11389
|
+
const pending = await backend.getPendingKindRemovals(graphId);
|
|
11390
|
+
const kindFilter = options.kinds === void 0 ? void 0 : new Set(options.kinds);
|
|
11391
|
+
const candidates = pending.filter(
|
|
11392
|
+
(row) => kindFilter === void 0 ? true : kindFilter.has(row.kindName)
|
|
11393
|
+
);
|
|
11394
|
+
if (candidates.length === 0) return { results: [] };
|
|
11395
|
+
const tableNames = backend.tableNames;
|
|
11396
|
+
const nodesTable = tableNames?.nodes ?? "typegraph_nodes";
|
|
11397
|
+
const edgesTable = tableNames?.edges ?? "typegraph_edges";
|
|
11398
|
+
const embeddingsTable = tableNames?.embeddings ?? "typegraph_node_embeddings";
|
|
11399
|
+
const fulltextTable = tableNames?.fulltext ?? "typegraph_node_fulltext";
|
|
11400
|
+
const uniquesTable = tableNames?.uniques ?? "typegraph_node_uniques";
|
|
11401
|
+
const ctx = {
|
|
11402
|
+
backend,
|
|
11403
|
+
graphId,
|
|
11404
|
+
nodesTable,
|
|
11405
|
+
edgesTable,
|
|
11406
|
+
embeddingsTable,
|
|
11407
|
+
fulltextTable,
|
|
11408
|
+
uniquesTable
|
|
11409
|
+
};
|
|
11410
|
+
return {
|
|
11411
|
+
results: await runMaterialization(
|
|
11412
|
+
candidates,
|
|
11413
|
+
options,
|
|
11414
|
+
(removal) => materializeOne2(removal, ctx)
|
|
11415
|
+
)
|
|
11416
|
+
};
|
|
11417
|
+
}
|
|
11418
|
+
async function reconcilePendingRemovals(context) {
|
|
11419
|
+
const { backend, graphId } = context;
|
|
11420
|
+
const recordKindRemoval = backend.recordKindRemoval;
|
|
11421
|
+
if (recordKindRemoval === void 0) return;
|
|
11422
|
+
const activeRow = await backend.getActiveSchema(graphId);
|
|
11423
|
+
if (activeRow === void 0 || activeRow.version <= 1) return;
|
|
11424
|
+
if (backend.ensureReconciliationMarkersTable !== void 0) {
|
|
11425
|
+
await backend.ensureReconciliationMarkersTable();
|
|
11426
|
+
}
|
|
11427
|
+
const marker = backend.getReconciliationMarker === void 0 ? void 0 : await backend.getReconciliationMarker(graphId);
|
|
11428
|
+
if (marker !== void 0 && marker >= activeRow.version) return;
|
|
11429
|
+
const allRemovals = await (backend.getAllKindRemovals === void 0 ? backend.getPendingKindRemovals(graphId) : backend.getAllKindRemovals(graphId));
|
|
11430
|
+
const recorded = new Set(
|
|
11431
|
+
allRemovals.map(
|
|
11432
|
+
(row) => kindRemovalKey(row.entity, row.kindName, row.schemaVersion)
|
|
11433
|
+
)
|
|
11434
|
+
);
|
|
11435
|
+
const reconciliations = [];
|
|
11436
|
+
const stopAtVersion = marker ?? 1;
|
|
11437
|
+
let currentRow = activeRow;
|
|
11438
|
+
let currentSchema = parseSerializedSchema(activeRow.schema_doc);
|
|
11439
|
+
while (currentRow.version > stopAtVersion) {
|
|
11440
|
+
const priorRow = await backend.getSchemaVersion(
|
|
11441
|
+
graphId,
|
|
11442
|
+
currentRow.version - 1
|
|
11443
|
+
);
|
|
11444
|
+
if (priorRow === void 0) break;
|
|
11445
|
+
const priorSchema = parseSerializedSchema(priorRow.schema_doc);
|
|
11446
|
+
for (const entity of ENTITY_KINDS) {
|
|
11447
|
+
const priorEntries = priorSchema[entity === "node" ? "nodes" : "edges"];
|
|
11448
|
+
const currentEntries = currentSchema[entity === "node" ? "nodes" : "edges"];
|
|
11449
|
+
for (const kindName of Object.keys(priorEntries)) {
|
|
11450
|
+
if (kindName in currentEntries) continue;
|
|
11451
|
+
if (recorded.has(kindRemovalKey(entity, kindName, currentRow.version))) {
|
|
11452
|
+
continue;
|
|
11453
|
+
}
|
|
11454
|
+
reconciliations.push({
|
|
11455
|
+
kindName,
|
|
11456
|
+
entity,
|
|
11457
|
+
schemaVersion: currentRow.version
|
|
11458
|
+
});
|
|
10513
11459
|
}
|
|
10514
|
-
const collection = createNodeCollection({
|
|
10515
|
-
graphId,
|
|
10516
|
-
kind,
|
|
10517
|
-
backend,
|
|
10518
|
-
...operations
|
|
10519
|
-
});
|
|
10520
|
-
collectionCache.set(kind, collection);
|
|
10521
|
-
return collection;
|
|
10522
11460
|
}
|
|
10523
|
-
|
|
11461
|
+
currentRow = priorRow;
|
|
11462
|
+
currentSchema = priorSchema;
|
|
11463
|
+
}
|
|
11464
|
+
if (reconciliations.length > 0) {
|
|
11465
|
+
const attemptedAt = nowIso();
|
|
11466
|
+
await Promise.all(
|
|
11467
|
+
reconciliations.map(
|
|
11468
|
+
(entry2) => recordKindRemoval(
|
|
11469
|
+
buildPendingKindRemoval({
|
|
11470
|
+
graphId,
|
|
11471
|
+
kindName: entry2.kindName,
|
|
11472
|
+
entity: entry2.entity,
|
|
11473
|
+
schemaVersion: entry2.schemaVersion,
|
|
11474
|
+
attemptedAt
|
|
11475
|
+
})
|
|
11476
|
+
)
|
|
11477
|
+
)
|
|
11478
|
+
);
|
|
11479
|
+
}
|
|
11480
|
+
if (backend.setReconciliationMarker !== void 0) {
|
|
11481
|
+
await backend.setReconciliationMarker(graphId, activeRow.version);
|
|
11482
|
+
}
|
|
10524
11483
|
}
|
|
10525
|
-
function
|
|
10526
|
-
|
|
10527
|
-
|
|
10528
|
-
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10536
|
-
|
|
10537
|
-
|
|
10538
|
-
|
|
10539
|
-
|
|
10540
|
-
|
|
10541
|
-
|
|
10542
|
-
|
|
10543
|
-
|
|
11484
|
+
function kindRemovalKey(entity, kindName, version) {
|
|
11485
|
+
return `${entity}|${kindName}|${version}`;
|
|
11486
|
+
}
|
|
11487
|
+
async function materializeOne2(row, ctx) {
|
|
11488
|
+
const executeDdl = ctx.backend.executeDdl;
|
|
11489
|
+
const recordKindRemoval = ctx.backend.recordKindRemoval;
|
|
11490
|
+
try {
|
|
11491
|
+
const deletes = [];
|
|
11492
|
+
const graphLit = literal2(ctx.graphId);
|
|
11493
|
+
const kindLit = literal2(row.kindName);
|
|
11494
|
+
if (row.entity === "node") {
|
|
11495
|
+
deletes.push(
|
|
11496
|
+
executeDdl(
|
|
11497
|
+
`DELETE FROM ${quote(ctx.nodesTable)} WHERE graph_id = ${graphLit} AND kind = ${kindLit}`
|
|
11498
|
+
),
|
|
11499
|
+
executeDdl(
|
|
11500
|
+
`DELETE FROM ${quote(ctx.edgesTable)} WHERE graph_id = ${graphLit} AND (from_kind = ${kindLit} OR to_kind = ${kindLit})`
|
|
11501
|
+
),
|
|
11502
|
+
executeDdl(
|
|
11503
|
+
`DELETE FROM ${quote(ctx.embeddingsTable)} WHERE graph_id = ${graphLit} AND node_kind = ${kindLit}`
|
|
11504
|
+
),
|
|
11505
|
+
executeDdl(
|
|
11506
|
+
`DELETE FROM ${quote(ctx.fulltextTable)} WHERE graph_id = ${graphLit} AND node_kind = ${kindLit}`
|
|
11507
|
+
),
|
|
11508
|
+
executeDdl(
|
|
11509
|
+
`DELETE FROM ${quote(ctx.uniquesTable)} WHERE graph_id = ${graphLit} AND node_kind = ${kindLit}`
|
|
11510
|
+
)
|
|
11511
|
+
);
|
|
11512
|
+
} else {
|
|
11513
|
+
deletes.push(
|
|
11514
|
+
executeDdl(
|
|
11515
|
+
`DELETE FROM ${quote(ctx.edgesTable)} WHERE graph_id = ${graphLit} AND kind = ${kindLit}`
|
|
11516
|
+
)
|
|
11517
|
+
);
|
|
10544
11518
|
}
|
|
10545
|
-
|
|
11519
|
+
await Promise.all(deletes);
|
|
11520
|
+
const removedAt = nowIso();
|
|
11521
|
+
await recordKindRemoval({
|
|
11522
|
+
graphId: ctx.graphId,
|
|
11523
|
+
kindName: row.kindName,
|
|
11524
|
+
entity: row.entity,
|
|
11525
|
+
schemaVersion: row.schemaVersion,
|
|
11526
|
+
attemptedAt: removedAt,
|
|
11527
|
+
removedAt,
|
|
11528
|
+
error: void 0
|
|
11529
|
+
});
|
|
11530
|
+
return {
|
|
11531
|
+
kind: row.kindName,
|
|
11532
|
+
entity: row.entity,
|
|
11533
|
+
status: "removed"
|
|
11534
|
+
};
|
|
11535
|
+
} catch (error_) {
|
|
11536
|
+
const error = error_ instanceof Error ? error_ : new Error(String(error_));
|
|
11537
|
+
await recordKindRemoval({
|
|
11538
|
+
graphId: ctx.graphId,
|
|
11539
|
+
kindName: row.kindName,
|
|
11540
|
+
entity: row.entity,
|
|
11541
|
+
schemaVersion: row.schemaVersion,
|
|
11542
|
+
attemptedAt: nowIso(),
|
|
11543
|
+
removedAt: void 0,
|
|
11544
|
+
error: error.message
|
|
11545
|
+
});
|
|
11546
|
+
return {
|
|
11547
|
+
kind: row.kindName,
|
|
11548
|
+
entity: row.entity,
|
|
11549
|
+
status: "failed",
|
|
11550
|
+
error
|
|
11551
|
+
};
|
|
11552
|
+
}
|
|
11553
|
+
}
|
|
11554
|
+
function quote(identifier) {
|
|
11555
|
+
return `"${identifier.replaceAll('"', '""')}"`;
|
|
11556
|
+
}
|
|
11557
|
+
function literal2(value) {
|
|
11558
|
+
return `'${value.replaceAll("'", "''")}'`;
|
|
10546
11559
|
}
|
|
10547
11560
|
|
|
10548
11561
|
// src/constraints/index.ts
|
|
@@ -11389,13 +12402,13 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
11389
12402
|
});
|
|
11390
12403
|
}
|
|
11391
12404
|
const uniqueEndpoints = /* @__PURE__ */ new Map();
|
|
11392
|
-
for (const
|
|
11393
|
-
if (!uniqueEndpoints.has(
|
|
11394
|
-
uniqueEndpoints.set(
|
|
11395
|
-
fromKind:
|
|
11396
|
-
fromId:
|
|
11397
|
-
toKind:
|
|
11398
|
-
toId:
|
|
12405
|
+
for (const entry2 of validated) {
|
|
12406
|
+
if (!uniqueEndpoints.has(entry2.endpointKey)) {
|
|
12407
|
+
uniqueEndpoints.set(entry2.endpointKey, {
|
|
12408
|
+
fromKind: entry2.fromKind,
|
|
12409
|
+
fromId: entry2.fromId,
|
|
12410
|
+
toKind: entry2.toKind,
|
|
12411
|
+
toId: entry2.toId
|
|
11399
12412
|
});
|
|
11400
12413
|
}
|
|
11401
12414
|
}
|
|
@@ -11417,29 +12430,29 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
11417
12430
|
const toFetch = [];
|
|
11418
12431
|
const duplicateOf = [];
|
|
11419
12432
|
const seenKeys = /* @__PURE__ */ new Map();
|
|
11420
|
-
for (const [index,
|
|
11421
|
-
const previousIndex = seenKeys.get(
|
|
12433
|
+
for (const [index, entry2] of validated.entries()) {
|
|
12434
|
+
const previousIndex = seenKeys.get(entry2.compositeKey);
|
|
11422
12435
|
if (previousIndex !== void 0) {
|
|
11423
12436
|
duplicateOf.push({ index, sourceIndex: previousIndex });
|
|
11424
12437
|
continue;
|
|
11425
12438
|
}
|
|
11426
|
-
seenKeys.set(
|
|
11427
|
-
const candidateRows = rowsByEndpoint.get(
|
|
12439
|
+
seenKeys.set(entry2.compositeKey, index);
|
|
12440
|
+
const candidateRows = rowsByEndpoint.get(entry2.endpointKey) ?? [];
|
|
11428
12441
|
const { liveRow, deletedRow } = findMatchingEdge(
|
|
11429
12442
|
candidateRows,
|
|
11430
12443
|
matchOn,
|
|
11431
|
-
|
|
12444
|
+
entry2.validatedProps
|
|
11432
12445
|
);
|
|
11433
12446
|
if (liveRow === void 0 && deletedRow === void 0) {
|
|
11434
12447
|
toCreate.push({
|
|
11435
12448
|
index,
|
|
11436
12449
|
input: {
|
|
11437
12450
|
kind,
|
|
11438
|
-
fromKind:
|
|
11439
|
-
fromId:
|
|
11440
|
-
toKind:
|
|
11441
|
-
toId:
|
|
11442
|
-
props:
|
|
12451
|
+
fromKind: entry2.fromKind,
|
|
12452
|
+
fromId: entry2.fromId,
|
|
12453
|
+
toKind: entry2.toKind,
|
|
12454
|
+
toId: entry2.toId,
|
|
12455
|
+
props: entry2.validatedProps
|
|
11443
12456
|
}
|
|
11444
12457
|
});
|
|
11445
12458
|
} else {
|
|
@@ -11451,32 +12464,32 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
11451
12464
|
index,
|
|
11452
12465
|
row: bestRow,
|
|
11453
12466
|
isDeleted: liveRow === void 0,
|
|
11454
|
-
validatedProps:
|
|
11455
|
-
fromKind:
|
|
11456
|
-
fromId:
|
|
11457
|
-
toKind:
|
|
11458
|
-
toId:
|
|
12467
|
+
validatedProps: entry2.validatedProps,
|
|
12468
|
+
fromKind: entry2.fromKind,
|
|
12469
|
+
fromId: entry2.fromId,
|
|
12470
|
+
toKind: entry2.toKind,
|
|
12471
|
+
toId: entry2.toId
|
|
11459
12472
|
});
|
|
11460
12473
|
}
|
|
11461
12474
|
}
|
|
11462
12475
|
const results = Array.from({ length: items.length });
|
|
11463
12476
|
if (toCreate.length > 0) {
|
|
11464
|
-
const createInputs = toCreate.map((
|
|
12477
|
+
const createInputs = toCreate.map((entry2) => entry2.input);
|
|
11465
12478
|
const createdEdges = await executeEdgeCreateBatch(
|
|
11466
12479
|
ctx,
|
|
11467
12480
|
createInputs,
|
|
11468
12481
|
backend
|
|
11469
12482
|
);
|
|
11470
|
-
for (const [batchIndex,
|
|
11471
|
-
results[
|
|
12483
|
+
for (const [batchIndex, entry2] of toCreate.entries()) {
|
|
12484
|
+
results[entry2.index] = {
|
|
11472
12485
|
edge: createdEdges[batchIndex],
|
|
11473
12486
|
action: "created"
|
|
11474
12487
|
};
|
|
11475
12488
|
}
|
|
11476
12489
|
}
|
|
11477
|
-
for (const
|
|
11478
|
-
if (
|
|
11479
|
-
const effectiveValidTo =
|
|
12490
|
+
for (const entry2 of toFetch) {
|
|
12491
|
+
if (entry2.isDeleted) {
|
|
12492
|
+
const effectiveValidTo = entry2.row.valid_to;
|
|
11480
12493
|
const constraintContext = {
|
|
11481
12494
|
graphId: ctx.graphId,
|
|
11482
12495
|
registry: ctx.registry,
|
|
@@ -11486,28 +12499,28 @@ async function executeEdgeBulkGetOrCreateByEndpoints(ctx, kind, items, backend,
|
|
|
11486
12499
|
constraintContext,
|
|
11487
12500
|
kind,
|
|
11488
12501
|
cardinality,
|
|
11489
|
-
|
|
11490
|
-
|
|
11491
|
-
|
|
11492
|
-
|
|
12502
|
+
entry2.fromKind,
|
|
12503
|
+
entry2.fromId,
|
|
12504
|
+
entry2.toKind,
|
|
12505
|
+
entry2.toId,
|
|
11493
12506
|
effectiveValidTo
|
|
11494
12507
|
);
|
|
11495
12508
|
const edge = await executeEdgeUpsertUpdate(
|
|
11496
12509
|
ctx,
|
|
11497
|
-
{ id:
|
|
12510
|
+
{ id: entry2.row.id, props: entry2.validatedProps },
|
|
11498
12511
|
backend,
|
|
11499
12512
|
{ clearDeleted: true }
|
|
11500
12513
|
);
|
|
11501
|
-
results[
|
|
12514
|
+
results[entry2.index] = { edge, action: "resurrected" };
|
|
11502
12515
|
} else if (ifExists === "update") {
|
|
11503
12516
|
const edge = await executeEdgeUpsertUpdate(
|
|
11504
12517
|
ctx,
|
|
11505
|
-
{ id:
|
|
12518
|
+
{ id: entry2.row.id, props: entry2.validatedProps },
|
|
11506
12519
|
backend
|
|
11507
12520
|
);
|
|
11508
|
-
results[
|
|
12521
|
+
results[entry2.index] = { edge, action: "updated" };
|
|
11509
12522
|
} else {
|
|
11510
|
-
results[
|
|
12523
|
+
results[entry2.index] = { edge: rowToEdge(entry2.row), action: "found" };
|
|
11511
12524
|
}
|
|
11512
12525
|
}
|
|
11513
12526
|
for (const { index, sourceIndex } of duplicateOf) {
|
|
@@ -12480,7 +13493,7 @@ function validateAndComputeKeys(nodeKind, kind, constraint, items) {
|
|
|
12480
13493
|
function collectUniqueKeys(validated) {
|
|
12481
13494
|
return [
|
|
12482
13495
|
...new Set(
|
|
12483
|
-
validated.map((
|
|
13496
|
+
validated.map((entry2) => entry2.key).filter((key) => key !== void 0)
|
|
12484
13497
|
)
|
|
12485
13498
|
];
|
|
12486
13499
|
}
|
|
@@ -12586,21 +13599,21 @@ async function executeNodeBulkGetOrCreateByConstraint(ctx, kind, constraintName,
|
|
|
12586
13599
|
}
|
|
12587
13600
|
const results = Array.from({ length: items.length });
|
|
12588
13601
|
if (toCreate.length > 0) {
|
|
12589
|
-
const createInputs = toCreate.map((
|
|
13602
|
+
const createInputs = toCreate.map((entry2) => entry2.input);
|
|
12590
13603
|
const createdNodes = await executeNodeCreateBatch(
|
|
12591
13604
|
ctx,
|
|
12592
13605
|
createInputs,
|
|
12593
13606
|
backend
|
|
12594
13607
|
);
|
|
12595
|
-
for (const [batchIndex,
|
|
12596
|
-
results[
|
|
13608
|
+
for (const [batchIndex, entry2] of toCreate.entries()) {
|
|
13609
|
+
results[entry2.index] = {
|
|
12597
13610
|
node: createdNodes[batchIndex],
|
|
12598
13611
|
action: "created"
|
|
12599
13612
|
};
|
|
12600
13613
|
}
|
|
12601
13614
|
}
|
|
12602
|
-
for (const
|
|
12603
|
-
const { index, concreteKind, validatedProps, isSoftDeleted, nodeId } =
|
|
13615
|
+
for (const entry2 of toFetch) {
|
|
13616
|
+
const { index, concreteKind, validatedProps, isSoftDeleted, nodeId } = entry2;
|
|
12604
13617
|
const existingRow = await backend.getNode(
|
|
12605
13618
|
ctx.graphId,
|
|
12606
13619
|
concreteKind,
|
|
@@ -12845,7 +13858,7 @@ async function executeFulltextSearch(ctx, nodeKind, options) {
|
|
|
12845
13858
|
};
|
|
12846
13859
|
const rows = await backend.fulltextSearch(params);
|
|
12847
13860
|
if (rows.length === 0) return [];
|
|
12848
|
-
const ids = rows.map((
|
|
13861
|
+
const ids = rows.map((row) => row.nodeId);
|
|
12849
13862
|
const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
|
|
12850
13863
|
const hits = [];
|
|
12851
13864
|
let rank = 1;
|
|
@@ -12862,6 +13875,41 @@ async function executeFulltextSearch(ctx, nodeKind, options) {
|
|
|
12862
13875
|
}
|
|
12863
13876
|
return hits;
|
|
12864
13877
|
}
|
|
13878
|
+
async function executeVectorSearch(ctx, nodeKind, options) {
|
|
13879
|
+
const { backend, graphId } = ctx;
|
|
13880
|
+
if (!backend.vectorSearch) {
|
|
13881
|
+
throw new ConfigurationError("Backend does not support vector search", {
|
|
13882
|
+
backend: backend.dialect,
|
|
13883
|
+
capability: "vector"
|
|
13884
|
+
});
|
|
13885
|
+
}
|
|
13886
|
+
if (!Number.isInteger(options.limit) || options.limit <= 0) {
|
|
13887
|
+
throw new RangeError(
|
|
13888
|
+
`vectorSearch.limit must be a positive integer, got: ${options.limit}`
|
|
13889
|
+
);
|
|
13890
|
+
}
|
|
13891
|
+
const rows = await backend.vectorSearch({
|
|
13892
|
+
graphId,
|
|
13893
|
+
nodeKind,
|
|
13894
|
+
fieldPath: options.fieldPath,
|
|
13895
|
+
queryEmbedding: options.queryEmbedding,
|
|
13896
|
+
metric: options.metric ?? "cosine",
|
|
13897
|
+
limit: options.limit,
|
|
13898
|
+
...options.minScore === void 0 ? {} : { minScore: options.minScore }
|
|
13899
|
+
});
|
|
13900
|
+
if (rows.length === 0) return [];
|
|
13901
|
+
const ids = rows.map((row) => row.nodeId);
|
|
13902
|
+
const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
|
|
13903
|
+
const hits = [];
|
|
13904
|
+
let rank = 1;
|
|
13905
|
+
for (const row of rows) {
|
|
13906
|
+
const node = nodeMap.get(row.nodeId);
|
|
13907
|
+
if (!node) continue;
|
|
13908
|
+
hits.push({ node, score: row.score, rank });
|
|
13909
|
+
rank += 1;
|
|
13910
|
+
}
|
|
13911
|
+
return hits;
|
|
13912
|
+
}
|
|
12865
13913
|
async function executeHybridSearch(ctx, nodeKind, options) {
|
|
12866
13914
|
const { backend, graphId } = ctx;
|
|
12867
13915
|
if (!backend.vectorSearch) {
|
|
@@ -12922,58 +13970,58 @@ async function executeHybridSearch(ctx, nodeKind, options) {
|
|
|
12922
13970
|
for (const [index, row] of vectorRows.entries()) {
|
|
12923
13971
|
const rank2 = index + 1;
|
|
12924
13972
|
const contribution = vectorWeight / (fusionK + rank2);
|
|
12925
|
-
const
|
|
13973
|
+
const entry2 = fused.get(row.nodeId) ?? {
|
|
12926
13974
|
nodeId: row.nodeId,
|
|
12927
13975
|
fusedScore: 0
|
|
12928
13976
|
};
|
|
12929
|
-
|
|
12930
|
-
|
|
12931
|
-
|
|
12932
|
-
fused.set(row.nodeId,
|
|
13977
|
+
entry2.fusedScore += contribution;
|
|
13978
|
+
entry2.vectorRank = rank2;
|
|
13979
|
+
entry2.vectorScore = row.score;
|
|
13980
|
+
fused.set(row.nodeId, entry2);
|
|
12933
13981
|
}
|
|
12934
13982
|
for (const [index, row] of fulltextRows.entries()) {
|
|
12935
13983
|
const rank2 = index + 1;
|
|
12936
13984
|
const contribution = fulltextWeight / (fusionK + rank2);
|
|
12937
|
-
const
|
|
13985
|
+
const entry2 = fused.get(row.nodeId) ?? {
|
|
12938
13986
|
nodeId: row.nodeId,
|
|
12939
13987
|
fusedScore: 0
|
|
12940
13988
|
};
|
|
12941
|
-
|
|
12942
|
-
|
|
12943
|
-
|
|
13989
|
+
entry2.fusedScore += contribution;
|
|
13990
|
+
entry2.fulltextRank = rank2;
|
|
13991
|
+
entry2.fulltextScore = row.score;
|
|
12944
13992
|
if (row.snippet !== void 0) {
|
|
12945
|
-
|
|
13993
|
+
entry2.fulltextSnippet = row.snippet;
|
|
12946
13994
|
}
|
|
12947
|
-
fused.set(row.nodeId,
|
|
13995
|
+
fused.set(row.nodeId, entry2);
|
|
12948
13996
|
}
|
|
12949
13997
|
const ranked = [...fused.values()].toSorted(
|
|
12950
13998
|
(a, b) => b.fusedScore - a.fusedScore || a.nodeId.localeCompare(b.nodeId)
|
|
12951
13999
|
).slice(0, options.limit);
|
|
12952
|
-
const ids = ranked.map((
|
|
14000
|
+
const ids = ranked.map((entry2) => entry2.nodeId);
|
|
12953
14001
|
const nodeMap = await fetchNodesByIds(backend, graphId, nodeKind, ids);
|
|
12954
14002
|
const hits = [];
|
|
12955
14003
|
let rank = 1;
|
|
12956
|
-
for (const
|
|
12957
|
-
const node = nodeMap.get(
|
|
14004
|
+
for (const entry2 of ranked) {
|
|
14005
|
+
const node = nodeMap.get(entry2.nodeId);
|
|
12958
14006
|
if (!node) continue;
|
|
12959
14007
|
const typedNode = node;
|
|
12960
14008
|
const hit = {
|
|
12961
14009
|
node: typedNode,
|
|
12962
|
-
score:
|
|
14010
|
+
score: entry2.fusedScore,
|
|
12963
14011
|
rank,
|
|
12964
|
-
...
|
|
14012
|
+
...entry2.vectorRank !== void 0 && entry2.vectorScore !== void 0 ? {
|
|
12965
14013
|
vector: {
|
|
12966
14014
|
node: typedNode,
|
|
12967
|
-
score:
|
|
12968
|
-
rank:
|
|
14015
|
+
score: entry2.vectorScore,
|
|
14016
|
+
rank: entry2.vectorRank
|
|
12969
14017
|
}
|
|
12970
14018
|
} : {},
|
|
12971
|
-
...
|
|
14019
|
+
...entry2.fulltextRank !== void 0 && entry2.fulltextScore !== void 0 ? {
|
|
12972
14020
|
fulltext: {
|
|
12973
14021
|
node: typedNode,
|
|
12974
|
-
score:
|
|
12975
|
-
rank:
|
|
12976
|
-
...
|
|
14022
|
+
score: entry2.fulltextScore,
|
|
14023
|
+
rank: entry2.fulltextRank,
|
|
14024
|
+
...entry2.fulltextSnippet === void 0 ? {} : { snippet: entry2.fulltextSnippet }
|
|
12977
14025
|
}
|
|
12978
14026
|
} : {}
|
|
12979
14027
|
};
|
|
@@ -13086,12 +14134,32 @@ var StoreSearch = class {
|
|
|
13086
14134
|
* `Node` objects.
|
|
13087
14135
|
*/
|
|
13088
14136
|
async fulltext(nodeKind, options) {
|
|
14137
|
+
this.#assertKindRegistered(nodeKind);
|
|
13089
14138
|
return executeFulltextSearch(
|
|
13090
14139
|
{ graphId: this.#context.graphId, backend: this.#context.backend },
|
|
13091
14140
|
nodeKind,
|
|
13092
14141
|
options
|
|
13093
14142
|
);
|
|
13094
14143
|
}
|
|
14144
|
+
/**
|
|
14145
|
+
* Runs a vector similarity search against nodes of the given kind.
|
|
14146
|
+
*
|
|
14147
|
+
* Requires a field on the node schema declared with `embedding()`,
|
|
14148
|
+
* either at compile time or via a graph extension (the auto-derived
|
|
14149
|
+
* `VectorIndexDeclaration` flows through `materializeIndexes()` on
|
|
14150
|
+
* the same path either way).
|
|
14151
|
+
*
|
|
14152
|
+
* Pure vector — no fulltext leg, no fusion. For combined
|
|
14153
|
+
* vector+fulltext ranking, use `hybrid`.
|
|
14154
|
+
*/
|
|
14155
|
+
async vector(nodeKind, options) {
|
|
14156
|
+
this.#assertKindRegistered(nodeKind);
|
|
14157
|
+
return executeVectorSearch(
|
|
14158
|
+
{ graphId: this.#context.graphId, backend: this.#context.backend },
|
|
14159
|
+
nodeKind,
|
|
14160
|
+
options
|
|
14161
|
+
);
|
|
14162
|
+
}
|
|
13095
14163
|
/**
|
|
13096
14164
|
* Runs a vector + fulltext hybrid search and fuses the results with
|
|
13097
14165
|
* Reciprocal Rank Fusion.
|
|
@@ -13102,6 +14170,7 @@ var StoreSearch = class {
|
|
|
13102
14170
|
* `fulltext.k` for higher-recall corpora.
|
|
13103
14171
|
*/
|
|
13104
14172
|
async hybrid(nodeKind, options) {
|
|
14173
|
+
this.#assertKindRegistered(nodeKind);
|
|
13105
14174
|
return executeHybridSearch(
|
|
13106
14175
|
{ graphId: this.#context.graphId, backend: this.#context.backend },
|
|
13107
14176
|
nodeKind,
|
|
@@ -13127,6 +14196,7 @@ var StoreSearch = class {
|
|
|
13127
14196
|
* full consistency.
|
|
13128
14197
|
*/
|
|
13129
14198
|
async rebuildFulltext(nodeKind, options = {}) {
|
|
14199
|
+
if (nodeKind !== void 0) this.#assertKindRegistered(nodeKind);
|
|
13130
14200
|
return rebuildFulltextIndex(
|
|
13131
14201
|
{
|
|
13132
14202
|
graphId: this.#context.graphId,
|
|
@@ -13137,27 +14207,62 @@ var StoreSearch = class {
|
|
|
13137
14207
|
options
|
|
13138
14208
|
);
|
|
13139
14209
|
}
|
|
14210
|
+
#assertKindRegistered(kind) {
|
|
14211
|
+
if (this.#context.registry.hasNodeType(kind)) return;
|
|
14212
|
+
throw new KindNotFoundError(kind, "node", {
|
|
14213
|
+
graphId: this.#context.graphId,
|
|
14214
|
+
suggestion: "Compile-time kinds come from defineGraph; extension kinds appear after store.evolve() returns. Check store.introspect() for the registered set."
|
|
14215
|
+
});
|
|
14216
|
+
}
|
|
13140
14217
|
};
|
|
13141
14218
|
|
|
13142
14219
|
// src/store/store.ts
|
|
13143
|
-
var
|
|
14220
|
+
var UNKNOWN_SCHEMA_METADATA = Object.freeze({
|
|
14221
|
+
schemaVersion: void 0,
|
|
14222
|
+
schemaHash: void 0
|
|
14223
|
+
});
|
|
14224
|
+
var CAUGHT_UP_VERB_DETAILS = {
|
|
14225
|
+
evolve: { phrase: "evolve", code: "EVOLVE_BEFORE_INITIALIZE" },
|
|
14226
|
+
materialize: {
|
|
14227
|
+
phrase: "materialize indexes on",
|
|
14228
|
+
code: "MATERIALIZE_BEFORE_INITIALIZE"
|
|
14229
|
+
},
|
|
14230
|
+
remove: { phrase: "remove kinds on", code: "REMOVE_BEFORE_INITIALIZE" },
|
|
14231
|
+
deprecate: {
|
|
14232
|
+
phrase: "deprecate kinds on",
|
|
14233
|
+
code: "DEPRECATE_BEFORE_INITIALIZE"
|
|
14234
|
+
},
|
|
14235
|
+
undeprecate: {
|
|
14236
|
+
phrase: "undeprecate kinds on",
|
|
14237
|
+
code: "DEPRECATE_BEFORE_INITIALIZE"
|
|
14238
|
+
}
|
|
14239
|
+
};
|
|
14240
|
+
var Store = class _Store {
|
|
13144
14241
|
#graph;
|
|
13145
14242
|
#backend;
|
|
13146
14243
|
#registry;
|
|
13147
14244
|
#hooks;
|
|
13148
14245
|
#schema;
|
|
14246
|
+
#schemaMetadata;
|
|
13149
14247
|
#defaultTraversalExpansion;
|
|
14248
|
+
// Stored verbatim so `evolve()` can construct the next Store with
|
|
14249
|
+
// identical options. Reconstructing from the individual private
|
|
14250
|
+
// fields would silently drop any future StoreOptions field a
|
|
14251
|
+
// maintainer adds without also threading it through evolve.
|
|
14252
|
+
#options;
|
|
13150
14253
|
#nodeCollections;
|
|
13151
14254
|
#edgeCollections;
|
|
13152
14255
|
#algorithms;
|
|
13153
14256
|
#search;
|
|
13154
|
-
constructor(graph, backend, options) {
|
|
14257
|
+
constructor(graph, backend, options, schemaMetadata) {
|
|
13155
14258
|
this.#graph = graph;
|
|
13156
14259
|
this.#backend = backend;
|
|
13157
14260
|
this.#registry = buildKindRegistry(graph);
|
|
13158
14261
|
this.#hooks = options?.hooks ?? {};
|
|
13159
14262
|
this.#schema = options?.schema ?? (backend.tableNames ? createSqlSchema(backend.tableNames) : void 0);
|
|
13160
14263
|
this.#defaultTraversalExpansion = options?.queryDefaults?.traversalExpansion ?? "inverse";
|
|
14264
|
+
this.#options = options;
|
|
14265
|
+
this.#schemaMetadata = schemaMetadata ?? UNKNOWN_SCHEMA_METADATA;
|
|
13161
14266
|
}
|
|
13162
14267
|
// === Accessors ===
|
|
13163
14268
|
/** The graph definition */
|
|
@@ -13271,23 +14376,125 @@ var Store = class {
|
|
|
13271
14376
|
*
|
|
13272
14377
|
* Use this for runtime string-keyed access when the kind is not known at
|
|
13273
14378
|
* compile time (e.g., iterating all kinds, resolving from edge metadata,
|
|
13274
|
-
* dynamic admin UIs).
|
|
14379
|
+
* dynamic admin UIs). For the post-evolve "I just added this kind, give
|
|
14380
|
+
* me the collection" pattern, prefer `getNodeCollectionOrThrow` — it
|
|
14381
|
+
* throws `KindNotFoundError` instead of forcing a null-check.
|
|
13275
14382
|
*/
|
|
13276
14383
|
getNodeCollection(kind) {
|
|
13277
14384
|
if (!Object.hasOwn(this.#graph.nodes, kind)) return void 0;
|
|
13278
14385
|
return this.nodes[kind];
|
|
13279
14386
|
}
|
|
14387
|
+
/**
|
|
14388
|
+
* Returns the node collection for the given kind. Throws
|
|
14389
|
+
* `KindNotFoundError` when the kind is not registered.
|
|
14390
|
+
*
|
|
14391
|
+
* The dominant graph-extension-kind access pattern: `await store.evolve(...)`
|
|
14392
|
+
* returns a new store, the caller immediately operates on the new
|
|
14393
|
+
* kind, and the null-check the optional variant requires is busywork.
|
|
14394
|
+
*/
|
|
14395
|
+
getNodeCollectionOrThrow(kind) {
|
|
14396
|
+
const collection = this.getNodeCollection(kind);
|
|
14397
|
+
if (collection === void 0) {
|
|
14398
|
+
throw new KindNotFoundError(kind, "node", {
|
|
14399
|
+
graphId: this.graphId
|
|
14400
|
+
});
|
|
14401
|
+
}
|
|
14402
|
+
return collection;
|
|
14403
|
+
}
|
|
13280
14404
|
/**
|
|
13281
14405
|
* Returns the edge collection for the given kind, or undefined if the kind
|
|
13282
14406
|
* is not registered in this graph.
|
|
13283
14407
|
*
|
|
13284
14408
|
* Use this for runtime string-keyed access when the kind is not known at
|
|
13285
|
-
* compile time.
|
|
14409
|
+
* compile time. For post-evolve access, prefer `getEdgeCollectionOrThrow`.
|
|
13286
14410
|
*/
|
|
13287
14411
|
getEdgeCollection(kind) {
|
|
13288
14412
|
if (!Object.hasOwn(this.#graph.edges, kind)) return void 0;
|
|
13289
14413
|
return this.edges[kind];
|
|
13290
14414
|
}
|
|
14415
|
+
/**
|
|
14416
|
+
* Returns the edge collection for the given kind. Throws
|
|
14417
|
+
* `KindNotFoundError` when the kind is not registered.
|
|
14418
|
+
*/
|
|
14419
|
+
getEdgeCollectionOrThrow(kind) {
|
|
14420
|
+
const collection = this.getEdgeCollection(kind);
|
|
14421
|
+
if (collection === void 0) {
|
|
14422
|
+
throw new KindNotFoundError(kind, "edge", {
|
|
14423
|
+
graphId: this.graphId
|
|
14424
|
+
});
|
|
14425
|
+
}
|
|
14426
|
+
return collection;
|
|
14427
|
+
}
|
|
14428
|
+
// === Dynamic Props Schema Access ===
|
|
14429
|
+
/**
|
|
14430
|
+
* Returns the Zod props schema for the given node kind, or `undefined`
|
|
14431
|
+
* if the kind is not registered. Identity-preserving: returns the
|
|
14432
|
+
* exact instance used by `.create()` / `.update()`, so a `parse()`
|
|
14433
|
+
* surfaces the same underlying Zod issues that `ValidationError`
|
|
14434
|
+
* wraps (operation-level checks like uniqueness and endpoints stay
|
|
14435
|
+
* in `collection.create`).
|
|
14436
|
+
*/
|
|
14437
|
+
getNodePropsSchema(kind) {
|
|
14438
|
+
if (!Object.hasOwn(this.#graph.nodes, kind)) return void 0;
|
|
14439
|
+
return this.#graph.nodes[kind].type.schema;
|
|
14440
|
+
}
|
|
14441
|
+
/**
|
|
14442
|
+
* Returns the Zod props schema for the given node kind. Throws
|
|
14443
|
+
* `KindNotFoundError` when the kind is not registered.
|
|
14444
|
+
*/
|
|
14445
|
+
getNodePropsSchemaOrThrow(kind) {
|
|
14446
|
+
const schema = this.getNodePropsSchema(kind);
|
|
14447
|
+
if (schema === void 0) {
|
|
14448
|
+
throw new KindNotFoundError(kind, "node", {
|
|
14449
|
+
graphId: this.graphId
|
|
14450
|
+
});
|
|
14451
|
+
}
|
|
14452
|
+
return schema;
|
|
14453
|
+
}
|
|
14454
|
+
/**
|
|
14455
|
+
* Returns the Zod props schema for the given edge kind, or `undefined`
|
|
14456
|
+
* if the kind is not registered. Symmetric with `getNodePropsSchema`.
|
|
14457
|
+
*/
|
|
14458
|
+
getEdgePropsSchema(kind) {
|
|
14459
|
+
if (!Object.hasOwn(this.#graph.edges, kind)) return void 0;
|
|
14460
|
+
return this.#graph.edges[kind].type.schema;
|
|
14461
|
+
}
|
|
14462
|
+
/**
|
|
14463
|
+
* Returns the Zod props schema for the given edge kind. Throws
|
|
14464
|
+
* `KindNotFoundError` when the kind is not registered.
|
|
14465
|
+
*/
|
|
14466
|
+
getEdgePropsSchemaOrThrow(kind) {
|
|
14467
|
+
const schema = this.getEdgePropsSchema(kind);
|
|
14468
|
+
if (schema === void 0) {
|
|
14469
|
+
throw new KindNotFoundError(kind, "edge", {
|
|
14470
|
+
graphId: this.graphId
|
|
14471
|
+
});
|
|
14472
|
+
}
|
|
14473
|
+
return schema;
|
|
14474
|
+
}
|
|
14475
|
+
/**
|
|
14476
|
+
* Returns a unified read of the merged schema — every compile-time
|
|
14477
|
+
* and graph-extension kind, every edge, every ontology relation,
|
|
14478
|
+
* with explicit `origin: "compile-time" | "runtime"` markers — plus
|
|
14479
|
+
* the persisted `extension` for round-tripping.
|
|
14480
|
+
*
|
|
14481
|
+
* Pure synchronous read built from the in-memory graph and the
|
|
14482
|
+
* already-merged graph-extension document. `schemaVersion` and `schemaHash`
|
|
14483
|
+
* are populated when the loader cached them at construction or after
|
|
14484
|
+
* `evolve` returns; consumers needing a fresh read should call
|
|
14485
|
+
* `backend.getActiveSchema(graphId)`.
|
|
14486
|
+
*
|
|
14487
|
+
* The return shape is the canonical schema-introspection surface:
|
|
14488
|
+
* the prior standalone `store.deprecatedKinds` accessor is replaced
|
|
14489
|
+
* by `introspect().deprecatedKinds`.
|
|
14490
|
+
*/
|
|
14491
|
+
introspect() {
|
|
14492
|
+
return introspectSchema(this.#graph, {
|
|
14493
|
+
graphId: this.graphId,
|
|
14494
|
+
schemaVersion: this.#schemaMetadata.schemaVersion,
|
|
14495
|
+
schemaHash: this.#schemaMetadata.schemaHash
|
|
14496
|
+
});
|
|
14497
|
+
}
|
|
13291
14498
|
/**
|
|
13292
14499
|
* Node operations bound to this store instance.
|
|
13293
14500
|
*/
|
|
@@ -13639,6 +14846,415 @@ var Store = class {
|
|
|
13639
14846
|
async refreshStatistics() {
|
|
13640
14847
|
await this.#backend.refreshStatistics();
|
|
13641
14848
|
}
|
|
14849
|
+
/**
|
|
14850
|
+
* Evolves the graph at runtime by merging a graph extension into the
|
|
14851
|
+
* current schema, atomically committing a new schema version, and
|
|
14852
|
+
* returning a fresh `Store` constructed against the merged graph.
|
|
14853
|
+
*
|
|
14854
|
+
* The `Store` is immutable — its registry, collections, and operation
|
|
14855
|
+
* contexts close over the graph at construction time — so callers
|
|
14856
|
+
* must use the returned store (or pass a `ref` to be re-pointed) for
|
|
14857
|
+
* any work involving the new kinds.
|
|
14858
|
+
*
|
|
14859
|
+
* **Cost for purely additive extensions is proportional to schema
|
|
14860
|
+
* document size, not row count.** The commit is a single CAS write.
|
|
14861
|
+
* Tightening changes against existing graph-extension-declared kinds run
|
|
14862
|
+
* row-count probes for those affected kinds so populated kinds can
|
|
14863
|
+
* be rejected without a backfill.
|
|
14864
|
+
*
|
|
14865
|
+
* **Concurrent evolve recovery.** On `StaleVersionError`, refetch the
|
|
14866
|
+
* current active schema (or dereference your `StoreRef`),
|
|
14867
|
+
* reconstruct your `Store`, and re-call `evolve(extension)` against
|
|
14868
|
+
* the new store. Re-validation may now surface deterministic errors
|
|
14869
|
+
* (e.g., another caller just added a kind that collides with yours,
|
|
14870
|
+
* or redeclared one of yours with a different shape). Don't loop
|
|
14871
|
+
* blindly — surface the error.
|
|
14872
|
+
*
|
|
14873
|
+
* @param extension - Graph extension produced by
|
|
14874
|
+
* `defineGraphExtension(...)`.
|
|
14875
|
+
* @param options.ref - Optional handle whose `current` is overwritten
|
|
14876
|
+
* atomically with the schema commit. Long-lived consumers
|
|
14877
|
+
* (request handlers, background workers) that dereference through
|
|
14878
|
+
* the ref see the new kinds on the *next* call.
|
|
14879
|
+
*
|
|
14880
|
+
* @throws {GraphExtensionValidationError} when the extension is
|
|
14881
|
+
* structurally invalid.
|
|
14882
|
+
* @throws {KindCollisionError} when an extension kind shadows a
|
|
14883
|
+
* compile-time kind (code `KIND_COLLISION`).
|
|
14884
|
+
* @throws {IncompatibleChangeError} when a redeclared kind narrows
|
|
14885
|
+
* in a way the existing rows can't satisfy
|
|
14886
|
+
* (code `INCOMPATIBLE_CHANGE`).
|
|
14887
|
+
* @throws {GraphExtensionUnresolvedEndpointError} when an edge
|
|
14888
|
+
* endpoint references a kind that exists in neither the extension
|
|
14889
|
+
* nor the host graph
|
|
14890
|
+
* (code `GRAPH_EXTENSION_UNRESOLVED_ENDPOINT`).
|
|
14891
|
+
* @throws {StaleVersionError} when another writer has advanced the
|
|
14892
|
+
* schema since this store was constructed; recovery as above.
|
|
14893
|
+
* @throws {SchemaContentConflictError} when a row already exists at
|
|
14894
|
+
* the target version with a different content hash.
|
|
14895
|
+
*/
|
|
14896
|
+
async evolve(extension, options) {
|
|
14897
|
+
const { activeRow, baseline } = await this.#loadCaughtUp("evolve");
|
|
14898
|
+
const merged = mergeGraphExtension(baseline, extension);
|
|
14899
|
+
if (merged === baseline) {
|
|
14900
|
+
if (baseline === this.#graph) {
|
|
14901
|
+
if (options?.ref !== void 0) options.ref.current = this;
|
|
14902
|
+
if (options?.eager !== void 0) {
|
|
14903
|
+
await this.#runEagerOrThrow(this, options.eager);
|
|
14904
|
+
}
|
|
14905
|
+
return this;
|
|
14906
|
+
}
|
|
14907
|
+
const caughtUp = this.#cloneWithGraph(
|
|
14908
|
+
baseline,
|
|
14909
|
+
options?.ref,
|
|
14910
|
+
schemaMetadataFromRow(activeRow)
|
|
14911
|
+
);
|
|
14912
|
+
if (options?.eager !== void 0) {
|
|
14913
|
+
await this.#runEagerOrThrow(caughtUp, options.eager);
|
|
14914
|
+
}
|
|
14915
|
+
return caughtUp;
|
|
14916
|
+
}
|
|
14917
|
+
const baselineDocument = baseline.extension ?? Object.freeze({});
|
|
14918
|
+
const classification = classifyModifications(baselineDocument, extension);
|
|
14919
|
+
if (classification.incompatible.length > 0) {
|
|
14920
|
+
throw new IncompatibleChangeError(
|
|
14921
|
+
classification.incompatible,
|
|
14922
|
+
this.graphId
|
|
14923
|
+
);
|
|
14924
|
+
}
|
|
14925
|
+
if (classification.requireEmpty.length > 0) {
|
|
14926
|
+
const nonEmpty = await this.#probeEmptyKinds(classification.requireEmpty);
|
|
14927
|
+
const error = buildIncompatibleChangeError(
|
|
14928
|
+
classification,
|
|
14929
|
+
nonEmpty,
|
|
14930
|
+
this.graphId
|
|
14931
|
+
);
|
|
14932
|
+
if (error !== void 0) throw error;
|
|
14933
|
+
}
|
|
14934
|
+
const committed = await commitNewSchemaVersion(
|
|
14935
|
+
this.#backend,
|
|
14936
|
+
merged,
|
|
14937
|
+
activeRow.version
|
|
14938
|
+
);
|
|
14939
|
+
const evolved = this.#cloneWithGraph(
|
|
14940
|
+
merged,
|
|
14941
|
+
options?.ref,
|
|
14942
|
+
schemaMetadataFromRow(committed)
|
|
14943
|
+
);
|
|
14944
|
+
if (options?.eager !== void 0) {
|
|
14945
|
+
await this.#runEagerOrThrow(evolved, options.eager);
|
|
14946
|
+
}
|
|
14947
|
+
return evolved;
|
|
14948
|
+
}
|
|
14949
|
+
async #runEagerOrThrow(store, eager) {
|
|
14950
|
+
const result = await store.materializeIndexes(eager);
|
|
14951
|
+
if (result.results.some((entry2) => entry2.status === "failed")) {
|
|
14952
|
+
throw new EagerMaterializationError(result, this.graphId);
|
|
14953
|
+
}
|
|
14954
|
+
}
|
|
14955
|
+
/**
|
|
14956
|
+
* Marks the named node and edge kinds as soft-deprecated. Surfaces in
|
|
14957
|
+
* `store.introspect().deprecatedKinds` for introspection (codegen,
|
|
14958
|
+
* UI tooling, lints) but does not gate reads, writes, or queries —
|
|
14959
|
+
* deprecation is a signal, not a removal.
|
|
14960
|
+
*
|
|
14961
|
+
* Atomically commits a new schema version through the same primitive
|
|
14962
|
+
* `evolve()` uses, so concurrent deprecate/evolve calls produce
|
|
14963
|
+
* `StaleVersionError | SchemaContentConflictError` race losers that
|
|
14964
|
+
* the caller refetches and retries. Idempotent: re-deprecating a
|
|
14965
|
+
* kind that's already marked is a no-op (no version bump).
|
|
14966
|
+
*
|
|
14967
|
+
* @param names - Node or edge kind names to mark as deprecated.
|
|
14968
|
+
* @param options.ref - Optional handle to be re-pointed atomically
|
|
14969
|
+
* with the schema commit, mirroring `evolve()`.
|
|
14970
|
+
*
|
|
14971
|
+
* @throws {ConfigurationError} on `DEPRECATE_BEFORE_INITIALIZE` (no
|
|
14972
|
+
* schema yet) or `DEPRECATE_UNKNOWN_KIND` (name not on the graph).
|
|
14973
|
+
* @throws {StaleVersionError} when another writer has advanced the
|
|
14974
|
+
* schema since this store was constructed; refetch and retry per
|
|
14975
|
+
* the same recipe `evolve()` documents.
|
|
14976
|
+
* @throws {SchemaContentConflictError} when a row already exists at
|
|
14977
|
+
* the target version with a different content hash.
|
|
14978
|
+
*/
|
|
14979
|
+
async deprecateKinds(names, options) {
|
|
14980
|
+
return this.#updateDeprecatedKinds("add", names, options);
|
|
14981
|
+
}
|
|
14982
|
+
/**
|
|
14983
|
+
* Reverses `deprecateKinds(...)` for the named kinds. Same race +
|
|
14984
|
+
* idempotency semantics: removing a name that isn't currently
|
|
14985
|
+
* deprecated is a no-op for that name (the call as a whole is a
|
|
14986
|
+
* no-op only if every name was already absent).
|
|
14987
|
+
*
|
|
14988
|
+
* @param names - Node or edge kind names to remove from the
|
|
14989
|
+
* deprecated set.
|
|
14990
|
+
* @param options.ref - Optional handle to be re-pointed atomically
|
|
14991
|
+
* with the schema commit.
|
|
14992
|
+
*
|
|
14993
|
+
* @throws {ConfigurationError} on `DEPRECATE_BEFORE_INITIALIZE`.
|
|
14994
|
+
* @throws {StaleVersionError} on a CAS race with another writer.
|
|
14995
|
+
* @throws {SchemaContentConflictError} on a same-version content
|
|
14996
|
+
* conflict.
|
|
14997
|
+
*/
|
|
14998
|
+
async undeprecateKinds(names, options) {
|
|
14999
|
+
return this.#updateDeprecatedKinds("remove", names, options);
|
|
15000
|
+
}
|
|
15001
|
+
/**
|
|
15002
|
+
* Runs `CREATE INDEX` DDL for the indexes declared on this graph and
|
|
15003
|
+
* records per-deployment status in `typegraph_index_materializations`.
|
|
15004
|
+
* The status table is per-database (two replicas of the same
|
|
15005
|
+
* `schema_doc` are still two different databases for DDL purposes),
|
|
15006
|
+
* so the same call against two replicas materializes independently.
|
|
15007
|
+
*
|
|
15008
|
+
* Idempotent. Re-running the verb against an already-materialized
|
|
15009
|
+
* index is a no-op (`status: "alreadyMaterialized"`). Postgres uses
|
|
15010
|
+
* `CREATE INDEX CONCURRENTLY` so live tables never take an
|
|
15011
|
+
* `AccessExclusiveLock`. SQLite is single-writer regardless.
|
|
15012
|
+
*
|
|
15013
|
+
* Best-effort by default: a failed index records `status: "failed"`
|
|
15014
|
+
* with the error and the loop continues. Pass `{ stopOnError: true }`
|
|
15015
|
+
* to halt on the first failure.
|
|
15016
|
+
*
|
|
15017
|
+
* @param options.kinds - Restrict to indexes whose `kind` is in this
|
|
15018
|
+
* set. Throws `ConfigurationError` (`code:
|
|
15019
|
+
* "MATERIALIZE_UNKNOWN_KIND"`) if any name doesn't match a known
|
|
15020
|
+
* compile-time or extension kind.
|
|
15021
|
+
* @param options.stopOnError - Halt on first failure. Default false.
|
|
15022
|
+
*
|
|
15023
|
+
* @throws {ConfigurationError} `MATERIALIZE_BACKEND_UNSUPPORTED` if
|
|
15024
|
+
* the backend lacks `executeDdl`, `getIndexMaterialization`, or
|
|
15025
|
+
* `recordIndexMaterialization`; `MATERIALIZE_UNKNOWN_KIND` for an
|
|
15026
|
+
* unknown kind name; `MATERIALIZE_BEFORE_INITIALIZE` if the store
|
|
15027
|
+
* has no schema yet.
|
|
15028
|
+
*/
|
|
15029
|
+
async materializeIndexes(options) {
|
|
15030
|
+
const { activeRow, baseline } = await this.#loadCaughtUp("materialize");
|
|
15031
|
+
return materializeIndexes(
|
|
15032
|
+
{
|
|
15033
|
+
graph: baseline,
|
|
15034
|
+
graphId: this.graphId,
|
|
15035
|
+
backend: this.#backend,
|
|
15036
|
+
schemaVersion: activeRow.version
|
|
15037
|
+
},
|
|
15038
|
+
options ?? {}
|
|
15039
|
+
);
|
|
15040
|
+
}
|
|
15041
|
+
/**
|
|
15042
|
+
* Removes extension kinds from the schema with cascading edge and
|
|
15043
|
+
* ontology cleanup. Two-phase by design:
|
|
15044
|
+
*
|
|
15045
|
+
* 1. **Schema commit (this method).** Validates the removal,
|
|
15046
|
+
* rebuilds the persisted extension without the named kinds
|
|
15047
|
+
* (and without extension edges that lose their last endpoint),
|
|
15048
|
+
* CAS-commits the new schema version, and queues
|
|
15049
|
+
* per-deployment data-cleanup status. Millisecond budget.
|
|
15050
|
+
* 2. **Data cleanup (`materializeRemovals`).** Deletes the orphan
|
|
15051
|
+
* rows from the nodes/edges tables. Bounded by row count.
|
|
15052
|
+
*
|
|
15053
|
+
* Pass `{ eager: {} }` to run the data-cleanup pass inline with
|
|
15054
|
+
* default options, or `{ eager: { ... } }` to scope it; otherwise
|
|
15055
|
+
* call `materializeRemovals()` later.
|
|
15056
|
+
*
|
|
15057
|
+
* Idempotent: removing a name that doesn't exist is a no-op (no
|
|
15058
|
+
* version bump). Removing an extension kind referenced by a compile-
|
|
15059
|
+
* time edge or ontology relation throws `KindHasReferentsError`.
|
|
15060
|
+
* Removing a compile-time kind throws `RemoveCompileTimeKindError` —
|
|
15061
|
+
* compile-time kinds are removed by recompiling and redeploying.
|
|
15062
|
+
*
|
|
15063
|
+
* @throws {RemoveCompileTimeKindError} when `names` includes a
|
|
15064
|
+
* compile-time kind.
|
|
15065
|
+
* @throws {KindHasReferentsError} when an extension kind being
|
|
15066
|
+
* removed is referenced by a compile-time declaration.
|
|
15067
|
+
* @throws {StaleVersionError} on a CAS race with another writer.
|
|
15068
|
+
* @throws {SchemaContentConflictError} on a same-version content
|
|
15069
|
+
* conflict.
|
|
15070
|
+
*/
|
|
15071
|
+
async removeKinds(names, options) {
|
|
15072
|
+
const { activeRow, baseline } = await this.#loadCaughtUp("remove");
|
|
15073
|
+
const plan = planRemovals(baseline, names);
|
|
15074
|
+
if (plan.removedNodeKinds.length === 0 && plan.removedEdgeKinds.length === 0) {
|
|
15075
|
+
if (baseline === this.#graph) {
|
|
15076
|
+
if (options?.ref !== void 0) options.ref.current = this;
|
|
15077
|
+
return this;
|
|
15078
|
+
}
|
|
15079
|
+
return this.#cloneWithGraph(
|
|
15080
|
+
baseline,
|
|
15081
|
+
options?.ref,
|
|
15082
|
+
schemaMetadataFromRow(activeRow)
|
|
15083
|
+
);
|
|
15084
|
+
}
|
|
15085
|
+
const compileTimeGraph = stripGraphExtension(this.#graph);
|
|
15086
|
+
const merged = plan.document === void 0 ? compileTimeGraph : mergeGraphExtension(compileTimeGraph, plan.document);
|
|
15087
|
+
const finalGraph = applyDeprecatedKinds(merged, baseline.deprecatedKinds);
|
|
15088
|
+
const committedRow = await commitNewSchemaVersion(
|
|
15089
|
+
this.#backend,
|
|
15090
|
+
finalGraph,
|
|
15091
|
+
activeRow.version
|
|
15092
|
+
);
|
|
15093
|
+
const recordKindRemoval = this.#backend.recordKindRemoval;
|
|
15094
|
+
if (recordKindRemoval !== void 0) {
|
|
15095
|
+
if (this.#backend.ensureKindRemovalsTable !== void 0) {
|
|
15096
|
+
await this.#backend.ensureKindRemovalsTable();
|
|
15097
|
+
}
|
|
15098
|
+
const attemptedAt = nowIso();
|
|
15099
|
+
const newSchemaVersion = committedRow.version;
|
|
15100
|
+
const queue = (kindName, entity) => recordKindRemoval(
|
|
15101
|
+
buildPendingKindRemoval({
|
|
15102
|
+
graphId: this.graphId,
|
|
15103
|
+
kindName,
|
|
15104
|
+
entity,
|
|
15105
|
+
schemaVersion: newSchemaVersion,
|
|
15106
|
+
attemptedAt
|
|
15107
|
+
})
|
|
15108
|
+
);
|
|
15109
|
+
await Promise.all([
|
|
15110
|
+
...plan.removedNodeKinds.map((name) => queue(name, "node")),
|
|
15111
|
+
...plan.removedEdgeKinds.map((name) => queue(name, "edge"))
|
|
15112
|
+
]);
|
|
15113
|
+
}
|
|
15114
|
+
const evolved = this.#cloneWithGraph(
|
|
15115
|
+
finalGraph,
|
|
15116
|
+
options?.ref,
|
|
15117
|
+
schemaMetadataFromRow(committedRow)
|
|
15118
|
+
);
|
|
15119
|
+
if (options?.eager !== void 0) {
|
|
15120
|
+
const kinds = [...plan.removedNodeKinds, ...plan.removedEdgeKinds];
|
|
15121
|
+
await evolved.materializeRemovals({
|
|
15122
|
+
...options.eager,
|
|
15123
|
+
kinds: options.eager.kinds ?? kinds
|
|
15124
|
+
});
|
|
15125
|
+
}
|
|
15126
|
+
return evolved;
|
|
15127
|
+
}
|
|
15128
|
+
/**
|
|
15129
|
+
* Runs the data-cleanup phase for any kinds removed via
|
|
15130
|
+
* `removeKinds()` whose data has not yet been deleted on this
|
|
15131
|
+
* deployment. Safe to call repeatedly; idempotent.
|
|
15132
|
+
*/
|
|
15133
|
+
async materializeRemovals(options) {
|
|
15134
|
+
return materializeRemovals(
|
|
15135
|
+
{ graphId: this.graphId, backend: this.#backend },
|
|
15136
|
+
options ?? {}
|
|
15137
|
+
);
|
|
15138
|
+
}
|
|
15139
|
+
async #updateDeprecatedKinds(direction, names, options) {
|
|
15140
|
+
const verb = direction === "add" ? "deprecate" : "undeprecate";
|
|
15141
|
+
const { activeRow, storedSchema, baseline } = await this.#loadCaughtUp(verb);
|
|
15142
|
+
const nextSet = new Set(baseline.deprecatedKinds);
|
|
15143
|
+
if (direction === "add") {
|
|
15144
|
+
for (const name of names) {
|
|
15145
|
+
if (!isKnownKind(baseline, name)) {
|
|
15146
|
+
throw new KindNotFoundError(
|
|
15147
|
+
name,
|
|
15148
|
+
isKnownEdgeKind(baseline, name) ? "edge" : "node",
|
|
15149
|
+
{
|
|
15150
|
+
graphId: this.graphId,
|
|
15151
|
+
suggestion: "Only kinds declared on the graph (compile-time or runtime) can be deprecated."
|
|
15152
|
+
}
|
|
15153
|
+
);
|
|
15154
|
+
}
|
|
15155
|
+
nextSet.add(name);
|
|
15156
|
+
}
|
|
15157
|
+
} else {
|
|
15158
|
+
for (const name of names) nextSet.delete(name);
|
|
15159
|
+
}
|
|
15160
|
+
if (setsEqual(nextSet, baseline.deprecatedKinds)) {
|
|
15161
|
+
if (baseline === this.#graph) {
|
|
15162
|
+
if (options?.ref !== void 0) options.ref.current = this;
|
|
15163
|
+
return this;
|
|
15164
|
+
}
|
|
15165
|
+
return this.#cloneWithGraph(
|
|
15166
|
+
baseline,
|
|
15167
|
+
options?.ref,
|
|
15168
|
+
schemaMetadataFromRow(activeRow)
|
|
15169
|
+
);
|
|
15170
|
+
}
|
|
15171
|
+
const merged = applyDeprecatedKinds(baseline, nextSet);
|
|
15172
|
+
const result = await ensureSchema(this.#backend, merged, {
|
|
15173
|
+
preloaded: { activeRow, storedSchema },
|
|
15174
|
+
autoMigrate: true
|
|
15175
|
+
});
|
|
15176
|
+
const metadata = result.status === "migrated" || result.status === "initialized" ? schemaMetadataFromRow(result.committedRow) : schemaMetadataFromRow(activeRow);
|
|
15177
|
+
return this.#cloneWithGraph(merged, options?.ref, metadata);
|
|
15178
|
+
}
|
|
15179
|
+
#catchUpToStored(storedSchema) {
|
|
15180
|
+
const compileTimeGraph = stripGraphExtension(this.#graph);
|
|
15181
|
+
const withGraphExtension = storedSchema.extension === void 0 ? compileTimeGraph : mergeGraphExtension(compileTimeGraph, storedSchema.extension);
|
|
15182
|
+
return applyDeprecatedKinds(
|
|
15183
|
+
withGraphExtension,
|
|
15184
|
+
storedSchema.deprecatedKinds
|
|
15185
|
+
);
|
|
15186
|
+
}
|
|
15187
|
+
/**
|
|
15188
|
+
* Loads the active schema row, parses it, and catches the in-memory
|
|
15189
|
+
* graph up to the persisted state — the shared preamble for
|
|
15190
|
+
* `evolve`, `materializeIndexes`, and `(un)deprecateKinds`.
|
|
15191
|
+
*
|
|
15192
|
+
* Throws `ConfigurationError` with a verb-specific code when the
|
|
15193
|
+
* graph has not been initialized yet. The catch-up step replays the
|
|
15194
|
+
* persisted graph-extension document and deprecation set on top of the
|
|
15195
|
+
* compile-time graph so we diff against the same baseline another
|
|
15196
|
+
* writer would; the CAS guard inside `commitSchemaVersion` still
|
|
15197
|
+
* serializes the actual commit.
|
|
15198
|
+
*/
|
|
15199
|
+
async #loadCaughtUp(verb) {
|
|
15200
|
+
const activeRow = await loadActiveSchemaWithBootstrap(
|
|
15201
|
+
this.#backend,
|
|
15202
|
+
this.graphId
|
|
15203
|
+
);
|
|
15204
|
+
if (activeRow === void 0) {
|
|
15205
|
+
const { phrase, code } = CAUGHT_UP_VERB_DETAILS[verb];
|
|
15206
|
+
throw new ConfigurationError(
|
|
15207
|
+
`Cannot ${phrase} graph "${this.graphId}": no schema has been initialized. Call createStoreWithSchema first.`,
|
|
15208
|
+
{ code }
|
|
15209
|
+
);
|
|
15210
|
+
}
|
|
15211
|
+
const storedSchema = parseSerializedSchema(activeRow.schema_doc);
|
|
15212
|
+
const baseline = this.#catchUpToStored(storedSchema);
|
|
15213
|
+
return { activeRow, storedSchema, baseline };
|
|
15214
|
+
}
|
|
15215
|
+
/**
|
|
15216
|
+
* Probes each `requireEmpty` entry for any rows. Returns the set of
|
|
15217
|
+
* composite keys (`${entity}:${kindName}`) that have at least one
|
|
15218
|
+
* row — those entries need to be promoted from "allowed-on-empty"
|
|
15219
|
+
* to incompatible. Dispatches to `countNodesByKind` for `node`
|
|
15220
|
+
* entries and `countEdgesByKind` for `edge` entries (a single-
|
|
15221
|
+
* primitive probe would always return 0 for the wrong-entity case
|
|
15222
|
+
* and silently bypass the gate).
|
|
15223
|
+
*
|
|
15224
|
+
* Probes run in parallel; each entry is independent. Race window
|
|
15225
|
+
* (probe → CAS commit): another writer can insert a row into a
|
|
15226
|
+
* previously-empty kind. The schema commit still succeeds (CAS
|
|
15227
|
+
* guards on schema version, not row count); the new schema rejects
|
|
15228
|
+
* the inserted row at next read, which the operator inspects and
|
|
15229
|
+
* either deletes or reverts. Rare in practice; tighter elimination
|
|
15230
|
+
* would require `SELECT FOR UPDATE` on the rows table, too
|
|
15231
|
+
* heavyweight for a millisecond-budget operation.
|
|
15232
|
+
*/
|
|
15233
|
+
async #probeEmptyKinds(requireEmpty) {
|
|
15234
|
+
const probes = requireEmpty.map(
|
|
15235
|
+
async (entry2) => {
|
|
15236
|
+
const count2 = entry2.entity === "node" ? await this.#backend.countNodesByKind({
|
|
15237
|
+
graphId: this.graphId,
|
|
15238
|
+
kind: entry2.kindName
|
|
15239
|
+
}) : await this.#backend.countEdgesByKind({
|
|
15240
|
+
graphId: this.graphId,
|
|
15241
|
+
kind: entry2.kindName
|
|
15242
|
+
});
|
|
15243
|
+
return [entry2, count2];
|
|
15244
|
+
}
|
|
15245
|
+
);
|
|
15246
|
+
const results = await Promise.all(probes);
|
|
15247
|
+
const nonEmpty = /* @__PURE__ */ new Set();
|
|
15248
|
+
for (const [entry2, count2] of results) {
|
|
15249
|
+
if (count2 > 0) nonEmpty.add(entry2);
|
|
15250
|
+
}
|
|
15251
|
+
return nonEmpty;
|
|
15252
|
+
}
|
|
15253
|
+
#cloneWithGraph(graph, ref, schemaMetadata = this.#schemaMetadata) {
|
|
15254
|
+
const next = new _Store(graph, this.#backend, this.#options, schemaMetadata);
|
|
15255
|
+
if (ref !== void 0) ref.current = next;
|
|
15256
|
+
return next;
|
|
15257
|
+
}
|
|
13642
15258
|
/**
|
|
13643
15259
|
* Closes the store and releases underlying resources.
|
|
13644
15260
|
*
|
|
@@ -13737,12 +15353,46 @@ var Store = class {
|
|
|
13737
15353
|
function createStore(graph, backend, options) {
|
|
13738
15354
|
return new Store(graph, backend, options);
|
|
13739
15355
|
}
|
|
15356
|
+
function isKnownEdgeKind(graph, name) {
|
|
15357
|
+
return Object.hasOwn(graph.edges, name);
|
|
15358
|
+
}
|
|
15359
|
+
function setsEqual(a, b) {
|
|
15360
|
+
if (a.size !== b.size) return false;
|
|
15361
|
+
for (const value of a) if (!b.has(value)) return false;
|
|
15362
|
+
return true;
|
|
15363
|
+
}
|
|
15364
|
+
function schemaMetadataFromRow(row) {
|
|
15365
|
+
if (row === void 0) return UNKNOWN_SCHEMA_METADATA;
|
|
15366
|
+
return Object.freeze({
|
|
15367
|
+
schemaVersion: row.version,
|
|
15368
|
+
schemaHash: row.schema_hash
|
|
15369
|
+
});
|
|
15370
|
+
}
|
|
15371
|
+
function schemaMetadataForResult(activeRow, result) {
|
|
15372
|
+
if (result.status === "initialized" || result.status === "migrated") {
|
|
15373
|
+
return schemaMetadataFromRow(result.committedRow);
|
|
15374
|
+
}
|
|
15375
|
+
return schemaMetadataFromRow(activeRow);
|
|
15376
|
+
}
|
|
13740
15377
|
async function createStoreWithSchema(graph, backend, options) {
|
|
13741
|
-
const
|
|
13742
|
-
|
|
15378
|
+
const {
|
|
15379
|
+
graph: merged,
|
|
15380
|
+
activeRow,
|
|
15381
|
+
storedSchema
|
|
15382
|
+
} = await loadAndMergeGraphExtensionDocument(backend, graph);
|
|
15383
|
+
const result = await ensureSchema(backend, merged, {
|
|
15384
|
+
...options,
|
|
15385
|
+
preloaded: { activeRow, storedSchema }
|
|
15386
|
+
});
|
|
15387
|
+
const store = new Store(
|
|
15388
|
+
merged,
|
|
15389
|
+
backend,
|
|
15390
|
+
options,
|
|
15391
|
+
schemaMetadataForResult(activeRow, result)
|
|
15392
|
+
);
|
|
13743
15393
|
return [store, result];
|
|
13744
15394
|
}
|
|
13745
15395
|
|
|
13746
|
-
export { DEFAULT_SQL_SCHEMA, ExecutableAggregateQuery, ExecutableQuery, MAX_EXPLICIT_RECURSIVE_DEPTH, MAX_RECURSIVE_DEPTH, PreparedQuery, QueryBuilder, StoreSearch, UnionableQuery, avg,
|
|
15396
|
+
export { DEFAULT_SQL_SCHEMA, ExecutableAggregateQuery, ExecutableQuery, MAX_EXPLICIT_RECURSIVE_DEPTH, MAX_RECURSIVE_DEPTH, PreparedQuery, QueryBuilder, StoreSearch, UnionableQuery, avg, composeFragments, count, countDistinct, countDistinctEdges, countEdges, createExternalRef, createFragment, createQueryBuilder, createSqlSchema, createStore, createStoreWithSchema, defineSubgraphProject, exists, externalRef, field, fieldRef, generateId, getExternalRefTable, having, havingEq, havingGt, havingGte, havingLt, havingLte, inSubquery, isExternalRefSchema, isParameterRef, limitFragment, max, metaEdge, min, notExists, notInSubquery, offsetFragment, orderByFragment, param, sum };
|
|
13747
15397
|
//# sourceMappingURL=index.js.map
|
|
13748
15398
|
//# sourceMappingURL=index.js.map
|