@utaba/deep-memory-storage-cosmosdb 0.16.0 → 0.17.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/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
1
+ import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, EntityReadOptions, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
2
2
  import { UsageSink, StorageRepositoryConfig, StoredRepository, RepositoryFilter, PaginatedResult, StoredRepositorySummary, RepositoryUpdate, DeleteProgressCallback, RepositoryStats, MemoryVocabulary, PaginationOptions, VocabularyChangeRecord, StoredEntity, StoredEntityUpdate, StorageFindQuery, StoredRelationship, RelationshipQueryOptions, StorageExploreOptions, StorageNeighborhood, StoragePathOptions, StoragePathResult, StorageTimelineOptions, StorageTimelineResult, ExportChunk, ImportChunk, BulkImportOptions, BulkImportResult, TraversalSpec, TraversalResult } from '@utaba/deep-memory/types';
3
3
  import gremlin from 'gremlin';
4
4
 
@@ -36,9 +36,17 @@ interface CosmosDbProviderConfig {
36
36
  */
37
37
  declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvider {
38
38
  private readonly conn;
39
+ private readonly docClient;
39
40
  private readonly config;
40
41
  private readonly compiler;
41
42
  private readonly reportUsage;
43
+ /**
44
+ * Per-process vocabulary cache, keyed by repositoryId. Read lazily by
45
+ * `traverseImpl` via `getVocabularyCached`; invalidated on `saveVocabulary`.
46
+ * Each provider instance owns its own cache so isolated test providers do
47
+ * not share state.
48
+ */
49
+ private readonly vocabularyCache;
42
50
  constructor(config: CosmosDbProviderConfig);
43
51
  /**
44
52
  * Run the given operation inside an async-local usage scope. While the
@@ -68,6 +76,18 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
68
76
  dispose(): Promise<void>;
69
77
  ensureSchema(): Promise<EnsureSchemaResult>;
70
78
  private ensureSchemaImpl;
79
+ /**
80
+ * Step E — verify the container's indexing policy covers every path that
81
+ * the SQL `findEntities` rewrite hits. The probe (2026-05-26) confirmed
82
+ * code-managed containers get the Cosmos default policy (everything
83
+ * indexed). This guard is for operator-facing protection: externally
84
+ * provisioned containers (ARM/Bicep) can have `excludedPaths` set, which
85
+ * would force `findEntities` to scan rather than index-lookup.
86
+ *
87
+ * Single GET against the colls resource, minimal RU. Never fails
88
+ * `ensureSchema` — a diagnostic must not break provisioning.
89
+ */
90
+ private runIndexingPolicyDiagnostic;
71
91
  /** Derive the REST endpoint from config — either explicit or from the Gremlin endpoint host. */
72
92
  private getRestEndpoint;
73
93
  createRepository(config: StorageRepositoryConfig): Promise<StoredRepository>;
@@ -81,24 +101,49 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
81
101
  }>;
82
102
  getRepositoryStats(repositoryId: string): Promise<RepositoryStats>;
83
103
  getVocabulary(repositoryId: string): Promise<MemoryVocabulary>;
104
+ /**
105
+ * Cached vocabulary read used by traversal compilation. The vocabulary is
106
+ * compile-time context for the GremlinCompiler — it changes on the order of
107
+ * once per session, but the traversal hot path pays one round-trip per call
108
+ * to fetch it. The cache flips that to one round-trip per TTL window.
109
+ *
110
+ * Reads inside an active usage scope are still recorded if a fetch happens
111
+ * (cache miss); cache hits emit no round-trip and therefore no usage entry.
112
+ */
113
+ private getVocabularyCached;
114
+ /** Drop the cache entry for a repository — call after any vocabulary write. */
115
+ private invalidateVocabularyCache;
84
116
  saveVocabulary(repositoryId: string, vocabulary: MemoryVocabulary): Promise<void>;
85
117
  getVocabularyChangeLog(repositoryId: string, options?: PaginationOptions): Promise<PaginatedResult<VocabularyChangeRecord>>;
86
118
  createEntity(repositoryId: string, entity: StoredEntity): Promise<StoredEntity>;
87
- getEntity(repositoryId: string, entityId: string): Promise<StoredEntity | null>;
88
- getEntityBySlug(repositoryId: string, slug: string): Promise<StoredEntity | null>;
89
- getEntities(repositoryId: string, entityIds: string[]): Promise<Map<string, StoredEntity>>;
119
+ getEntity(repositoryId: string, entityId: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
120
+ getEntityBySlug(repositoryId: string, slug: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
121
+ getEntities(repositoryId: string, entityIds: string[], options?: EntityReadOptions): Promise<Map<string, StoredEntity>>;
90
122
  updateEntity(repositoryId: string, entityId: string, updates: StoredEntityUpdate): Promise<StoredEntity>;
91
123
  deleteEntity(repositoryId: string, entityId: string): Promise<void>;
92
124
  deleteEntities(repositoryId: string, ids: string[]): Promise<{
93
125
  deleted: string[];
94
126
  notFound: string[];
95
127
  }>;
128
+ /**
129
+ * Single round-trip bulk-delete via the aggregate-side-effect pattern:
130
+ * collapses the per-chunk existence-check + drop into one Gremlin call.
131
+ *
132
+ * g.V()...hasId(within(...)).has('entityType')
133
+ * .aggregate('found').by('id') // collects the ids that match
134
+ * .drop() // drops the vertices (and cascaded edges)
135
+ * .cap('found') // emits the bucket as the single result
136
+ *
137
+ * The bucket is always emitted as a single list item — empty when nothing
138
+ * matched (probe-verified 2026-05-25, local-tests/phase7-shape-probe.mjs).
139
+ * `notFound` is derived client-side as the set difference.
140
+ */
96
141
  private deleteEntitiesImpl;
97
142
  deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
98
143
  deletedEntities: number;
99
- deletedRelationships: number;
144
+ deletedRelationships: number | undefined;
100
145
  }>;
101
- findEntities(repositoryId: string, query: StorageFindQuery): Promise<PaginatedResult<StoredEntity>>;
146
+ findEntities(repositoryId: string, query: StorageFindQuery, options?: EntityReadOptions): Promise<PaginatedResult<StoredEntity>>;
102
147
  createRelationship(repositoryId: string, relationship: StoredRelationship): Promise<StoredRelationship>;
103
148
  getRelationship(repositoryId: string, relationshipId: string): Promise<StoredRelationship | null>;
104
149
  getEntityRelationships(repositoryId: string, entityId: string, options?: RelationshipQueryOptions): Promise<PaginatedResult<StoredRelationship>>;
@@ -107,12 +152,69 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
107
152
  deleted: string[];
108
153
  notFound: string[];
109
154
  }>;
155
+ /**
156
+ * Single round-trip bulk relationship delete via the aggregate-side-effect
157
+ * pattern: collapses the per-chunk existence-check + drop into one Gremlin
158
+ * call. Gremlin drop on edges is routed by the engine and the bucket gives
159
+ * back the exact ids that were actually dropped, so `notFound` can be
160
+ * derived client-side.
161
+ *
162
+ * Source-id partition routing is not exposed on this method (the public
163
+ * surface accepts only edge ids), so the lookup may fan out across
164
+ * partitions — see [docs/cosmosdb-gremlin-compatibility.md §`g.E().has`
165
+ * doesn't always push partition down]. Callers that already hold a
166
+ * StoredRelationship and want partition-scoped routing should add a
167
+ * dedicated method when the need is concrete.
168
+ */
110
169
  private deleteRelationshipsImpl;
111
170
  deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
112
171
  deletedRelationships: number;
113
172
  }>;
