@utaba/deep-memory-storage-neo4j 0.19.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/LICENSE +190 -0
- package/README.md +337 -0
- package/dist/index.cjs +3324 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +430 -0
- package/dist/index.d.ts +430 -0
- package/dist/index.js +3299 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import { EnsureSchemaResult, EntityReadOptions, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
|
|
2
|
+
import { UsageSink, StorageRepositoryConfig, StoredRepository, RepositoryFilter, PaginatedResult, StoredRepositorySummary, RepositoryUpdate, DeleteProgressCallback, MemoryVocabulary, PaginationOptions, VocabularyChangeRecord, StoredEntity, StoredEntityUpdate, StorageFindQuery, StoredRelationship, RelationshipQueryOptions, TraversalSpec, TraversalResult, StorageExploreOptions, StorageNeighborhood, StoragePathOptions, StoragePathResult, StorageTimelineOptions, StorageTimelineResult, ExportChunk, ImportChunk, BulkImportOptions, BulkImportResult, RepositoryStats } from '@utaba/deep-memory/types';
|
|
3
|
+
|
|
4
|
+
/** Connection configuration. */
|
|
5
|
+
interface Neo4jConnectionConfig {
|
|
6
|
+
/** Bolt URI, e.g. `bolt://localhost:7687` or `neo4j+s://aura-host`. */
|
|
7
|
+
uri: string;
|
|
8
|
+
/** Username for basic auth. */
|
|
9
|
+
username: string;
|
|
10
|
+
/** Password for basic auth. */
|
|
11
|
+
password: string;
|
|
12
|
+
/**
|
|
13
|
+
* Database name. Per the driver manual, this should be specified explicitly
|
|
14
|
+
* even on single-database Community instances. Defaults to `'neo4j'`.
|
|
15
|
+
*/
|
|
16
|
+
database?: string;
|
|
17
|
+
/** User-agent string sent on the Bolt handshake. */
|
|
18
|
+
userAgent?: string;
|
|
19
|
+
/** Maximum time (ms) the driver will retry a managed transaction. */
|
|
20
|
+
maxTransactionRetryTime?: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Configuration for `Neo4jStorageProvider`. */
|
|
24
|
+
interface Neo4jStorageProviderConfig extends Neo4jConnectionConfig {
|
|
25
|
+
/**
|
|
26
|
+
* Optional usage sink. When provided, the provider emits one
|
|
27
|
+
* `OperationUsage` record per public method call. The record's `value` is
|
|
28
|
+
* the aggregated `summary.resultConsumedAfter` (server-side ms) across
|
|
29
|
+
* every Bolt round-trip the operation produced; `unit` is `'server_ms'`.
|
|
30
|
+
*
|
|
31
|
+
* The sink is **never** plumbed through to AI-agent-facing surfaces — MCP
|
|
32
|
+
* tools must not expose RU / server-time figures to model responses.
|
|
33
|
+
*/
|
|
34
|
+
reportUsage?: UsageSink;
|
|
35
|
+
/**
|
|
36
|
+
* When `true`, prepend `PROFILE` to every compiled traversal query and
|
|
37
|
+
* surface the resulting plan summary under `details.profile` on the sink
|
|
38
|
+
* record. Defaults to `false` — `PROFILE` more than doubles wall-clock on
|
|
39
|
+
* short traversals (see plan §D14 and the Phase 9 probe results), so the
|
|
40
|
+
* cost is worth paying only when an operator is actively investigating
|
|
41
|
+
* planner behaviour.
|
|
42
|
+
*/
|
|
43
|
+
profileTraversals?: boolean;
|
|
44
|
+
}
|
|
45
|
+
declare class Neo4jStorageProvider {
|
|
46
|
+
private readonly connection;
|
|
47
|
+
private readonly traversalExecutor;
|
|
48
|
+
private initialized;
|
|
49
|
+
/**
|
|
50
|
+
* In-process vocabulary cache. Reads hit this map first; writes inside this
|
|
51
|
+
* process invalidate the entry so cache hits stay coherent with the local
|
|
52
|
+
* write. Cross-process staleness is bounded by `VOCABULARY_CACHE_TTL_MS`.
|
|
53
|
+
*/
|
|
54
|
+
private readonly vocabularyCache;
|
|
55
|
+
/**
|
|
56
|
+
* Captured at construction so `exportAll`'s `trackIterable` can emit
|
|
57
|
+
* outside the Proxy's promise-resolution path. The Proxy itself relies on
|
|
58
|
+
* the same `safeSink` captured by closure; this field exists for the
|
|
59
|
+
* streaming-iterator case only.
|
|
60
|
+
*/
|
|
61
|
+
private readonly reportUsage;
|
|
62
|
+
constructor(config: Neo4jStorageProviderConfig);
|
|
63
|
+
initialize(): Promise<void>;
|
|
64
|
+
dispose(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Idempotent constraint / index DDL plus a single `_Meta` schema-version
|
|
67
|
+
* handshake. Safe to call repeatedly — `CREATE ... IF NOT EXISTS` makes
|
|
68
|
+
* each statement a no-op on subsequent runs.
|
|
69
|
+
*
|
|
70
|
+
* Neo4j Community has no per-tenant database concept — `databaseCreated`
|
|
71
|
+
* is always `false`. Operators are responsible for the target database
|
|
72
|
+
* existing before the provider connects.
|
|
73
|
+
*/
|
|
74
|
+
ensureSchema(): Promise<EnsureSchemaResult>;
|
|
75
|
+
private readSchemaVersion;
|
|
76
|
+
private writeSchemaVersion;
|
|
77
|
+
/**
|
|
78
|
+
* Create a new repository. Fixed-shape `CREATE` template — every optional
|
|
79
|
+
* field is bound on every call so the server plan-caches one entry across
|
|
80
|
+
* all repository creates. Optional fields bound as `null` are not persisted
|
|
81
|
+
* (Cypher drops null property values on write — symmetric with read).
|
|
82
|
+
*
|
|
83
|
+
* The `(:_Repository) REQUIRE n.repositoryId IS UNIQUE` constraint surfaces
|
|
84
|
+
* duplicates as `Neo.ClientError.Schema.ConstraintValidationFailed`, which
|
|
85
|
+
* `mapDriverError({ kind: 'repository', ... })` routes to
|
|
86
|
+
* `DuplicateRepositoryError`.
|
|
87
|
+
*/
|
|
88
|
+
createRepository(config: StorageRepositoryConfig): Promise<StoredRepository>;
|
|
89
|
+
getRepository(repositoryId: string): Promise<StoredRepository | null>;
|
|
90
|
+
listRepositories(filter?: RepositoryFilter): Promise<PaginatedResult<StoredRepositorySummary>>;
|
|
91
|
+
/**
|
|
92
|
+
* Variable-shape Cypher (per D23 trade-off — repository writes are rare so
|
|
93
|
+
* the plan-cache cost is negligible). Projection-on-write returns the
|
|
94
|
+
* updated row in one round-trip; empty-rowset → `RepositoryNotFoundError`.
|
|
95
|
+
*/
|
|
96
|
+
updateRepository(repositoryId: string, updates: RepositoryUpdate): Promise<StoredRepository>;
|
|
97
|
+
/**
|
|
98
|
+
* Drop every node and relationship scoped to `repositoryId`, including the
|
|
99
|
+
* `_Repository` node itself. Two-stage chunked wipe driven by app-side loops
|
|
100
|
+
* so the progress callback fires at a useful cadence:
|
|
101
|
+
*
|
|
102
|
+
* 1. Drain relationships in batches via `CALL ( ) { ... } IN TRANSACTIONS`.
|
|
103
|
+
* 2. Drain nodes (entities + system) in batches via the same form with
|
|
104
|
+
* `DETACH DELETE` (catches any straggler edges).
|
|
105
|
+
*
|
|
106
|
+
* `IN TRANSACTIONS` can only run on auto-commit sessions — `executeWrite`
|
|
107
|
+
* fails with `Neo.DatabaseError.Transaction.TransactionStartFailed` per
|
|
108
|
+
* probe P13. The chokepoint's `executeImplicitInTransactions` is the only
|
|
109
|
+
* legitimate entry point for this pattern.
|
|
110
|
+
*/
|
|
111
|
+
deleteRepository(repositoryId: string, onProgress?: DeleteProgressCallback): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Drop every entity and relationship scoped to `repositoryId` but preserve
|
|
114
|
+
* the `_Repository` and `_Vocabulary` / `_VocabularyChangeLog` system nodes.
|
|
115
|
+
* Same chunked-wipe contract as `deleteRepository`, restricted to the
|
|
116
|
+
* `:_Entity` umbrella label for nodes.
|
|
117
|
+
*/
|
|
118
|
+
deleteAllContents(repositoryId: string, onProgress?: DeleteProgressCallback): Promise<{
|
|
119
|
+
deletedEntities: number;
|
|
120
|
+
deletedRelationships: number;
|
|
121
|
+
}>;
|
|
122
|
+
/**
|
|
123
|
+
* One-shot pre-count used by `deleteRepository` / `deleteAllContents`. The
|
|
124
|
+
* entity count uses the umbrella `:_Entity` label so system nodes
|
|
125
|
+
* (`_Repository` / `_Vocabulary`) are excluded — that matches the
|
|
126
|
+
* user-facing semantics of the progress callback.
|
|
127
|
+
*
|
|
128
|
+
* The relationship count uses the **directed** pattern `()-[r]->()`: every
|
|
129
|
+
* edge is written in its stored direction (D5), so the directed pattern
|
|
130
|
+
* binds each edge to exactly one row. An undirected `-` pattern would
|
|
131
|
+
* double-count every edge whose endpoints are distinct vertices, because
|
|
132
|
+
* Cypher enumerates the pattern from both directions.
|
|
133
|
+
*/
|
|
134
|
+
private countRepositoryContents;
|
|
135
|
+
/**
|
|
136
|
+
* Read the vocabulary for a repository. Cache-aware: cache hits return
|
|
137
|
+
* synchronously with zero Bolt round-trips. The TRACKED_METHODS proxy still
|
|
138
|
+
* fires for every call — the sink record on a cache hit carries
|
|
139
|
+
* `details.calls === 0` and `value === 0`, which is the contract the sink
|
|
140
|
+
* expects to express "this operation ran but did no server work".
|
|
141
|
+
*/
|
|
142
|
+
getVocabulary(repositoryId: string): Promise<MemoryVocabulary>;
|
|
143
|
+
/**
|
|
144
|
+
* Cached vocabulary read used by `getVocabulary` and (in later phases) by
|
|
145
|
+
* traversal compilation. The vocabulary is compile-time context for the
|
|
146
|
+
* Cypher compiler — it changes on the order of once per session, but the
|
|
147
|
+
* traversal hot path would otherwise pay one round-trip per call. The cache
|
|
148
|
+
* flips that to one round-trip per TTL window.
|
|
149
|
+
*
|
|
150
|
+
* Reads inside an active usage scope still record a round-trip when a fetch
|
|
151
|
+
* actually happens (cache miss); cache hits emit no round-trip and therefore
|
|
152
|
+
* contribute nothing to the scope.
|
|
153
|
+
*/
|
|
154
|
+
private getVocabularyCached;
|
|
155
|
+
/** Drop the cache entry for a repository — call after every vocabulary write. */
|
|
156
|
+
private invalidateVocabularyCache;
|
|
157
|
+
/**
|
|
158
|
+
* Upsert the vocabulary for a repository. Invalidates the in-process cache
|
|
159
|
+
* on success so subsequent reads observe the new state immediately within
|
|
160
|
+
* this process (cross-process staleness is bounded by the 60 s TTL).
|
|
161
|
+
*/
|
|
162
|
+
saveVocabulary(repositoryId: string, vocabulary: MemoryVocabulary): Promise<void>;
|
|
163
|
+
/**
|
|
164
|
+
* Page the vocabulary change-log newest first. Writes land in
|
|
165
|
+
* `proposeVocabularyExtension` (out of scope here) — this method only reads
|
|
166
|
+
* the `_VocabularyChangeLog` nodes back, ordered by `proposedAt` to match
|
|
167
|
+
* the audit semantic on `VocabularyChangeRecord`.
|
|
168
|
+
*/
|
|
169
|
+
getVocabularyChangeLog(repositoryId: string, options?: PaginationOptions): Promise<PaginatedResult<VocabularyChangeRecord>>;
|
|
170
|
+
/**
|
|
171
|
+
* Create a new entity via fixed-shape `CREATE` + catch on the uniqueness
|
|
172
|
+
* constraint. A `MERGE`-with-discriminator alternative is marginally faster
|
|
173
|
+
* on the happy path but mutates the existing node on collisions, writing
|
|
174
|
+
* a discriminator property onto durable graph state the caller never
|
|
175
|
+
* requested — correctness wins over the marginal perf delta.
|
|
176
|
+
*/
|
|
177
|
+
createEntity(repositoryId: string, entity: StoredEntity): Promise<StoredEntity>;
|
|
178
|
+
/** Read a single entity by id; `null` when not found. */
|
|
179
|
+
getEntity(repositoryId: string, entityId: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
|
|
180
|
+
/** Read a single entity by slug; `null` when not found. */
|
|
181
|
+
getEntityBySlug(repositoryId: string, slug: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
|
|
182
|
+
/**
|
|
183
|
+
* Batch read by ids. Absent ids do not appear in the returned `Map`; empty
|
|
184
|
+
* input returns an empty map without a round-trip.
|
|
185
|
+
*/
|
|
186
|
+
getEntities(repositoryId: string, entityIds: string[], options?: EntityReadOptions): Promise<Map<string, StoredEntity>>;
|
|
187
|
+
/**
|
|
188
|
+
* Variable-shape projection-on-write update (D23) — single round-trip
|
|
189
|
+
* MATCH+SET+RETURN. Empty record array → `EntityNotFoundError`.
|
|
190
|
+
*/
|
|
191
|
+
updateEntity(repositoryId: string, entityId: string, updates: StoredEntityUpdate): Promise<StoredEntity>;
|
|
192
|
+
/**
|
|
193
|
+
* Delete a single entity (and its incident relationships via `DETACH
|
|
194
|
+
* DELETE`). Throws `EntityNotFoundError` when the match returns zero rows.
|
|
195
|
+
*/
|
|
196
|
+
deleteEntity(repositoryId: string, entityId: string): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Bulk delete by ids — single round-trip. Returns the ids actually
|
|
199
|
+
* deleted; missing ids land in `notFound`.
|
|
200
|
+
*/
|
|
201
|
+
deleteEntities(repositoryId: string, ids: string[]): Promise<{
|
|
202
|
+
deleted: string[];
|
|
203
|
+
notFound: string[];
|
|
204
|
+
}>;
|
|
205
|
+
/**
|
|
206
|
+
* Delete every entity of a type plus their incident relationships, with
|
|
207
|
+
* exact counts (entity + relationship) returned in one round-trip — a
|
|
208
|
+
* strict improvement over Cosmos's `deletedRelationships: undefined` path
|
|
209
|
+
* (Gremlin would fan out across every partition the type touches).
|
|
210
|
+
*/
|
|
211
|
+
deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
|
|
212
|
+
deletedEntities: number;
|
|
213
|
+
deletedRelationships: number | undefined;
|
|
214
|
+
}>;
|
|
215
|
+
/**
|
|
216
|
+
* Page entities matching a `StorageFindQuery`. Parallel data + count Cypher
|
|
217
|
+
* pair; `total` is always exact because every filter (entity-type, property
|
|
218
|
+
* equality, search term, provenance) is server-side via either a typed
|
|
219
|
+
* predicate or the `dm_entity_text` fulltext index. Search-term queries
|
|
220
|
+
* order by Lucene score descending; non-search queries order by `n.id` to
|
|
221
|
+
* pin pagination determinism across slices.
|
|
222
|
+
*/
|
|
223
|
+
findEntities(repositoryId: string, query: StorageFindQuery, options?: EntityReadOptions): Promise<PaginatedResult<StoredEntity>>;
|
|
224
|
+
/**
|
|
225
|
+
* Create a relationship. Both endpoint entities are matched under the
|
|
226
|
+
* repository scope before the edge is created, so cross-repository edges
|
|
227
|
+
* are structurally impossible to write (D3b layer 3). A missing endpoint
|
|
228
|
+
* surfaces as `EntityNotFoundError` carrying the absent id.
|
|
229
|
+
*/
|
|
230
|
+
createRelationship(repositoryId: string, relationship: StoredRelationship): Promise<StoredRelationship>;
|
|
231
|
+
/** Read a single relationship by id; `null` when not found. */
|
|
232
|
+
getRelationship(repositoryId: string, relationshipId: string): Promise<StoredRelationship | null>;
|
|
233
|
+
/**
|
|
234
|
+
* Page an entity's incident relationships. `direction: 'out' | 'in'`
|
|
235
|
+
* additionally surfaces edges flagged `bidirectional: true` from the
|
|
236
|
+
* opposite endpoint, mirroring the Cosmos read-time duplication of bidir
|
|
237
|
+
* edges. `propertyFilters` is applied client-side and reports
|
|
238
|
+
* `total: undefined` in that branch — same trade-off as the Cosmos
|
|
239
|
+
* provider, because relationship `properties` is a JSON blob with no
|
|
240
|
+
* per-key index.
|
|
241
|
+
*/
|
|
242
|
+
getEntityRelationships(repositoryId: string, entityId: string, options?: RelationshipQueryOptions): Promise<PaginatedResult<StoredRelationship>>;
|
|
243
|
+
/** Drop a single relationship by id. No-op when the id does not match. */
|
|
244
|
+
deleteRelationship(repositoryId: string, relationshipId: string): Promise<void>;
|
|
245
|
+
/**
|
|
246
|
+
* Bulk drop by ids — single round-trip. Returns the ids actually deleted;
|
|
247
|
+
* missing ids land in `notFound`.
|
|
248
|
+
*/
|
|
249
|
+
deleteRelationships(repositoryId: string, ids: string[]): Promise<{
|
|
250
|
+
deleted: string[];
|
|
251
|
+
notFound: string[];
|
|
252
|
+
}>;
|
|
253
|
+
/**
|
|
254
|
+
* Drop every relationship of a type in the repository. Returns an exact
|
|
255
|
+
* delete count in a single round-trip.
|
|
256
|
+
*/
|
|
257
|
+
deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
|
|
258
|
+
deletedRelationships: number;
|
|
259
|
+
}>;
|
|
260
|
+
/**
|
|
261
|
+
* Capabilities surface used by the dispatcher to decide whether a given
|
|
262
|
+
* `TraversalSpec` shape is supported natively. Strict improvement over the
|
|
263
|
+
* Cosmos provider in two cells: `supportsAggregation` is `true` (Cypher's
|
|
264
|
+
* native aggregation makes `count` / per-key projection a one-statement
|
|
265
|
+
* shape) and the runtime supports every other traversal lever.
|
|
266
|
+
*/
|
|
267
|
+
getCapabilities(): GraphTraversalCapabilities;
|
|
268
|
+
/**
|
|
269
|
+
* Execute a `TraversalSpec` against this repository's subgraph. The
|
|
270
|
+
* provider owns Cypher compilation; the spec stays language-agnostic.
|
|
271
|
+
* `track('traverse')` opens the per-operation usage scope so every
|
|
272
|
+
* `executeQuery` round-trip the executor performs aggregates into a single
|
|
273
|
+
* `OperationUsage` record.
|
|
274
|
+
*/
|
|
275
|
+
traverse(repositoryId: string, spec: TraversalSpec): Promise<TraversalResult>;
|
|
276
|
+
/**
|
|
277
|
+
* Internal compile → submit → project pipeline. Shared by the public
|
|
278
|
+
* `traverse` method and by the compiler-model rewrites of
|
|
279
|
+
* `exploreNeighborhood` / `findPaths`, which both consume the raw stored
|
|
280
|
+
* shape to rebuild their storage-level outputs.
|
|
281
|
+
*/
|
|
282
|
+
private traverseInternal;
|
|
283
|
+
/**
|
|
284
|
+
* Lower-level compile + submit + parse helper. Fetches the cached
|
|
285
|
+
* vocabulary once (D16) and hands it to the executor; the executor handles
|
|
286
|
+
* the repositoryId-scope rewrite, optional PROFILE prefix, and Path-object
|
|
287
|
+
* parsing.
|
|
288
|
+
*/
|
|
289
|
+
private executeRawTraversal;
|
|
290
|
+
/**
|
|
291
|
+
* BFS-like neighbourhood exploration. For each depth `d` from 1 to
|
|
292
|
+
* `options.depth`, compile a cumulative `'all'`-mode spec with `d` discrete
|
|
293
|
+
* `'both'`-direction steps, run it through the executor, and walk one BFS
|
|
294
|
+
* layer client-side from the previous frontier using the returned edges.
|
|
295
|
+
*
|
|
296
|
+
* Round-trips per call: `options.depth`. Server-side step direction is
|
|
297
|
+
* fixed to `'both'` (catches every edge in either direction); the
|
|
298
|
+
* directional + bidirectional filter and entity-type filter run client-side
|
|
299
|
+
* during layer reconstruction — both to preserve the observable contract
|
|
300
|
+
* shared with the Cosmos provider and because the compiler's prefix walk at
|
|
301
|
+
* each depth is intentionally unfiltered so deeper layers stay reachable
|
|
302
|
+
* through any intermediate.
|
|
303
|
+
*/
|
|
304
|
+
exploreNeighborhood(repositoryId: string, entityId: string, options: StorageExploreOptions): Promise<StorageNeighborhood>;
|
|
305
|
+
/**
|
|
306
|
+
* Path finding between two entities. Single round-trip via a variable-length
|
|
307
|
+
* `MATCH p = (s)-[*1..N]-(t)` pattern; the compiler's path-binding emission
|
|
308
|
+
* lets the executor recover ordered nodes and relationships via `nodes(p)`
|
|
309
|
+
* / `relationships(p)`. The default `DIFFERENT RELATIONSHIPS` match mode in
|
|
310
|
+
* Cypher 25 prevents edge reuse within a single path — no explicit dedup
|
|
311
|
+
* filter is needed.
|
|
312
|
+
*
|
|
313
|
+
* The traversal walks the graph topologically regardless of relationship
|
|
314
|
+
* directionality (a path is defined by reachability, not semantic
|
|
315
|
+
* direction); entity-type and relationship-property filters apply during
|
|
316
|
+
* compilation, target filtering happens post-fetch.
|
|
317
|
+
*/
|
|
318
|
+
findPaths(repositoryId: string, sourceId: string, targetId: string, options: StoragePathOptions): Promise<StoragePathResult>;
|
|
319
|
+
/**
|
|
320
|
+
* Reconstruct the timeline event stream for an entity. One server
|
|
321
|
+
* round-trip: the centre entity's provenance scalars plus every incident
|
|
322
|
+
* edge's id + createdAt arrive in a single tuple via `OPTIONAL MATCH +
|
|
323
|
+
* collect()`. The provider walks the row client-side to emit
|
|
324
|
+
* `entity:created` / `entity:updated` / `relationship:created` events.
|
|
325
|
+
*
|
|
326
|
+
* Cosmos pays two round-trips for the same information because Gremlin
|
|
327
|
+
* cannot bind an aggregated edge list to a vertex projection in one shot —
|
|
328
|
+
* a platform divergence, not an inherent trade-off.
|
|
329
|
+
*/
|
|
330
|
+
getTimeline(repositoryId: string, entityId: string, options: StorageTimelineOptions): Promise<StorageTimelineResult>;
|
|
331
|
+
/**
|
|
332
|
+
* Stream every entity and every relationship in the repository as
|
|
333
|
+
* cursor-paginated chunks. Entities come first, then relationships; each
|
|
334
|
+
* chunk carries a monotonic `sequence` and an `isLast` flag.
|
|
335
|
+
*
|
|
336
|
+
* The Proxy-based tracking flow does not apply here. Tracked methods emit
|
|
337
|
+
* one sink record at promise resolution; an `AsyncIterable` returns
|
|
338
|
+
* synchronously, before any chunk has streamed. Instead, `trackIterable`
|
|
339
|
+
* wraps the underlying generator in its own `UsageScope` that opens at
|
|
340
|
+
* iterator creation, records each round-trip as the consumer pulls the
|
|
341
|
+
* next chunk, and emits one sink record when the iterator drains — so the
|
|
342
|
+
* resulting record aggregates server time across every chunk fetch.
|
|
343
|
+
*/
|
|
344
|
+
exportAll(repositoryId: string): AsyncIterable<ExportChunk>;
|
|
345
|
+
/**
|
|
346
|
+
* Run a bulk import: every entity then every relationship from the input
|
|
347
|
+
* chunks lands in the repository. `skipExistenceCheck: true` uses CREATE
|
|
348
|
+
* for the absolute peak throughput; `false` (default) uses MERGE for
|
|
349
|
+
* idempotent re-imports.
|
|
350
|
+
*
|
|
351
|
+
* Returns a single aggregate `BulkImportResult` spanning every chunk.
|
|
352
|
+
* Per-row failures land in `result.errors`; surviving rows still count
|
|
353
|
+
* toward `entitiesImported` / `relationshipsImported`.
|
|
354
|
+
*/
|
|
355
|
+
importBulk(repositoryId: string, data: ImportChunk[], options?: BulkImportOptions): Promise<BulkImportResult>;
|
|
356
|
+
/**
|
|
357
|
+
* Wrap an `AsyncIterable` so every chunk it produces is generated inside a
|
|
358
|
+
* single shared `UsageScope`. The scope aggregates `summary.resultConsumedAfter`
|
|
359
|
+
* across every round-trip the iterator performs; one sink record fires when
|
|
360
|
+
* the iterator drains (or is closed / thrown into).
|
|
361
|
+
*
|
|
362
|
+
* Implementation mirrors the Cosmos `trackIterable` precedent: each
|
|
363
|
+
* `iter.next()` call re-enters the scope via `runInUsageScope`. The
|
|
364
|
+
* `AsyncLocalStorage` chain links async work performed inside the
|
|
365
|
+
* generator body to the scope for the duration of that step. Between
|
|
366
|
+
* `next()` calls the scope is dormant — the consumer's awaits do not
|
|
367
|
+
* accumulate into it, which is exactly the desired semantics.
|
|
368
|
+
*
|
|
369
|
+
* When the sink is absent the wrap degenerates to a pass-through; the
|
|
370
|
+
* Proxy's `createSafeSink` short-circuit covers the no-sink case at
|
|
371
|
+
* construction time, so this method is only reached when `reportUsage` is
|
|
372
|
+
* present.
|
|
373
|
+
*/
|
|
374
|
+
private trackIterable;
|
|
375
|
+
/**
|
|
376
|
+
* Aggregate repository statistics — entity / relationship totals, per-type
|
|
377
|
+
* breakdowns, vocabulary version. Two parallel native-aggregation round-
|
|
378
|
+
* trips (`count(n)` per `entityType`, `count(r)` per `type(r)`); the
|
|
379
|
+
* vocabulary version comes from the cached `_Vocabulary` node so a warm
|
|
380
|
+
* cache costs exactly two round-trips total.
|
|
381
|
+
*
|
|
382
|
+
* Strict improvement over Cosmos's Gremlin `.group().by().by(count())`
|
|
383
|
+
* shape — Cypher's native aggregation collapses each metric to a one-
|
|
384
|
+
* statement plan that hits the `(repositoryId, entityType)` and
|
|
385
|
+
* relationship-property indexes directly.
|
|
386
|
+
*/
|
|
387
|
+
getRepositoryStats(repositoryId: string): Promise<RepositoryStats>;
|
|
388
|
+
/**
|
|
389
|
+
* Execute a raw Cypher statement with caller-supplied bindings.
|
|
390
|
+
*
|
|
391
|
+
* ⚠️ ELEVATED PRIVILEGE — SYSTEM-LEVEL OPERATION ⚠️
|
|
392
|
+
*
|
|
393
|
+
* This method is an unscoped pass-through: it does not filter by
|
|
394
|
+
* repository, does not inject the `$rid` binding, and performs no
|
|
395
|
+
* validation on the Cypher string. A single call can read or mutate any
|
|
396
|
+
* node or relationship in the database regardless of which repository it
|
|
397
|
+
* belongs to.
|
|
398
|
+
*
|
|
399
|
+
* DO NOT expose this method to AI agents, end users, or any untrusted
|
|
400
|
+
* caller. It is intended for:
|
|
401
|
+
* - administrative tooling (migrations, diagnostics, repairs)
|
|
402
|
+
* - internal library operations that need cross-repository reach
|
|
403
|
+
*
|
|
404
|
+
* `repositoryId` is accepted for interface symmetry but is intentionally
|
|
405
|
+
* ignored here — the caller is trusted to scope the query themselves.
|
|
406
|
+
* Because the call is cross-repository by design, the emitted usage
|
|
407
|
+
* record carries no `repositoryId` (see `TRACKED_METHODS`).
|
|
408
|
+
*
|
|
409
|
+
* For agent-facing graph queries use {@link traverse}, which enforces the
|
|
410
|
+
* repositoryId scope predicate.
|
|
411
|
+
*/
|
|
412
|
+
executeNativeQuery(_repositoryId: string, query: string, params?: Record<string, unknown>): Promise<unknown[]>;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
declare const SCHEMA_VERSION = 1;
|
|
416
|
+
/**
|
|
417
|
+
* Returns the constraint / index DDL for the deep-memory Neo4j schema as an
|
|
418
|
+
* array of single statements. Each statement is idempotent via
|
|
419
|
+
* `IF NOT EXISTS`. Callers (the provider's `ensureSchema`, or operators
|
|
420
|
+
* running out-of-band against a managed Neo4j) execute the statements one by
|
|
421
|
+
* one.
|
|
422
|
+
*
|
|
423
|
+
* Composite uniqueness and lookup indexes all lead with `repositoryId` so the
|
|
424
|
+
* planner uses it as the cheap discriminator — see D3b layer 1 in the plan.
|
|
425
|
+
* The fulltext index is unfiltered; callers that query it must add an
|
|
426
|
+
* explicit `WHERE node.repositoryId = $rid` post-filter.
|
|
427
|
+
*/
|
|
428
|
+
declare function getSchemaCypher(): readonly string[];
|
|
429
|
+
|
|
430
|
+
export { Neo4jStorageProvider, type Neo4jStorageProviderConfig, SCHEMA_VERSION, getSchemaCypher };
|