@utaba/deep-memory-storage-cosmosdb 0.8.1 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +260 -39
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.js +261 -40
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
|
|
2
|
-
import { 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';
|
|
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
|
|
|
5
5
|
/** Configuration for CosmosDbProvider. */
|
|
@@ -22,6 +22,13 @@ interface CosmosDbProviderConfig {
|
|
|
22
22
|
defaultTimeoutMs?: number;
|
|
23
23
|
/** Whether to reject unauthorized TLS certs — set false for emulator (default: true) */
|
|
24
24
|
rejectUnauthorized?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Optional usage sink. When provided, the provider emits one
|
|
27
|
+
* {@link OperationUsage} record per public method call with the aggregated
|
|
28
|
+
* CosmosDB request charge (RU). Never exposed to AI agents — the MCP server
|
|
29
|
+
* deliberately does not plumb this sink through.
|
|
30
|
+
*/
|
|
31
|
+
reportUsage?: UsageSink;
|
|
25
32
|
}
|
|
26
33
|
/**
|
|
27
34
|
* CosmosDB Gremlin storage provider for deep-memory.
|
|
@@ -31,7 +38,22 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
31
38
|
private readonly conn;
|
|
32
39
|
private readonly config;
|
|
33
40
|
private readonly compiler;
|
|
41
|
+
private readonly reportUsage;
|
|
34
42
|
constructor(config: CosmosDbProviderConfig);
|
|
43
|
+
/**
|
|
44
|
+
* Run the given operation inside an async-local usage scope. While the
|
|
45
|
+
* operation runs, every `conn.submit()` call accumulates its request charge
|
|
46
|
+
* into a shared accumulator. When the operation completes (success or
|
|
47
|
+
* failure), a single {@link OperationUsage} record is emitted to the sink.
|
|
48
|
+
*
|
|
49
|
+
* Nested public-method calls (e.g. `traverse` internally calls
|
|
50
|
+
* `getVocabulary`) are detected: the inner call joins the outer scope
|
|
51
|
+
* instead of starting a new one, so one user-visible operation produces
|
|
52
|
+
* exactly one usage record covering all its internal round-trips.
|
|
53
|
+
*
|
|
54
|
+
* If no sink is configured, this is a zero-overhead pass-through.
|
|
55
|
+
*/
|
|
56
|
+
private track;
|
|
35
57
|
/**
|
|
36
58
|
* Reject any repositoryId that is not a valid v4 UUID.
|
|
37
59
|
*
|
|
@@ -45,6 +67,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
45
67
|
initialize(): Promise<void>;
|
|
46
68
|
dispose(): Promise<void>;
|
|
47
69
|
ensureSchema(): Promise<EnsureSchemaResult>;
|
|
70
|
+
private ensureSchemaImpl;
|
|
48
71
|
/** Derive the REST endpoint from config — either explicit or from the Gremlin endpoint host. */
|
|
49
72
|
private getRestEndpoint;
|
|
50
73
|
createRepository(config: StorageRepositoryConfig): Promise<StoredRepository>;
|
|
@@ -70,6 +93,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
70
93
|
deleted: string[];
|
|
71
94
|
notFound: string[];
|
|
72
95
|
}>;
|
|
96
|
+
private deleteEntitiesImpl;
|
|
73
97
|
deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
|
|
74
98
|
deletedEntities: number;
|
|
75
99
|
deletedRelationships: number;
|
|
@@ -83,6 +107,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
83
107
|
deleted: string[];
|
|
84
108
|
notFound: string[];
|
|
85
109
|
}>;
|
|
110
|
+
private deleteRelationshipsImpl;
|
|
86
111
|
deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
|
|
87
112
|
deletedRelationships: number;
|
|
88
113
|
}>;
|
|
@@ -91,8 +116,16 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
91
116
|
getTimeline(repositoryId: string, entityId: string, options: StorageTimelineOptions): Promise<StorageTimelineResult>;
|
|
92
117
|
exportAll(repositoryId: string): AsyncIterable<ExportChunk>;
|
|
93
118
|
importBulk(repositoryId: string, data: ImportChunk[], options?: BulkImportOptions): Promise<BulkImportResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Wrap an AsyncIterable so every emitted chunk is generated inside the
|
|
121
|
+
* usage scope. On each iteration step, the scope aggregates charges from
|
|
122
|
+
* the next chunk's submits; when the iterator completes (or is closed), a
|
|
123
|
+
* single usage record is emitted for the whole stream.
|
|
124
|
+
*/
|
|
125
|
+
private trackIterable;
|
|
94
126
|
getCapabilities(): GraphTraversalCapabilities;
|
|
95
127
|
traverse(repositoryId: string, spec: TraversalSpec): Promise<TraversalResult>;
|
|
128
|
+
private traverseImpl;
|
|
96
129
|
/**
|
|
97
130
|
* Execute a raw Gremlin query with caller-supplied bindings.
|
|
98
131
|
*
|
|
@@ -115,6 +148,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
115
148
|
* repositoryId partition predicate.
|
|
116
149
|
*/
|
|
117
150
|
executeNativeQuery(_repositoryId: string, query: string, params?: Record<string, unknown>): Promise<TraversalResult>;
|
|
151
|
+
private executeNativeQueryImpl;
|
|
118
152
|
}
|
|
119
153
|
|
|
120
154
|
interface CosmosDbConnectionConfig {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { StorageProvider, GraphTraversalProvider, EnsureSchemaResult, GraphTraversalCapabilities } from '@utaba/deep-memory/providers';
|
|
2
|
-
import { 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';
|
|
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
|
|
|
5
5
|
/** Configuration for CosmosDbProvider. */
|
|
@@ -22,6 +22,13 @@ interface CosmosDbProviderConfig {
|
|
|
22
22
|
defaultTimeoutMs?: number;
|
|
23
23
|
/** Whether to reject unauthorized TLS certs — set false for emulator (default: true) */
|
|
24
24
|
rejectUnauthorized?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Optional usage sink. When provided, the provider emits one
|
|
27
|
+
* {@link OperationUsage} record per public method call with the aggregated
|
|
28
|
+
* CosmosDB request charge (RU). Never exposed to AI agents — the MCP server
|
|
29
|
+
* deliberately does not plumb this sink through.
|
|
30
|
+
*/
|
|
31
|
+
reportUsage?: UsageSink;
|
|
25
32
|
}
|
|
26
33
|
/**
|
|
27
34
|
* CosmosDB Gremlin storage provider for deep-memory.
|
|
@@ -31,7 +38,22 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
31
38
|
private readonly conn;
|
|
32
39
|
private readonly config;
|
|
33
40
|
private readonly compiler;
|
|
41
|
+
private readonly reportUsage;
|
|
34
42
|
constructor(config: CosmosDbProviderConfig);
|
|
43
|
+
/**
|
|
44
|
+
* Run the given operation inside an async-local usage scope. While the
|
|
45
|
+
* operation runs, every `conn.submit()` call accumulates its request charge
|
|
46
|
+
* into a shared accumulator. When the operation completes (success or
|
|
47
|
+
* failure), a single {@link OperationUsage} record is emitted to the sink.
|
|
48
|
+
*
|
|
49
|
+
* Nested public-method calls (e.g. `traverse` internally calls
|
|
50
|
+
* `getVocabulary`) are detected: the inner call joins the outer scope
|
|
51
|
+
* instead of starting a new one, so one user-visible operation produces
|
|
52
|
+
* exactly one usage record covering all its internal round-trips.
|
|
53
|
+
*
|
|
54
|
+
* If no sink is configured, this is a zero-overhead pass-through.
|
|
55
|
+
*/
|
|
56
|
+
private track;
|
|
35
57
|
/**
|
|
36
58
|
* Reject any repositoryId that is not a valid v4 UUID.
|
|
37
59
|
*
|
|
@@ -45,6 +67,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
45
67
|
initialize(): Promise<void>;
|
|
46
68
|
dispose(): Promise<void>;
|
|
47
69
|
ensureSchema(): Promise<EnsureSchemaResult>;
|
|
70
|
+
private ensureSchemaImpl;
|
|
48
71
|
/** Derive the REST endpoint from config — either explicit or from the Gremlin endpoint host. */
|
|
49
72
|
private getRestEndpoint;
|
|
50
73
|
createRepository(config: StorageRepositoryConfig): Promise<StoredRepository>;
|
|
@@ -70,6 +93,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
70
93
|
deleted: string[];
|
|
71
94
|
notFound: string[];
|
|
72
95
|
}>;
|
|
96
|
+
private deleteEntitiesImpl;
|
|
73
97
|
deleteEntitiesByType(repositoryId: string, entityType: string): Promise<{
|
|
74
98
|
deletedEntities: number;
|
|
75
99
|
deletedRelationships: number;
|
|
@@ -83,6 +107,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
83
107
|
deleted: string[];
|
|
84
108
|
notFound: string[];
|
|
85
109
|
}>;
|
|
110
|
+
private deleteRelationshipsImpl;
|
|
86
111
|
deleteRelationshipsByType(repositoryId: string, relationshipType: string): Promise<{
|
|
87
112
|
deletedRelationships: number;
|
|
88
113
|
}>;
|
|
@@ -91,8 +116,16 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
91
116
|
getTimeline(repositoryId: string, entityId: string, options: StorageTimelineOptions): Promise<StorageTimelineResult>;
|
|
92
117
|
exportAll(repositoryId: string): AsyncIterable<ExportChunk>;
|
|
93
118
|
importBulk(repositoryId: string, data: ImportChunk[], options?: BulkImportOptions): Promise<BulkImportResult>;
|
|
119
|
+
/**
|
|
120
|
+
* Wrap an AsyncIterable so every emitted chunk is generated inside the
|
|
121
|
+
* usage scope. On each iteration step, the scope aggregates charges from
|
|
122
|
+
* the next chunk's submits; when the iterator completes (or is closed), a
|
|
123
|
+
* single usage record is emitted for the whole stream.
|
|
124
|
+
*/
|
|
125
|
+
private trackIterable;
|
|
94
126
|
getCapabilities(): GraphTraversalCapabilities;
|
|
95
127
|
traverse(repositoryId: string, spec: TraversalSpec): Promise<TraversalResult>;
|
|
128
|
+
private traverseImpl;
|
|
96
129
|
/**
|
|
97
130
|
* Execute a raw Gremlin query with caller-supplied bindings.
|
|
98
131
|
*
|
|
@@ -115,6 +148,7 @@ declare class CosmosDbProvider implements StorageProvider, GraphTraversalProvide
|
|
|
115
148
|
* repositoryId partition predicate.
|
|
116
149
|
*/
|
|
117
150
|
executeNativeQuery(_repositoryId: string, query: string, params?: Record<string, unknown>): Promise<TraversalResult>;
|
|
151
|
+
private executeNativeQueryImpl;
|
|
118
152
|
}
|
|
119
153
|
|
|
120
154
|
interface CosmosDbConnectionConfig {
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
// src/CosmosDbProvider.ts
|
|
2
2
|
import crypto from "crypto";
|
|
3
|
-
import { GremlinCompiler, ProviderError, InvalidInputError, isValidUuid, projectEntity } from "@utaba/deep-memory";
|
|
3
|
+
import { GremlinCompiler, ProviderError, InvalidInputError, isValidUuid, projectEntity, createSafeSink } from "@utaba/deep-memory";
|
|
4
4
|
|
|
5
5
|
// src/CosmosDbConnection.ts
|
|
6
6
|
import gremlin from "gremlin";
|
|
7
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
8
|
+
var usageScope = new AsyncLocalStorage();
|
|
7
9
|
var CosmosDbConnection = class {
|
|
8
10
|
client = null;
|
|
9
11
|
config;
|
|
@@ -51,11 +53,18 @@ var CosmosDbConnection = class {
|
|
|
51
53
|
const resultSet = await this.client.submit(query, bindings);
|
|
52
54
|
const items = resultSet.toArray();
|
|
53
55
|
const requestCharge = extractRequestCharge(resultSet);
|
|
56
|
+
const acc = usageScope.getStore();
|
|
57
|
+
if (acc) {
|
|
58
|
+
acc.calls++;
|
|
59
|
+
if (typeof requestCharge === "number") acc.ru += requestCharge;
|
|
60
|
+
}
|
|
54
61
|
return { items, requestCharge };
|
|
55
62
|
} catch (err) {
|
|
56
63
|
lastError = err;
|
|
57
64
|
if (isTransientError(err) && attempt < this.config.maxRetries) {
|
|
58
65
|
const retryAfterMs = getRetryAfterMs(err, attempt);
|
|
66
|
+
const acc = usageScope.getStore();
|
|
67
|
+
if (acc) acc.retries++;
|
|
59
68
|
await sleep(retryAfterMs);
|
|
60
69
|
continue;
|
|
61
70
|
}
|
|
@@ -873,6 +882,7 @@ async function exploreNeighborhood(conn, repositoryId, entityId, options) {
|
|
|
873
882
|
for (let depth = 0; depth < options.depth; depth++) {
|
|
874
883
|
const layer = {};
|
|
875
884
|
const nextFrontier = /* @__PURE__ */ new Set();
|
|
885
|
+
const layerCache = /* @__PURE__ */ new Map();
|
|
876
886
|
for (const currentId of frontier) {
|
|
877
887
|
const rels = await getRelationshipsForEntity(conn, repositoryId, currentId, options);
|
|
878
888
|
for (const rel of rels) {
|
|
@@ -886,20 +896,20 @@ async function exploreNeighborhood(conn, repositoryId, entityId, options) {
|
|
|
886
896
|
} else {
|
|
887
897
|
connectedId = rel.sourceEntityId;
|
|
888
898
|
}
|
|
889
|
-
if (
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
899
|
+
if (visited.has(connectedId)) continue;
|
|
900
|
+
let entity = layerCache.get(connectedId);
|
|
901
|
+
if (entity === void 0) {
|
|
902
|
+
const loaded = await getEntityLight(conn, repositoryId, connectedId);
|
|
903
|
+
if (loaded && (!options.entityTypes || options.entityTypes.length === 0 || options.entityTypes.includes(loaded.entityType))) {
|
|
904
|
+
entity = loaded;
|
|
894
905
|
} else {
|
|
895
|
-
|
|
896
|
-
if (entity) {
|
|
897
|
-
layer[relType].entities.push(entity);
|
|
898
|
-
}
|
|
906
|
+
entity = null;
|
|
899
907
|
}
|
|
900
|
-
|
|
901
|
-
nextFrontier.add(connectedId);
|
|
908
|
+
layerCache.set(connectedId, entity);
|
|
909
|
+
if (entity) nextFrontier.add(connectedId);
|
|
902
910
|
}
|
|
911
|
+
if (!entity) continue;
|
|
912
|
+
layer[relType].entities.push(entity);
|
|
903
913
|
layer[relType].relationships.push(rel);
|
|
904
914
|
layer[relType].total = layer[relType].entities.length;
|
|
905
915
|
}
|
|
@@ -914,6 +924,9 @@ async function exploreNeighborhood(conn, repositoryId, entityId, options) {
|
|
|
914
924
|
if (Object.keys(layer).length > 0) {
|
|
915
925
|
layers.push(layer);
|
|
916
926
|
}
|
|
927
|
+
for (const id of nextFrontier) {
|
|
928
|
+
visited.add(id);
|
|
929
|
+
}
|
|
917
930
|
frontier = nextFrontier;
|
|
918
931
|
if (frontier.size === 0) break;
|
|
919
932
|
}
|
|
@@ -1291,14 +1304,17 @@ async function upsertRelationship(conn, repositoryId, rel) {
|
|
|
1291
1304
|
}
|
|
1292
1305
|
|
|
1293
1306
|
// src/CosmosDbProvider.ts
|
|
1307
|
+
var PROVIDER_NAME = "cosmosdb";
|
|
1294
1308
|
var SCHEMA_VERSION = 1;
|
|
1295
1309
|
var META_VERTEX_ID = "_meta:schema";
|
|
1296
1310
|
var CosmosDbProvider = class {
|
|
1297
1311
|
conn;
|
|
1298
1312
|
config;
|
|
1299
1313
|
compiler = new GremlinCompiler();
|
|
1314
|
+
reportUsage;
|
|
1300
1315
|
constructor(config) {
|
|
1301
1316
|
this.config = config;
|
|
1317
|
+
this.reportUsage = createSafeSink(config.reportUsage);
|
|
1302
1318
|
this.conn = new CosmosDbConnection({
|
|
1303
1319
|
endpoint: config.endpoint,
|
|
1304
1320
|
key: config.key,
|
|
@@ -1309,6 +1325,39 @@ var CosmosDbProvider = class {
|
|
|
1309
1325
|
rejectUnauthorized: config.rejectUnauthorized
|
|
1310
1326
|
});
|
|
1311
1327
|
}
|
|
1328
|
+
/**
|
|
1329
|
+
* Run the given operation inside an async-local usage scope. While the
|
|
1330
|
+
* operation runs, every `conn.submit()` call accumulates its request charge
|
|
1331
|
+
* into a shared accumulator. When the operation completes (success or
|
|
1332
|
+
* failure), a single {@link OperationUsage} record is emitted to the sink.
|
|
1333
|
+
*
|
|
1334
|
+
* Nested public-method calls (e.g. `traverse` internally calls
|
|
1335
|
+
* `getVocabulary`) are detected: the inner call joins the outer scope
|
|
1336
|
+
* instead of starting a new one, so one user-visible operation produces
|
|
1337
|
+
* exactly one usage record covering all its internal round-trips.
|
|
1338
|
+
*
|
|
1339
|
+
* If no sink is configured, this is a zero-overhead pass-through.
|
|
1340
|
+
*/
|
|
1341
|
+
async track(operation, repositoryId, fn) {
|
|
1342
|
+
if (!this.reportUsage) return fn();
|
|
1343
|
+
if (usageScope.getStore()) return fn();
|
|
1344
|
+
const acc = { ru: 0, calls: 0, retries: 0 };
|
|
1345
|
+
try {
|
|
1346
|
+
return await usageScope.run(acc, fn);
|
|
1347
|
+
} finally {
|
|
1348
|
+
if (acc.calls > 0) {
|
|
1349
|
+
this.reportUsage({
|
|
1350
|
+
provider: PROVIDER_NAME,
|
|
1351
|
+
operation,
|
|
1352
|
+
unit: "RU",
|
|
1353
|
+
value: acc.ru,
|
|
1354
|
+
...repositoryId ? { repositoryId } : {},
|
|
1355
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1356
|
+
details: { calls: acc.calls, retries: acc.retries }
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1312
1361
|
/**
|
|
1313
1362
|
* Reject any repositoryId that is not a valid v4 UUID.
|
|
1314
1363
|
*
|
|
@@ -1334,6 +1383,9 @@ var CosmosDbProvider = class {
|
|
|
1334
1383
|
await this.conn.close();
|
|
1335
1384
|
}
|
|
1336
1385
|
async ensureSchema() {
|
|
1386
|
+
return this.track("ensureSchema", void 0, () => this.ensureSchemaImpl());
|
|
1387
|
+
}
|
|
1388
|
+
async ensureSchemaImpl() {
|
|
1337
1389
|
const restBase = this.getRestEndpoint();
|
|
1338
1390
|
const partitionKey = this.config.partitionKey ?? "/repositoryId";
|
|
1339
1391
|
let databaseCreated = false;
|
|
@@ -1412,72 +1464,139 @@ var CosmosDbProvider = class {
|
|
|
1412
1464
|
// ─── Repository ────────────────────────────────────────────────────
|
|
1413
1465
|
async createRepository(config) {
|
|
1414
1466
|
this.assertValidRepositoryId(config.repositoryId);
|
|
1415
|
-
return
|
|
1467
|
+
return this.track(
|
|
1468
|
+
"createRepository",
|
|
1469
|
+
config.repositoryId,
|
|
1470
|
+
() => createRepository(this.conn, config)
|
|
1471
|
+
);
|
|
1416
1472
|
}
|
|
1417
1473
|
async getRepository(repositoryId) {
|
|
1418
1474
|
this.assertValidRepositoryId(repositoryId);
|
|
1419
|
-
return
|
|
1475
|
+
return this.track(
|
|
1476
|
+
"getRepository",
|
|
1477
|
+
repositoryId,
|
|
1478
|
+
() => getRepository(this.conn, repositoryId)
|
|
1479
|
+
);
|
|
1420
1480
|
}
|
|
1421
1481
|
async listRepositories(filter) {
|
|
1422
|
-
return
|
|
1482
|
+
return this.track(
|
|
1483
|
+
"listRepositories",
|
|
1484
|
+
void 0,
|
|
1485
|
+
() => listRepositories(this.conn, filter)
|
|
1486
|
+
);
|
|
1423
1487
|
}
|
|
1424
1488
|
async updateRepository(repositoryId, updates) {
|
|
1425
1489
|
this.assertValidRepositoryId(repositoryId);
|
|
1426
|
-
return
|
|
1490
|
+
return this.track(
|
|
1491
|
+
"updateRepository",
|
|
1492
|
+
repositoryId,
|
|
1493
|
+
() => updateRepository(this.conn, repositoryId, updates)
|
|
1494
|
+
);
|
|
1427
1495
|
}
|
|
1428
1496
|
async deleteRepository(repositoryId, onProgress) {
|
|
1429
1497
|
this.assertValidRepositoryId(repositoryId);
|
|
1430
|
-
return
|
|
1498
|
+
return this.track(
|
|
1499
|
+
"deleteRepository",
|
|
1500
|
+
repositoryId,
|
|
1501
|
+
() => deleteRepository(this.conn, repositoryId, onProgress)
|
|
1502
|
+
);
|
|
1431
1503
|
}
|
|
1432
1504
|
async deleteAllContents(repositoryId, onProgress) {
|
|
1433
1505
|
this.assertValidRepositoryId(repositoryId);
|
|
1434
|
-
return
|
|
1506
|
+
return this.track(
|
|
1507
|
+
"deleteAllContents",
|
|
1508
|
+
repositoryId,
|
|
1509
|
+
() => deleteAllContents(this.conn, repositoryId, onProgress)
|
|
1510
|
+
);
|
|
1435
1511
|
}
|
|
1436
1512
|
async getRepositoryStats(repositoryId) {
|
|
1437
1513
|
this.assertValidRepositoryId(repositoryId);
|
|
1438
|
-
return
|
|
1514
|
+
return this.track(
|
|
1515
|
+
"getRepositoryStats",
|
|
1516
|
+
repositoryId,
|
|
1517
|
+
() => getRepositoryStats(this.conn, repositoryId)
|
|
1518
|
+
);
|
|
1439
1519
|
}
|
|
1440
1520
|
// ─── Vocabulary ────────────────────────────────────────────────────
|
|
1441
1521
|
async getVocabulary(repositoryId) {
|
|
1442
1522
|
this.assertValidRepositoryId(repositoryId);
|
|
1443
|
-
return
|
|
1523
|
+
return this.track(
|
|
1524
|
+
"getVocabulary",
|
|
1525
|
+
repositoryId,
|
|
1526
|
+
() => getVocabulary(this.conn, repositoryId)
|
|
1527
|
+
);
|
|
1444
1528
|
}
|
|
1445
1529
|
async saveVocabulary(repositoryId, vocabulary) {
|
|
1446
1530
|
this.assertValidRepositoryId(repositoryId);
|
|
1447
|
-
return
|
|
1531
|
+
return this.track(
|
|
1532
|
+
"saveVocabulary",
|
|
1533
|
+
repositoryId,
|
|
1534
|
+
() => saveVocabulary(this.conn, repositoryId, vocabulary)
|
|
1535
|
+
);
|
|
1448
1536
|
}
|
|
1449
1537
|
async getVocabularyChangeLog(repositoryId, options) {
|
|
1450
1538
|
this.assertValidRepositoryId(repositoryId);
|
|
1451
|
-
return
|
|
1539
|
+
return this.track(
|
|
1540
|
+
"getVocabularyChangeLog",
|
|
1541
|
+
repositoryId,
|
|
1542
|
+
() => getVocabularyChangeLog(this.conn, repositoryId, options)
|
|
1543
|
+
);
|
|
1452
1544
|
}
|
|
1453
1545
|
// ─── Entities ──────────────────────────────────────────────────────
|
|
1454
1546
|
async createEntity(repositoryId, entity) {
|
|
1455
1547
|
this.assertValidRepositoryId(repositoryId);
|
|
1456
|
-
return
|
|
1548
|
+
return this.track(
|
|
1549
|
+
"createEntity",
|
|
1550
|
+
repositoryId,
|
|
1551
|
+
() => createEntity(this.conn, repositoryId, entity)
|
|
1552
|
+
);
|
|
1457
1553
|
}
|
|
1458
1554
|
async getEntity(repositoryId, entityId) {
|
|
1459
1555
|
this.assertValidRepositoryId(repositoryId);
|
|
1460
|
-
return
|
|
1556
|
+
return this.track(
|
|
1557
|
+
"getEntity",
|
|
1558
|
+
repositoryId,
|
|
1559
|
+
() => getEntity(this.conn, repositoryId, entityId)
|
|
1560
|
+
);
|
|
1461
1561
|
}
|
|
1462
1562
|
async getEntityBySlug(repositoryId, slug) {
|
|
1463
1563
|
this.assertValidRepositoryId(repositoryId);
|
|
1464
|
-
return
|
|
1564
|
+
return this.track(
|
|
1565
|
+
"getEntityBySlug",
|
|
1566
|
+
repositoryId,
|
|
1567
|
+
() => getEntityBySlug(this.conn, repositoryId, slug)
|
|
1568
|
+
);
|
|
1465
1569
|
}
|
|
1466
1570
|
async getEntities(repositoryId, entityIds) {
|
|
1467
1571
|
this.assertValidRepositoryId(repositoryId);
|
|
1468
|
-
return
|
|
1572
|
+
return this.track(
|
|
1573
|
+
"getEntities",
|
|
1574
|
+
repositoryId,
|
|
1575
|
+
() => getEntities(this.conn, repositoryId, entityIds)
|
|
1576
|
+
);
|
|
1469
1577
|
}
|
|
1470
1578
|
async updateEntity(repositoryId, entityId, updates) {
|
|
1471
1579
|
this.assertValidRepositoryId(repositoryId);
|
|
1472
|
-
return
|
|
1580
|
+
return this.track(
|
|
1581
|
+
"updateEntity",
|
|
1582
|
+
repositoryId,
|
|
1583
|
+
() => updateEntity(this.conn, repositoryId, entityId, updates)
|
|
1584
|
+
);
|
|
1473
1585
|
}
|
|
1474
1586
|
async deleteEntity(repositoryId, entityId) {
|
|
1475
1587
|
this.assertValidRepositoryId(repositoryId);
|
|
1476
|
-
return
|
|
1588
|
+
return this.track(
|
|
1589
|
+
"deleteEntity",
|
|
1590
|
+
repositoryId,
|
|
1591
|
+
() => deleteEntity(this.conn, repositoryId, entityId)
|
|
1592
|
+
);
|
|
1477
1593
|
}
|
|
1478
1594
|
async deleteEntities(repositoryId, ids) {
|
|
1479
1595
|
if (ids.length === 0) return { deleted: [], notFound: [] };
|
|
1480
1596
|
this.assertValidRepositoryId(repositoryId);
|
|
1597
|
+
return this.track("deleteEntities", repositoryId, () => this.deleteEntitiesImpl(repositoryId, ids));
|
|
1598
|
+
}
|
|
1599
|
+
async deleteEntitiesImpl(repositoryId, ids) {
|
|
1481
1600
|
const deleted = [];
|
|
1482
1601
|
const CHUNK = 100;
|
|
1483
1602
|
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
@@ -1516,32 +1635,59 @@ var CosmosDbProvider = class {
|
|
|
1516
1635
|
}
|
|
1517
1636
|
async deleteEntitiesByType(repositoryId, entityType) {
|
|
1518
1637
|
this.assertValidRepositoryId(repositoryId);
|
|
1519
|
-
return
|
|
1638
|
+
return this.track(
|
|
1639
|
+
"deleteEntitiesByType",
|
|
1640
|
+
repositoryId,
|
|
1641
|
+
() => deleteEntitiesByType(this.conn, repositoryId, entityType)
|
|
1642
|
+
);
|
|
1520
1643
|
}
|
|
1521
1644
|
async findEntities(repositoryId, query) {
|
|
1522
1645
|
this.assertValidRepositoryId(repositoryId);
|
|
1523
|
-
return
|
|
1646
|
+
return this.track(
|
|
1647
|
+
"findEntities",
|
|
1648
|
+
repositoryId,
|
|
1649
|
+
() => findEntities(this.conn, repositoryId, query)
|
|
1650
|
+
);
|
|
1524
1651
|
}
|
|
1525
1652
|
// ─── Relationships ─────────────────────────────────────────────────
|
|
1526
1653
|
async createRelationship(repositoryId, relationship) {
|
|
1527
1654
|
this.assertValidRepositoryId(repositoryId);
|
|
1528
|
-
return
|
|
1655
|
+
return this.track(
|
|
1656
|
+
"createRelationship",
|
|
1657
|
+
repositoryId,
|
|
1658
|
+
() => createRelationship(this.conn, repositoryId, relationship)
|
|
1659
|
+
);
|
|
1529
1660
|
}
|
|
1530
1661
|
async getRelationship(repositoryId, relationshipId) {
|
|
1531
1662
|
this.assertValidRepositoryId(repositoryId);
|
|
1532
|
-
return
|
|
1663
|
+
return this.track(
|
|
1664
|
+
"getRelationship",
|
|
1665
|
+
repositoryId,
|
|
1666
|
+
() => getRelationship(this.conn, repositoryId, relationshipId)
|
|
1667
|
+
);
|
|
1533
1668
|
}
|
|
1534
1669
|
async getEntityRelationships(repositoryId, entityId, options) {
|
|
1535
1670
|
this.assertValidRepositoryId(repositoryId);
|
|
1536
|
-
return
|
|
1671
|
+
return this.track(
|
|
1672
|
+
"getEntityRelationships",
|
|
1673
|
+
repositoryId,
|
|
1674
|
+
() => getEntityRelationships(this.conn, repositoryId, entityId, options)
|
|
1675
|
+
);
|
|
1537
1676
|
}
|
|
1538
1677
|
async deleteRelationship(repositoryId, relationshipId) {
|
|
1539
1678
|
this.assertValidRepositoryId(repositoryId);
|
|
1540
|
-
return
|
|
1679
|
+
return this.track(
|
|
1680
|
+
"deleteRelationship",
|
|
1681
|
+
repositoryId,
|
|
1682
|
+
() => deleteRelationship(this.conn, repositoryId, relationshipId)
|
|
1683
|
+
);
|
|
1541
1684
|
}
|
|
1542
1685
|
async deleteRelationships(repositoryId, ids) {
|
|
1543
1686
|
if (ids.length === 0) return { deleted: [], notFound: [] };
|
|
1544
1687
|
this.assertValidRepositoryId(repositoryId);
|
|
1688
|
+
return this.track("deleteRelationships", repositoryId, () => this.deleteRelationshipsImpl(repositoryId, ids));
|
|
1689
|
+
}
|
|
1690
|
+
async deleteRelationshipsImpl(repositoryId, ids) {
|
|
1545
1691
|
const deleted = [];
|
|
1546
1692
|
const CHUNK = 100;
|
|
1547
1693
|
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
@@ -1577,30 +1723,99 @@ var CosmosDbProvider = class {
|
|
|
1577
1723
|
}
|
|
1578
1724
|
async deleteRelationshipsByType(repositoryId, relationshipType) {
|
|
1579
1725
|
this.assertValidRepositoryId(repositoryId);
|
|
1580
|
-
return
|
|
1726
|
+
return this.track(
|
|
1727
|
+
"deleteRelationshipsByType",
|
|
1728
|
+
repositoryId,
|
|
1729
|
+
() => deleteRelationshipsByType(this.conn, repositoryId, relationshipType)
|
|
1730
|
+
);
|
|
1581
1731
|
}
|
|
1582
1732
|
// ─── Graph Traversal (StorageProvider) ─────────────────────────────
|
|
1583
1733
|
async exploreNeighborhood(repositoryId, entityId, options) {
|
|
1584
1734
|
this.assertValidRepositoryId(repositoryId);
|
|
1585
|
-
return
|
|
1735
|
+
return this.track(
|
|
1736
|
+
"exploreNeighborhood",
|
|
1737
|
+
repositoryId,
|
|
1738
|
+
() => exploreNeighborhood(this.conn, repositoryId, entityId, options)
|
|
1739
|
+
);
|
|
1586
1740
|
}
|
|
1587
1741
|
async findPaths(repositoryId, sourceId, targetId, options) {
|
|
1588
1742
|
this.assertValidRepositoryId(repositoryId);
|
|
1589
|
-
return
|
|
1743
|
+
return this.track(
|
|
1744
|
+
"findPaths",
|
|
1745
|
+
repositoryId,
|
|
1746
|
+
() => findPaths(this.conn, repositoryId, sourceId, targetId, options)
|
|
1747
|
+
);
|
|
1590
1748
|
}
|
|
1591
1749
|
// ─── Timeline ──────────────────────────────────────────────────────
|
|
1592
1750
|
async getTimeline(repositoryId, entityId, options) {
|
|
1593
1751
|
this.assertValidRepositoryId(repositoryId);
|
|
1594
|
-
return
|
|
1752
|
+
return this.track(
|
|
1753
|
+
"getTimeline",
|
|
1754
|
+
repositoryId,
|
|
1755
|
+
() => getTimeline(this.conn, repositoryId, entityId, options)
|
|
1756
|
+
);
|
|
1595
1757
|
}
|
|
1596
1758
|
// ─── Bulk Operations ───────────────────────────────────────────────
|
|
1597
1759
|
exportAll(repositoryId) {
|
|
1598
1760
|
this.assertValidRepositoryId(repositoryId);
|
|
1599
|
-
return exportAll(this.conn, repositoryId);
|
|
1761
|
+
return this.trackIterable("exportAll", repositoryId, exportAll(this.conn, repositoryId));
|
|
1600
1762
|
}
|
|
1601
1763
|
async importBulk(repositoryId, data, options) {
|
|
1602
1764
|
this.assertValidRepositoryId(repositoryId);
|
|
1603
|
-
return
|
|
1765
|
+
return this.track(
|
|
1766
|
+
"importBulk",
|
|
1767
|
+
repositoryId,
|
|
1768
|
+
() => importBulk(this.conn, repositoryId, data, options)
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
/**
|
|
1772
|
+
* Wrap an AsyncIterable so every emitted chunk is generated inside the
|
|
1773
|
+
* usage scope. On each iteration step, the scope aggregates charges from
|
|
1774
|
+
* the next chunk's submits; when the iterator completes (or is closed), a
|
|
1775
|
+
* single usage record is emitted for the whole stream.
|
|
1776
|
+
*/
|
|
1777
|
+
trackIterable(operation, repositoryId, source) {
|
|
1778
|
+
if (!this.reportUsage) return source;
|
|
1779
|
+
const sink = this.reportUsage;
|
|
1780
|
+
return {
|
|
1781
|
+
[Symbol.asyncIterator]: () => {
|
|
1782
|
+
const iter = source[Symbol.asyncIterator]();
|
|
1783
|
+
const acc = { ru: 0, calls: 0, retries: 0 };
|
|
1784
|
+
let emitted = false;
|
|
1785
|
+
const emit = () => {
|
|
1786
|
+
if (emitted) return;
|
|
1787
|
+
emitted = true;
|
|
1788
|
+
if (acc.calls > 0) {
|
|
1789
|
+
sink({
|
|
1790
|
+
provider: PROVIDER_NAME,
|
|
1791
|
+
operation,
|
|
1792
|
+
unit: "RU",
|
|
1793
|
+
value: acc.ru,
|
|
1794
|
+
repositoryId,
|
|
1795
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1796
|
+
details: { calls: acc.calls, retries: acc.retries }
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
};
|
|
1800
|
+
return {
|
|
1801
|
+
async next() {
|
|
1802
|
+
const step = await usageScope.run(acc, () => iter.next());
|
|
1803
|
+
if (step.done) emit();
|
|
1804
|
+
return step;
|
|
1805
|
+
},
|
|
1806
|
+
async return(value) {
|
|
1807
|
+
emit();
|
|
1808
|
+
if (iter.return) return iter.return(value);
|
|
1809
|
+
return { done: true, value };
|
|
1810
|
+
},
|
|
1811
|
+
async throw(err) {
|
|
1812
|
+
emit();
|
|
1813
|
+
if (iter.throw) return iter.throw(err);
|
|
1814
|
+
throw err;
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1604
1819
|
}
|
|
1605
1820
|
// ─── GraphTraversalProvider ────────────────────────────────────────
|
|
1606
1821
|
getCapabilities() {
|
|
@@ -1618,6 +1833,9 @@ var CosmosDbProvider = class {
|
|
|
1618
1833
|
}
|
|
1619
1834
|
async traverse(repositoryId, spec) {
|
|
1620
1835
|
this.assertValidRepositoryId(repositoryId);
|
|
1836
|
+
return this.track("traverse", repositoryId, () => this.traverseImpl(repositoryId, spec));
|
|
1837
|
+
}
|
|
1838
|
+
async traverseImpl(repositoryId, spec) {
|
|
1621
1839
|
const startTime = Date.now();
|
|
1622
1840
|
const vocabulary = await this.getVocabulary(repositoryId);
|
|
1623
1841
|
const compiled = this.compiler.compile(spec, vocabulary);
|
|
@@ -1711,6 +1929,9 @@ var CosmosDbProvider = class {
|
|
|
1711
1929
|
* repositoryId partition predicate.
|
|
1712
1930
|
*/
|
|
1713
1931
|
async executeNativeQuery(_repositoryId, query, params) {
|
|
1932
|
+
return this.track("executeNativeQuery", void 0, () => this.executeNativeQueryImpl(query, params));
|
|
1933
|
+
}
|
|
1934
|
+
async executeNativeQueryImpl(query, params) {
|
|
1714
1935
|
const startTime = Date.now();
|
|
1715
1936
|
try {
|
|
1716
1937
|
const result = await this.conn.submit(query, params);
|