114
173
  exploreNeighborhood(repositoryId: string, entityId: string, options: StorageExploreOptions): Promise<StorageNeighborhood>;
174
+ /**
175
+ * Compiler-model implementation of exploreNeighborhood.
176
+ *
177
+ * Strategy: for each depth `d` from 1 to `options.depth`, build a
178
+ * cumulative `TraversalSpec` with `d` steps and `returnMode: 'all'`, run
179
+ * it through {@link executeTraversal}, then walk one BFS layer client-side
180
+ * from the previous frontier using the returned edges.
181
+ *
182
+ * The server-side step direction is fixed to `'both'` (catches every edge
183
+ * in either direction). The directional + bidirectional filter is applied
184
+ * client-side per hop — i.e. `direction: 'outbound'` includes inbound edges
185
+ * where `bidirectional` is true. The CosmosDB Gremlin compiler does not
186
+ * natively express the `union(outE, inE.has(bidirectional))` shape that
187
+ * would push this filter server-side, so it stays client-side; the
188
+ * observable contract (which edges count toward "outbound" given
189
+ * bidirectionality) is preserved.
190
+ *
191
+ * Round-trips per call: `options.depth` (one per layer). The previous BFS
192
+ * was `1 + fanout + fanout² + …` round-trips for the same depth.
193
+ */
194
+ private exploreNeighborhoodImpl;
115
195
  findPaths(repositoryId: string, sourceId: string, targetId: string, options: StoragePathOptions): Promise<StoragePathResult>;
