@utaba/deep-memory-storage-cosmosdb 0.8.1 → 0.9.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.cjs +245 -28
- 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 +246 -29
- 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
|
}
|
|
@@ -1291,14 +1300,17 @@ async function upsertRelationship(conn, repositoryId, rel) {
|
|
|
1291
1300
|
}
|
|
1292
1301
|
|
|
1293
1302
|
// src/CosmosDbProvider.ts
|
|
1303
|
+
var PROVIDER_NAME = "cosmosdb";
|
|
1294
1304
|
var SCHEMA_VERSION = 1;
|
|
1295
1305
|
var META_VERTEX_ID = "_meta:schema";
|
|
1296
1306
|
var CosmosDbProvider = class {
|
|
1297
1307
|
conn;
|
|
1298
1308
|
config;
|
|
1299
1309
|
compiler = new GremlinCompiler();
|
|
1310
|
+
reportUsage;
|
|
1300
1311
|
constructor(config) {
|
|
1301
1312
|
this.config = config;
|
|
1313
|
+
this.reportUsage = createSafeSink(config.reportUsage);
|
|
1302
1314
|
this.conn = new CosmosDbConnection({
|
|
1303
1315
|
endpoint: config.endpoint,
|
|
1304
1316
|
key: config.key,
|
|
@@ -1309,6 +1321,39 @@ var CosmosDbProvider = class {
|
|
|
1309
1321
|
rejectUnauthorized: config.rejectUnauthorized
|
|
1310
1322
|
});
|
|
1311
1323
|
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Run the given operation inside an async-local usage scope. While the
|
|
1326
|
+
* operation runs, every `conn.submit()` call accumulates its request charge
|
|
1327
|
+
* into a shared accumulator. When the operation completes (success or
|
|
1328
|
+
* failure), a single {@link OperationUsage} record is emitted to the sink.
|
|
1329
|
+
*
|
|
1330
|
+
* Nested public-method calls (e.g. `traverse` internally calls
|
|
1331
|
+
* `getVocabulary`) are detected: the inner call joins the outer scope
|
|
1332
|
+
* instead of starting a new one, so one user-visible operation produces
|
|
1333
|
+
* exactly one usage record covering all its internal round-trips.
|
|
1334
|
+
*
|
|
1335
|
+
* If no sink is configured, this is a zero-overhead pass-through.
|
|
1336
|
+
*/
|
|
1337
|
+
async track(operation, repositoryId, fn) {
|
|
1338
|
+
if (!this.reportUsage) return fn();
|
|
1339
|
+
if (usageScope.getStore()) return fn();
|
|
1340
|
+
const acc = { ru: 0, calls: 0, retries: 0 };
|
|
1341
|
+
try {
|
|
1342
|
+
return await usageScope.run(acc, fn);
|
|
1343
|
+
} finally {
|
|
1344
|
+
if (acc.calls > 0) {
|
|
1345
|
+
this.reportUsage({
|
|
1346
|
+
provider: PROVIDER_NAME,
|
|
1347
|
+
operation,
|
|
1348
|
+
unit: "RU",
|
|
1349
|
+
value: acc.ru,
|
|
1350
|
+
...repositoryId ? { repositoryId } : {},
|
|
1351
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1352
|
+
details: { calls: acc.calls, retries: acc.retries }
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1312
1357
|
/**
|
|
1313
1358
|
* Reject any repositoryId that is not a valid v4 UUID.
|
|
1314
1359
|
*
|
|
@@ -1334,6 +1379,9 @@ var CosmosDbProvider = class {
|
|
|
1334
1379
|
await this.conn.close();
|
|
1335
1380
|
}
|
|
1336
1381
|
async ensureSchema() {
|
|
1382
|
+
return this.track("ensureSchema", void 0, () => this.ensureSchemaImpl());
|
|
1383
|
+
}
|
|
1384
|
+
async ensureSchemaImpl() {
|
|
1337
1385
|
const restBase = this.getRestEndpoint();
|
|
1338
1386
|
const partitionKey = this.config.partitionKey ?? "/repositoryId";
|
|
1339
1387
|
let databaseCreated = false;
|
|
@@ -1412,72 +1460,139 @@ var CosmosDbProvider = class {
|
|
|
1412
1460
|
// ─── Repository ────────────────────────────────────────────────────
|
|
1413
1461
|
async createRepository(config) {
|
|
1414
1462
|
this.assertValidRepositoryId(config.repositoryId);
|
|
1415
|
-
return
|
|
1463
|
+
return this.track(
|
|
1464
|
+
"createRepository",
|
|
1465
|
+
config.repositoryId,
|
|
1466
|
+
() => createRepository(this.conn, config)
|
|
1467
|
+
);
|
|
1416
1468
|
}
|
|
1417
1469
|
async getRepository(repositoryId) {
|
|
1418
1470
|
this.assertValidRepositoryId(repositoryId);
|
|
1419
|
-
return
|
|
1471
|
+
return this.track(
|
|
1472
|
+
"getRepository",
|
|
1473
|
+
repositoryId,
|
|
1474
|
+
() => getRepository(this.conn, repositoryId)
|
|
1475
|
+
);
|
|
1420
1476
|
}
|
|
1421
1477
|
async listRepositories(filter) {
|
|
1422
|
-
return
|
|
1478
|
+
return this.track(
|
|
1479
|
+
"listRepositories",
|
|
1480
|
+
void 0,
|
|
1481
|
+
() => listRepositories(this.conn, filter)
|
|
1482
|
+
);
|
|
1423
1483
|
}
|
|
1424
1484
|
async updateRepository(repositoryId, updates) {
|
|
1425
1485
|
this.assertValidRepositoryId(repositoryId);
|
|
1426
|
-
return
|
|
1486
|
+
return this.track(
|
|
1487
|
+
"updateRepository",
|
|
1488
|
+
repositoryId,
|
|
1489
|
+
() => updateRepository(this.conn, repositoryId, updates)
|
|
1490
|
+
);
|
|
1427
1491
|
}
|
|
1428
1492
|
async deleteRepository(repositoryId, onProgress) {
|
|
1429
1493
|
this.assertValidRepositoryId(repositoryId);
|
|
1430
|
-
return
|
|
1494
|
+
return this.track(
|
|
1495
|
+
"deleteRepository",
|
|
1496
|
+
repositoryId,
|
|
1497
|
+
() => deleteRepository(this.conn, repositoryId, onProgress)
|
|
1498
|
+
);
|
|
1431
1499
|
}
|
|
1432
1500
|
async deleteAllContents(repositoryId, onProgress) {
|
|
1433
1501
|
this.assertValidRepositoryId(repositoryId);
|
|
1434
|
-
return
|
|
1502
|
+
return this.track(
|
|
1503
|
+
"deleteAllContents",
|
|
1504
|
+
repositoryId,
|
|
1505
|
+
() => deleteAllContents(this.conn, repositoryId, onProgress)
|
|
1506
|
+
);
|
|
1435
1507
|
}
|
|
1436
1508
|
async getRepositoryStats(repositoryId) {
|
|
1437
1509
|
this.assertValidRepositoryId(repositoryId);
|
|
1438
|
-
return
|
|
1510
|
+
return this.track(
|
|
1511
|
+
"getRepositoryStats",
|
|
1512
|
+
repositoryId,
|
|
1513
|
+
() => getRepositoryStats(this.conn, repositoryId)
|
|
1514
|
+
);
|
|
1439
1515
|
}
|
|
1440
1516
|
// ─── Vocabulary ────────────────────────────────────────────────────
|
|
1441
1517
|
async getVocabulary(repositoryId) {
|
|
1442
1518
|
this.assertValidRepositoryId(repositoryId);
|
|
1443
|
-
return
|
|
1519
|
+
return this.track(
|
|
1520
|
+
"getVocabulary",
|
|
1521
|
+
repositoryId,
|
|
1522
|
+
() => getVocabulary(this.conn, repositoryId)
|
|
1523
|
+
);
|
|
1444
1524
|
}
|
|
1445
1525
|
async saveVocabulary(repositoryId, vocabulary) {
|
|
1446
1526
|
this.assertValidRepositoryId(repositoryId);
|
|
1447
|
-
return
|
|
1527
|
+
return this.track(
|
|
1528
|
+
"saveVocabulary",
|
|
1529
|
+
repositoryId,
|
|
1530
|
+
() => saveVocabulary(this.conn, repositoryId, vocabulary)
|
|
1531
|
+
);
|
|
1448
1532
|
}
|
|
1449
1533
|
async getVocabularyChangeLog(repositoryId, options) {
|
|
1450
1534
|
this.assertValidRepositoryId(repositoryId);
|
|
1451
|
-
return
|
|
1535
|
+
return this.track(
|
|
1536
|
+
"getVocabularyChangeLog",
|
|
1537
|
+
repositoryId,
|
|
1538
|
+
() => getVocabularyChangeLog(this.conn, repositoryId, options)
|
|
1539
|
+
);
|
|
1452
1540
|
}
|
|
1453
1541
|
// ─── Entities ──────────────────────────────────────────────────────
|
|
1454
1542
|
async createEntity(repositoryId, entity) {
|
|
1455
1543
|
this.assertValidRepositoryId(repositoryId);
|
|
1456
|
-
return
|
|
1544
|
+
return this.track(
|
|
1545
|
+
"createEntity",
|
|
1546
|
+
repositoryId,
|
|
1547
|
+
() => createEntity(this.conn, repositoryId, entity)
|
|
1548
|
+
);
|
|
1457
1549
|
}
|
|
1458
1550
|
async getEntity(repositoryId, entityId) {
|
|
1459
1551
|
this.assertValidRepositoryId(repositoryId);
|
|
1460
|
-
return
|
|
1552
|
+
return this.track(
|
|
1553
|
+
"getEntity",
|
|
1554
|
+
repositoryId,
|
|
1555
|
+
() => getEntity(this.conn, repositoryId, entityId)
|
|
1556
|
+
);
|
|
1461
1557
|
}
|
|
1462
1558
|
async getEntityBySlug(repositoryId, slug) {
|
|
1463
1559
|
this.assertValidRepositoryId(repositoryId);
|
|
1464
|
-
return
|
|
1560
|
+
return this.track(
|
|
1561
|
+
"getEntityBySlug",
|
|
1562
|
+
repositoryId,
|
|
1563
|
+
() => getEntityBySlug(this.conn, repositoryId, slug)
|
|
1564
|
+
);
|
|
1465
1565
|
}
|
|
1466
1566
|
async getEntities(repositoryId, entityIds) {
|
|
1467
1567
|
this.assertValidRepositoryId(repositoryId);
|
|
1468
|
-
return
|
|
1568
|
+
return this.track(
|
|
1569
|
+
"getEntities",
|
|
1570
|
+
repositoryId,
|
|
1571
|
+
() => getEntities(this.conn, repositoryId, entityIds)
|
|
1572
|
+
);
|
|
1469
1573
|
}
|
|
1470
1574
|
async updateEntity(repositoryId, entityId, updates) {
|
|
1471
1575
|
this.assertValidRepositoryId(repositoryId);
|
|
1472
|
-
return
|
|
1576
|
+
return this.track(
|
|
1577
|
+
"updateEntity",
|
|
1578
|
+
repositoryId,
|
|
1579
|
+
() => updateEntity(this.conn, repositoryId, entityId, updates)
|
|
1580
|
+
);
|
|
1473
1581
|
}
|
|
1474
1582
|
async deleteEntity(repositoryId, entityId) {
|
|
1475
1583
|
this.assertValidRepositoryId(repositoryId);
|
|
1476
|
-
return
|
|
1584
|
+
return this.track(
|
|
1585
|
+
"deleteEntity",
|
|
1586
|
+
repositoryId,
|
|
1587
|
+
() => deleteEntity(this.conn, repositoryId, entityId)
|
|
1588
|
+
);
|
|
1477
1589
|
}
|
|
1478
1590
|
async deleteEntities(repositoryId, ids) {
|
|
1479
1591
|
if (ids.length === 0) return { deleted: [], notFound: [] };
|
|
1480
1592
|
this.assertValidRepositoryId(repositoryId);
|
|
1593
|
+
return this.track("deleteEntities", repositoryId, () => this.deleteEntitiesImpl(repositoryId, ids));
|
|
1594
|
+
}
|
|
1595
|
+
async deleteEntitiesImpl(repositoryId, ids) {
|
|
1481
1596
|
const deleted = [];
|
|
1482
1597
|
const CHUNK = 100;
|
|
1483
1598
|
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
@@ -1516,32 +1631,59 @@ var CosmosDbProvider = class {
|
|
|
1516
1631
|
}
|
|
1517
1632
|
async deleteEntitiesByType(repositoryId, entityType) {
|
|
1518
1633
|
this.assertValidRepositoryId(repositoryId);
|
|
1519
|
-
return
|
|
1634
|
+
return this.track(
|
|
1635
|
+
"deleteEntitiesByType",
|
|
1636
|
+
repositoryId,
|
|
1637
|
+
() => deleteEntitiesByType(this.conn, repositoryId, entityType)
|
|
1638
|
+
);
|
|
1520
1639
|
}
|
|
1521
1640
|
async findEntities(repositoryId, query) {
|
|
1522
1641
|
this.assertValidRepositoryId(repositoryId);
|
|
1523
|
-
return
|
|
1642
|
+
return this.track(
|
|
1643
|
+
"findEntities",
|
|
1644
|
+
repositoryId,
|
|
1645
|
+
() => findEntities(this.conn, repositoryId, query)
|
|
1646
|
+
);
|
|
1524
1647
|
}
|
|
1525
1648
|
// ─── Relationships ─────────────────────────────────────────────────
|
|
1526
1649
|
async createRelationship(repositoryId, relationship) {
|
|
1527
1650
|
this.assertValidRepositoryId(repositoryId);
|
|
1528
|
-
return
|
|
1651
|
+
return this.track(
|
|
1652
|
+
"createRelationship",
|
|
1653
|
+
repositoryId,
|
|
1654
|
+
() => createRelationship(this.conn, repositoryId, relationship)
|
|
1655
|
+
);
|
|
1529
1656
|
}
|
|
1530
1657
|
async getRelationship(repositoryId, relationshipId) {
|
|
1531
1658
|
this.assertValidRepositoryId(repositoryId);
|
|
1532
|
-
return
|
|
1659
|
+
return this.track(
|
|
1660
|
+
"getRelationship",
|
|
1661
|
+
repositoryId,
|
|
1662
|
+
() => getRelationship(this.conn, repositoryId, relationshipId)
|
|
1663
|
+
);
|
|
1533
1664
|
}
|
|
1534
1665
|
async getEntityRelationships(repositoryId, entityId, options) {
|
|
1535
1666
|
this.assertValidRepositoryId(repositoryId);
|
|
1536
|
-
return
|
|
1667
|
+
return this.track(
|
|
1668
|
+
"getEntityRelationships",
|
|
1669
|
+
repositoryId,
|
|
1670
|
+
() => getEntityRelationships(this.conn, repositoryId, entityId, options)
|
|
1671
|
+
);
|
|
1537
1672
|
}
|
|
1538
1673
|
async deleteRelationship(repositoryId, relationshipId) {
|
|
1539
1674
|
this.assertValidRepositoryId(repositoryId);
|
|
1540
|
-
return
|
|
1675
|
+
return this.track(
|
|
1676
|
+
"deleteRelationship",
|
|
1677
|
+
repositoryId,
|
|
1678
|
+
() => deleteRelationship(this.conn, repositoryId, relationshipId)
|
|
1679
|
+
);
|
|
1541
1680
|
}
|
|
1542
1681
|
async deleteRelationships(repositoryId, ids) {
|
|
1543
1682
|
if (ids.length === 0) return { deleted: [], notFound: [] };
|
|
1544
1683
|
this.assertValidRepositoryId(repositoryId);
|
|
1684
|
+
return this.track("deleteRelationships", repositoryId, () => this.deleteRelationshipsImpl(repositoryId, ids));
|
|
1685
|
+
}
|
|
1686
|
+
async deleteRelationshipsImpl(repositoryId, ids) {
|
|
1545
1687
|
const deleted = [];
|
|
1546
1688
|
const CHUNK = 100;
|
|
1547
1689
|
for (let i = 0; i < ids.length; i += CHUNK) {
|
|
@@ -1577,30 +1719,99 @@ var CosmosDbProvider = class {
|
|
|
1577
1719
|
}
|
|
1578
1720
|
async deleteRelationshipsByType(repositoryId, relationshipType) {
|
|
1579
1721
|
this.assertValidRepositoryId(repositoryId);
|
|
1580
|
-
return
|
|
1722
|
+
return this.track(
|
|
1723
|
+
"deleteRelationshipsByType",
|
|
1724
|
+
repositoryId,
|
|
1725
|
+
() => deleteRelationshipsByType(this.conn, repositoryId, relationshipType)
|
|
1726
|
+
);
|
|
1581
1727
|
}
|
|
1582
1728
|
// ─── Graph Traversal (StorageProvider) ─────────────────────────────
|
|
1583
1729
|
async exploreNeighborhood(repositoryId, entityId, options) {
|
|
1584
1730
|
this.assertValidRepositoryId(repositoryId);
|
|
1585
|
-
return
|
|
1731
|
+
return this.track(
|
|
1732
|
+
"exploreNeighborhood",
|
|
1733
|
+
repositoryId,
|
|
1734
|
+
() => exploreNeighborhood(this.conn, repositoryId, entityId, options)
|
|
1735
|
+
);
|
|
1586
1736
|
}
|
|
1587
1737
|
async findPaths(repositoryId, sourceId, targetId, options) {
|
|
1588
1738
|
this.assertValidRepositoryId(repositoryId);
|
|
1589
|
-
return
|
|
1739
|
+
return this.track(
|
|
1740
|
+
"findPaths",
|
|
1741
|
+
repositoryId,
|
|
1742
|
+
() => findPaths(this.conn, repositoryId, sourceId, targetId, options)
|
|
1743
|
+
);
|
|
1590
1744
|
}
|
|
1591
1745
|
// ─── Timeline ──────────────────────────────────────────────────────
|
|
1592
1746
|
async getTimeline(repositoryId, entityId, options) {
|
|
1593
1747
|
this.assertValidRepositoryId(repositoryId);
|
|
1594
|
-
return
|
|
1748
|
+
return this.track(
|
|
1749
|
+
"getTimeline",
|
|
1750
|
+
repositoryId,
|
|
1751
|
+
() => getTimeline(this.conn, repositoryId, entityId, options)
|
|
1752
|
+
);
|
|
1595
1753
|
}
|
|
1596
1754
|
// ─── Bulk Operations ───────────────────────────────────────────────
|
|
1597
1755
|
exportAll(repositoryId) {
|
|
1598
1756
|
this.assertValidRepositoryId(repositoryId);
|
|
1599
|
-
return exportAll(this.conn, repositoryId);
|
|
1757
|
+
return this.trackIterable("exportAll", repositoryId, exportAll(this.conn, repositoryId));
|
|
1600
1758
|
}
|
|
1601
1759
|
async importBulk(repositoryId, data, options) {
|
|
1602
1760
|
this.assertValidRepositoryId(repositoryId);
|
|
1603
|
-
return
|
|
1761
|
+
return this.track(
|
|
1762
|
+
"importBulk",
|
|
1763
|
+
repositoryId,
|
|
1764
|
+
() => importBulk(this.conn, repositoryId, data, options)
|
|
1765
|
+
);
|
|
1766
|
+
}
|
|
1767
|
+
/**
|
|
1768
|
+
* Wrap an AsyncIterable so every emitted chunk is generated inside the
|
|
1769
|
+
* usage scope. On each iteration step, the scope aggregates charges from
|
|
1770
|
+
* the next chunk's submits; when the iterator completes (or is closed), a
|
|
1771
|
+
* single usage record is emitted for the whole stream.
|
|
1772
|
+
*/
|
|
1773
|
+
trackIterable(operation, repositoryId, source) {
|
|
1774
|
+
if (!this.reportUsage) return source;
|
|
1775
|
+
const sink = this.reportUsage;
|
|
1776
|
+
return {
|
|
1777
|
+
[Symbol.asyncIterator]: () => {
|
|
1778
|
+
const iter = source[Symbol.asyncIterator]();
|
|
1779
|
+
const acc = { ru: 0, calls: 0, retries: 0 };
|
|
1780
|
+
let emitted = false;
|
|
1781
|
+
const emit = () => {
|
|
1782
|
+
if (emitted) return;
|
|
1783
|
+
emitted = true;
|
|
1784
|
+
if (acc.calls > 0) {
|
|
1785
|
+
sink({
|
|
1786
|
+
provider: PROVIDER_NAME,
|
|
1787
|
+
operation,
|
|
1788
|
+
unit: "RU",
|
|
1789
|
+
value: acc.ru,
|
|
1790
|
+
repositoryId,
|
|
1791
|
+
timestamp: /* @__PURE__ */ new Date(),
|
|
1792
|
+
details: { calls: acc.calls, retries: acc.retries }
|
|
1793
|
+
});
|
|
1794
|
+
}
|
|
1795
|
+
};
|
|
1796
|
+
return {
|
|
1797
|
+
async next() {
|
|
1798
|
+
const step = await usageScope.run(acc, () => iter.next());
|
|
1799
|
+
if (step.done) emit();
|
|
1800
|
+
return step;
|
|
1801
|
+
},
|
|
1802
|
+
async return(value) {
|
|
1803
|
+
emit();
|
|
1804
|
+
if (iter.return) return iter.return(value);
|
|
1805
|
+
return { done: true, value };
|
|
1806
|
+
},
|
|
1807
|
+
async throw(err) {
|
|
1808
|
+
emit();
|
|
1809
|
+
if (iter.throw) return iter.throw(err);
|
|
1810
|
+
throw err;
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1604
1815
|
}
|
|
1605
1816
|
// ─── GraphTraversalProvider ────────────────────────────────────────
|
|
1606
1817
|
getCapabilities() {
|
|
@@ -1618,6 +1829,9 @@ var CosmosDbProvider = class {
|
|
|
1618
1829
|
}
|
|
1619
1830
|
async traverse(repositoryId, spec) {
|
|
1620
1831
|
this.assertValidRepositoryId(repositoryId);
|
|
1832
|
+
return this.track("traverse", repositoryId, () => this.traverseImpl(repositoryId, spec));
|
|
1833
|
+
}
|
|
1834
|
+
async traverseImpl(repositoryId, spec) {
|
|
1621
1835
|
const startTime = Date.now();
|
|
1622
1836
|
const vocabulary = await this.getVocabulary(repositoryId);
|
|
1623
1837
|
const compiled = this.compiler.compile(spec, vocabulary);
|
|
@@ -1711,6 +1925,9 @@ var CosmosDbProvider = class {
|
|
|
1711
1925
|
* repositoryId partition predicate.
|
|
1712
1926
|
*/
|
|
1713
1927
|
async executeNativeQuery(_repositoryId, query, params) {
|
|
1928
|
+
return this.track("executeNativeQuery", void 0, () => this.executeNativeQueryImpl(query, params));
|
|
1929
|
+
}
|
|
1930
|
+
async executeNativeQueryImpl(query, params) {
|
|
1714
1931
|
const startTime = Date.now();
|
|
1715
1932
|
try {
|
|
1716
1933
|
const result = await this.conn.submit(query, params);
|