196
+ /**
197
+ * Compiler-model implementation of findPaths.
198
+ *
199
+ * Strategy: build a `TraversalSpec` with a single `'both'` direction step
200
+ * in `repeat()` mode with `emitIntermediates: true` and `returnMode:
201
+ * 'path'`, run it once. The compiler always emits `.simplePath()` in path
202
+ * mode for cycle prevention, producing
203
+ * `.emit().repeat(bothE().otherV()).times(maxDepth).simplePath()
204
+ * .range(...).path().by(<v>).by(<e>)`, which yields paths of every length
205
+ * from 0 (the start vertex alone) to `maxDepth`. Live-probed against the
206
+ * Cosmos emulator 2026-05-25 — see local-tests/phase4-repeat-emit-probe.mjs.
207
+ *
208
+ * The pre-Phase-4 Cosmos BFS in (now-deleted) `packages/storage-cosmosdb/
209
+ * src/queries/traversal.ts` traversed edges with unconditional `bothE()`
210
+ * regardless of the `bidirectional` flag (path discovery is reachability,
211
+ * not semantic direction). Mirror that here by using step direction
212
+ * `'both'` and not applying any direction filter. The plan's §6
213
+ * observable-outputs rule requires preserving this.
214
+ *
215
+ * One round-trip total. The previous BFS was up to `1 + fanout + fanout² + …`.
216
+ */
217
+ private findPathsImpl;
116
218
  getTimeline(repositoryId: string, entityId: string, options: StorageTimelineOptions): Promise<StorageTimelineResult>;
117
219
  exportAll(repositoryId: string): AsyncIterable<ExportChunk>;
118
220
  importBulk(repositoryId: string, data: ImportChunk[], options?: BulkImportOptions): Promise<BulkImportResult>;
@@ -125,7 +227,34 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
125
227
  private trackIterable;
126
228
  getCapabilities(): GraphTraversalCapabilities;
127
229
  traverse(repositoryId: string, spec: TraversalSpec): Promise<TraversalResult>;
128
- private traverseImpl;
230
+ /**
231
+ * Compile + execute a TraversalSpec against this repository's partition.
232
+ *
233
+ * Internal entrypoint shared by `traverse` (the public surface) and the
234
+ * compiler-model rewrites of `exploreNeighborhood` / `findPaths`. Does NOT
235
+ * wrap in `track()` — the outer public method owns its own usage scope, and
236
+ * inner submits accumulate into that scope (the nested-scope guard in
237
+ * `track()` keeps nested public calls from emitting duplicate records).
238
+ */
239
+ private traverseInternal;
240
+ /**
241
+ * Lower-level traversal helper: compiles a spec, submits to Gremlin, and
242
+ * parses the rows into raw {@link StoredEntity} / {@link StoredRelationship}
243
+ * objects (no detail-level projection, no provenance stripping). Used by
244
+ * `traverseInternal` and by the storage-layer rewrites of
245
+ * `exploreNeighborhood` / `findPaths` that need the full stored shape to
246
+ * rebuild `StorageNeighborhood` / `StoragePathResult`.
247
+ *
248
+ * Returns a discriminated bag — only the fields relevant to `spec.returnMode`
249
+ * are populated:
250
+ * - `'terminal'`: `terminalEntities` (in row order, no dedup beyond what the
251
+ * server emitted).
252
+ * - `'all'`: `allEntities` and `allRelationships`, already server-deduped.
253
+ * - `'path'`: `pathRows` plus the `entityMap` / `relationshipMap` lookup
254
+ * tables and `pathRelFirstDirection` for the first-seen direction per
255
+ * deduped edge id.
256
+ */
257
+ private executeTraversal;
129
258
  /**
130
259
  * Execute a raw Gremlin query with caller-supplied bindings.
131
260
  *
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
1
+ import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, EntityReadOptions, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
2
2
  import { UsageSink, StorageRepositoryConfig, StoredRepository, RepositoryFilter, PaginatedResult, StoredRepositorySummary, RepositoryUpdate, DeleteProgressCallback, RepositoryStats, MemoryVocabulary, PaginationOptions, VocabularyChangeRecord, StoredEntity, StoredEntityUpdate, StorageFindQuery, StoredRelationship, RelationshipQueryOptions, StorageExploreOptions, StorageNeighborhood, StoragePathOptions, StoragePathResult, StorageTimelineOptions, StorageTimelineResult, ExportChunk, ImportChunk, BulkImportOptions, BulkImportResult, TraversalSpec, TraversalResult } from '@utaba/deep-memory/types';
3
3
  import gremlin from 'gremlin';
4
4
 
@@ -36,9 +36,17 @@ interface CosmosDbProviderConfig {
36
36
  */
37
37
  declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvider {
38
38
  private readonly conn;
39
+ private readonly docClient;
39
40
  private readonly config;
40
41
  private readonly compiler;
41
42
  private readonly reportUsage;
43
+ /**
44
+ * Per-process vocabulary cache, keyed by repositoryId. Read lazily by
45
+ * `traverseImpl` via `getVocabularyCached`; invalidated on `saveVocabulary`.
46
+ * Each provider instance owns its own cache so isolated test providers do
47
+ * not share state.
48
+ */
49
+ private readonly vocabularyCache;
42
50
  constructor(config: CosmosDbProviderConfig);
43
51
  /**
44
52
  * Run the given operation inside an async-local usage scope. While the
@@ -68,6 +76,18 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
68
76
  dispose(): Promise<void>;
69
77
  ensureSchema(): Promise<EnsureSchemaResult>;
70
78
  private ensureSchemaImpl;
79
+ /**
80
+ * Step E — verify the container's indexing policy covers every path that
81
+ * the SQL `findEntities` rewrite hits. The probe (2026-05-26) confirmed
82
+ * code-managed containers get the Cosmos default policy (everything
83
+ * indexed). This guard is for operator-facing protection: externally
84
+ * provisioned containers (ARM/Bicep) can have `excludedPaths` set, which
85
+ * would force `findEntities` to scan rather than index-lookup.
86
+ *
87
+ * Single GET against the colls resource, minimal RU. Never fails
88
+ * `ensureSchema` — a diagnostic must not break provisioning.
89
+ */
90
+ private runIndexingPolicyDiagnostic;
71
91
  /** Derive the REST endpoint from config — either explicit or from the Gremlin endpoint host. */
72
92
  private getRestEndpoint;
73
93
  createRepository(config: StorageRepositoryConfig): Promise<StoredRepository>;
@@ -81,24 +101,49 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
81
101
  }>;
82
102
  getRepositoryStats(repositoryId: string): Promise<RepositoryStats>;
83
103
  getVocabulary(repositoryId: string): Promise<MemoryVocabulary>;
104
+ /**
105
+ * Cached vocabulary read used by traversal compilation. The vocabulary is
106
+ * compile-time context for the GremlinCompiler — it changes on the order of
107
+ * once per session, but the traversal hot path pays one round-trip per call
108
+ * to fetch it. The cache flips that to one round-trip per TTL window.
109
+ *
110
+ * Reads inside an active usage scope are still recorded if a fetch happens
111
+ * (cache miss); cache hits emit no round-trip and therefore no usage entry.
112
+ */
113
+ private getVocabularyCached;
114
+ /** Drop the cache entry for a repository — call after any vocabulary write. */
115
+ private invalidateVocabularyCache;
84
116
  saveVocabulary(repositoryId: string, vocabulary: MemoryVocabulary): Promise<void>;
85
117
  getVocabularyChangeLog(repositoryId: string, options?: PaginationOptions): Promise<PaginatedResult<VocabularyChangeRecord>>;
86
118
  createEntity(repositoryId: string, entity: StoredEntity): Promise<StoredEntity>;
87
- getEntity(repositoryId: string, entityId: string): Promise<StoredEntity | null>;
88
- getEntityBySlug(repositoryId: string, slug: string): Promise<StoredEntity | null>;
89
- getEntities(repositoryId: string, entityIds: string[]): Promise<Map<string, StoredEntity>>;
119
+ getEntity(repositoryId: string, entityId: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
120
+ getEntityBySlug(repositoryId: string, slug: string, options?: EntityReadOptions): Promise<StoredEntity | null>;
121
+ getEntities(repositoryId: string, entityIds: string[], options?: EntityReadOptions): Promise<Map<string, StoredEntity>>;
90
122
  updateEntity(repositoryId: string, entityId: string, updates: StoredEntityUpdate): Promise<StoredEntity>;
91
123
  deleteEntity(repositoryId: string, entityId: string): Promise<void>;
92
124
  deleteEntities(repositoryId: string, ids: string[]): Promise<{
93
125
  deleted: string[];
94
126
  notFound: string[];
95
127
  }>;
128
+ /**
129
+ * Single round-trip bulk-delete via the aggregate-side-effect pattern:
130
+ * collapses the per-chunk existence-check + drop into one Gremlin call.
131
+ *
132
+ * g.V()...hasId(within(...)).has('entityType')
133
+ * .aggregate('found').by('id') // collects the ids that match
134
+ * .drop() // drops the vertices (and cascaded edges)
135
+ * .cap('found') // emits the bucket as the single result
136
+ *
137
+ * The bucket is always emitted as a single list item — empty when nothing
138
+ * matched (probe-verified 2026-05-25, local-tests/phase7-shape-probe.mjs).
139
+ * `notFound` is derived client-side as the set difference.
140
+ */
96
141
  private deleteEntitiesImpl;
97
142
  deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
98
143
  deletedEntities: number;
99
- deletedRelationships: number;
144
+ deletedRelationships: number | undefined;
100
145
  }>;
101
- findEntities(repositoryId: string, query: StorageFindQuery): Promise<PaginatedResult<StoredEntity>>;
146
+ findEntities(repositoryId: string, query: StorageFindQuery, options?: EntityReadOptions): Promise<PaginatedResult<StoredEntity>>;
102
147
  createRelationship(repositoryId: string, relationship: StoredRelationship): Promise<StoredRelationship>;
103
148
  getRelationship(repositoryId: string, relationshipId: string): Promise<StoredRelationship | null>;
104
149
  getEntityRelationships(repositoryId: string, entityId: string, options?: RelationshipQueryOptions): Promise<PaginatedResult<StoredRelationship>>;
@@ -107,12 +152,69 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
107
152
  deleted: string[];
108
153
  notFound: string[];
109
154
  }>;
155
+ /**
156
+ * Single round-trip bulk relationship delete via the aggregate-side-effect
157
+ * pattern: collapses the per-chunk existence-check + drop into one Gremlin
158
+ * call. Gremlin drop on edges is routed by the engine and the bucket gives
159
+ * back the exact ids that were actually dropped, so `notFound` can be
160
+ * derived client-side.
161
+ *
162
+ * Source-id partition routing is not exposed on this method (the public
163
+ * surface accepts only edge ids), so the lookup may fan out across
164
+ * partitions — see [docs/cosmosdb-gremlin-compatibility.md §`g.E().has`
165
+ * doesn't always push partition down]. Callers that already hold a
166
+ * StoredRelationship and want partition-scoped routing should add a
167
+ * dedicated method when the need is concrete.
168
+ */
110
169
  private deleteRelationshipsImpl;
111
170
  deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
112
171
  deletedRelationships: number;
113
172
  }>;
114
173
  exploreNeighborhood(repositoryId: string, entityId: string, options: StorageExploreOptions): Promise<StorageNeighborhood>;
174
+ /**
175
+ * Compiler-model implementation of exploreNeighborhood.
176
+ *
177
+ * Strategy: for each depth `d` from 1 to `options.depth`, build a
178
+ * cumulative `TraversalSpec` with `d` steps and `returnMode: 'all'`, run
179
+ * it through {@link executeTraversal}, then walk one BFS layer client-side
180
+ * from the previous frontier using the returned edges.
181
+ *
182
+ * The server-side step direction is fixed to `'both'` (catches every edge
183
+ * in either direction). The directional + bidirectional filter is applied
184
+ * client-side per hop — i.e. `direction: 'outbound'` includes inbound edges
185
+ * where `bidirectional` is true. The CosmosDB Gremlin compiler does not
186
+ * natively express the `union(outE, inE.has(bidirectional))` shape that
187
+ * would push this filter server-side, so it stays client-side; the
188
+ * observable contract (which edges count toward "outbound" given
189
+ * bidirectionality) is preserved.
190
+ *
191
+ * Round-trips per call: `options.depth` (one per layer). The previous BFS
192
+ * was `1 + fanout + fanout² + …` round-trips for the same depth.
193
+ */
194
+ private exploreNeighborhoodImpl;
115
195
  findPaths(repositoryId: string, sourceId: string, targetId: string, options: StoragePathOptions): Promise<StoragePathResult>;
196
+ /**
197
+ * Compiler-model implementation of findPaths.
198
+ *
199
+ * Strategy: build a `TraversalSpec` with a single `'both'` direction step
200
+ * in `repeat()` mode with `emitIntermediates: true` and `returnMode:
201
+ * 'path'`, run it once. The compiler always emits `.simplePath()` in path
202
+ * mode for cycle prevention, producing
203
+ * `.emit().repeat(bothE().otherV()).times(maxDepth).simplePath()
204
+ * .range(...).path().by(<v>).by(<e>)`, which yields paths of every length
205
+ * from 0 (the start vertex alone) to `maxDepth`. Live-probed against the
206
+ * Cosmos emulator 2026-05-25 — see local-tests/phase4-repeat-emit-probe.mjs.
207
+ *
208
+ * The pre-Phase-4 Cosmos BFS in (now-deleted) `packages/storage-cosmosdb/
209
+ * src/queries/traversal.ts` traversed edges with unconditional `bothE()`
210
+ * regardless of the `bidirectional` flag (path discovery is reachability,
211
+ * not semantic direction). Mirror that here by using step direction
212
+ * `'both'` and not applying any direction filter. The plan's §6
213
+ * observable-outputs rule requires preserving this.
214
+ *
215
+ * One round-trip total. The previous BFS was up to `1 + fanout + fanout² + …`.
216
+ */
217
+ private findPathsImpl;
116
218
  getTimeline(repositoryId: string, entityId: string, options: StorageTimelineOptions): Promise<StorageTimelineResult>;
117
219
  exportAll(repositoryId: string): AsyncIterable<ExportChunk>;
118
220
  importBulk(repositoryId: string, data: ImportChunk[], options?: BulkImportOptions): Promise<BulkImportResult>;
@@ -125,7 +227,34 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
125
227
  private trackIterable;
126
228
  getCapabilities(): GraphTraversalCapabilities;
127
229
  traverse(repositoryId: string, spec: TraversalSpec): Promise<TraversalResult>;
128
- private traverseImpl;
230
+ /**
231
+ * Compile + execute a TraversalSpec against this repository's partition.
232
+ *
233
+ * Internal entrypoint shared by `traverse` (the public surface) and the
234
+ * compiler-model rewrites of `exploreNeighborhood` / `findPaths`. Does NOT
235
+ * wrap in `track()` — the outer public method owns its own usage scope, and
236
+ * inner submits accumulate into that scope (the nested-scope guard in
237
+ * `track()` keeps nested public calls from emitting duplicate records).
238
+ */
239
+ private traverseInternal;
240
+ /**
241
+ * Lower-level traversal helper: compiles a spec, submits to Gremlin, and
242
+ * parses the rows into raw {@link StoredEntity} / {@link StoredRelationship}
243
+ * objects (no detail-level projection, no provenance stripping). Used by
244
+ * `traverseInternal` and by the storage-layer rewrites of
245
+ * `exploreNeighborhood` / `findPaths` that need the full stored shape to
246
+ * rebuild `StorageNeighborhood` / `StoragePathResult`.
247
+ *
248
+ * Returns a discriminated bag — only the fields relevant to `spec.returnMode`
249
+ * are populated:
250
+ * - `'terminal'`: `terminalEntities` (in row order, no dedup beyond what the
251
+ * server emitted).
252
+ * - `'all'`: `allEntities` and `allRelationships`, already server-deduped.
253
+ * - `'path'`: `pathRows` plus the `entityMap` / `relationshipMap` lookup
254
+ * tables and `pathRelFirstDirection` for the first-seen direction per
255
+ * deduped edge id.
256
+ */
257
+ private executeTraversal;
129
258
  /**
130
259
  * Execute a raw Gremlin query with caller-supplied bindings.
131
260
  *