@wiscale/velesdb-sdk 1.2.0 → 1.3.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.mts +257 -1
- package/dist/index.d.ts +257 -1
- package/dist/index.js +406 -5
- package/dist/index.mjs +406 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -331,6 +331,60 @@ var WasmBackend = class {
|
|
|
331
331
|
}
|
|
332
332
|
return Math.abs(hash);
|
|
333
333
|
}
|
|
334
|
+
// ========================================================================
|
|
335
|
+
// Index Management (EPIC-009) - Stubs for WASM backend
|
|
336
|
+
// Note: Full implementation requires velesdb-wasm support
|
|
337
|
+
// ========================================================================
|
|
338
|
+
async createIndex(_collection, _options) {
|
|
339
|
+
this.ensureInitialized();
|
|
340
|
+
throw new Error(
|
|
341
|
+
"WasmBackend: createIndex is not yet supported. Index operations require the REST backend with velesdb-server."
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
async listIndexes(_collection) {
|
|
345
|
+
this.ensureInitialized();
|
|
346
|
+
return [];
|
|
347
|
+
}
|
|
348
|
+
async hasIndex(_collection, _label, _property) {
|
|
349
|
+
this.ensureInitialized();
|
|
350
|
+
return false;
|
|
351
|
+
}
|
|
352
|
+
async dropIndex(_collection, _label, _property) {
|
|
353
|
+
this.ensureInitialized();
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
// ========================================================================
|
|
357
|
+
// Knowledge Graph (EPIC-016 US-041) - Stubs for WASM backend
|
|
358
|
+
// Note: Graph operations require server-side EdgeStore
|
|
359
|
+
// ========================================================================
|
|
360
|
+
async addEdge(_collection, _edge) {
|
|
361
|
+
this.ensureInitialized();
|
|
362
|
+
throw new VelesDBError(
|
|
363
|
+
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
364
|
+
"NOT_SUPPORTED"
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
async getEdges(_collection, _options) {
|
|
368
|
+
this.ensureInitialized();
|
|
369
|
+
throw new VelesDBError(
|
|
370
|
+
"Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
|
|
371
|
+
"NOT_SUPPORTED"
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
async traverseGraph(_collection, _request) {
|
|
375
|
+
this.ensureInitialized();
|
|
376
|
+
throw new VelesDBError(
|
|
377
|
+
"Graph traversal is not supported in WASM backend. Use REST backend for graph features.",
|
|
378
|
+
"NOT_SUPPORTED"
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
async getNodeDegree(_collection, _nodeId) {
|
|
382
|
+
this.ensureInitialized();
|
|
383
|
+
throw new VelesDBError(
|
|
384
|
+
"Graph degree query is not supported in WASM backend. Use REST backend for graph features.",
|
|
385
|
+
"NOT_SUPPORTED"
|
|
386
|
+
);
|
|
387
|
+
}
|
|
334
388
|
};
|
|
335
389
|
|
|
336
390
|
// src/backends/rest.ts
|
|
@@ -366,6 +420,38 @@ var RestBackend = class {
|
|
|
366
420
|
throw new ConnectionError("REST backend not initialized");
|
|
367
421
|
}
|
|
368
422
|
}
|
|
423
|
+
mapStatusToErrorCode(status) {
|
|
424
|
+
switch (status) {
|
|
425
|
+
case 400:
|
|
426
|
+
return "BAD_REQUEST";
|
|
427
|
+
case 401:
|
|
428
|
+
return "UNAUTHORIZED";
|
|
429
|
+
case 403:
|
|
430
|
+
return "FORBIDDEN";
|
|
431
|
+
case 404:
|
|
432
|
+
return "NOT_FOUND";
|
|
433
|
+
case 409:
|
|
434
|
+
return "CONFLICT";
|
|
435
|
+
case 429:
|
|
436
|
+
return "RATE_LIMITED";
|
|
437
|
+
case 500:
|
|
438
|
+
return "INTERNAL_ERROR";
|
|
439
|
+
case 503:
|
|
440
|
+
return "SERVICE_UNAVAILABLE";
|
|
441
|
+
default:
|
|
442
|
+
return "UNKNOWN_ERROR";
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
extractErrorPayload(data) {
|
|
446
|
+
if (!data || typeof data !== "object") {
|
|
447
|
+
return {};
|
|
448
|
+
}
|
|
449
|
+
const payload = data;
|
|
450
|
+
const code = typeof payload.code === "string" ? payload.code : void 0;
|
|
451
|
+
const messageField = payload.message ?? payload.error;
|
|
452
|
+
const message = typeof messageField === "string" ? messageField : void 0;
|
|
453
|
+
return { code, message };
|
|
454
|
+
}
|
|
369
455
|
async request(method, path, body) {
|
|
370
456
|
const url = `${this.baseUrl}${path}`;
|
|
371
457
|
const headers = {
|
|
@@ -384,12 +470,13 @@ var RestBackend = class {
|
|
|
384
470
|
signal: controller.signal
|
|
385
471
|
});
|
|
386
472
|
clearTimeout(timeoutId);
|
|
387
|
-
const data = await response.json();
|
|
473
|
+
const data = await response.json().catch(() => ({}));
|
|
388
474
|
if (!response.ok) {
|
|
475
|
+
const errorPayload = this.extractErrorPayload(data);
|
|
389
476
|
return {
|
|
390
477
|
error: {
|
|
391
|
-
code:
|
|
392
|
-
message:
|
|
478
|
+
code: errorPayload.code ?? this.mapStatusToErrorCode(response.status),
|
|
479
|
+
message: errorPayload.message ?? `HTTP ${response.status}`
|
|
393
480
|
}
|
|
394
481
|
};
|
|
395
482
|
}
|
|
@@ -625,8 +712,8 @@ var RestBackend = class {
|
|
|
625
712
|
{
|
|
626
713
|
vectors: formattedVectors,
|
|
627
714
|
top_k: options?.k ?? 10,
|
|
628
|
-
|
|
629
|
-
|
|
715
|
+
strategy: options?.fusion ?? "rrf",
|
|
716
|
+
rrf_k: options?.fusionParams?.k ?? 60,
|
|
630
717
|
filter: options?.filter
|
|
631
718
|
}
|
|
632
719
|
);
|
|
@@ -668,6 +755,158 @@ var RestBackend = class {
|
|
|
668
755
|
async close() {
|
|
669
756
|
this._initialized = false;
|
|
670
757
|
}
|
|
758
|
+
// ========================================================================
|
|
759
|
+
// Index Management (EPIC-009)
|
|
760
|
+
// ========================================================================
|
|
761
|
+
async createIndex(collection, options) {
|
|
762
|
+
this.ensureInitialized();
|
|
763
|
+
const response = await this.request(
|
|
764
|
+
"POST",
|
|
765
|
+
`/collections/${encodeURIComponent(collection)}/indexes`,
|
|
766
|
+
{
|
|
767
|
+
label: options.label,
|
|
768
|
+
property: options.property,
|
|
769
|
+
index_type: options.indexType ?? "hash"
|
|
770
|
+
}
|
|
771
|
+
);
|
|
772
|
+
if (response.error) {
|
|
773
|
+
if (response.error.code === "NOT_FOUND") {
|
|
774
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
775
|
+
}
|
|
776
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
async listIndexes(collection) {
|
|
780
|
+
this.ensureInitialized();
|
|
781
|
+
const response = await this.request(
|
|
782
|
+
"GET",
|
|
783
|
+
`/collections/${encodeURIComponent(collection)}/indexes`
|
|
784
|
+
);
|
|
785
|
+
if (response.error) {
|
|
786
|
+
if (response.error.code === "NOT_FOUND") {
|
|
787
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
788
|
+
}
|
|
789
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
790
|
+
}
|
|
791
|
+
return (response.data?.indexes ?? []).map((idx) => ({
|
|
792
|
+
label: idx.label,
|
|
793
|
+
property: idx.property,
|
|
794
|
+
indexType: idx.index_type,
|
|
795
|
+
cardinality: idx.cardinality,
|
|
796
|
+
memoryBytes: idx.memory_bytes
|
|
797
|
+
}));
|
|
798
|
+
}
|
|
799
|
+
async hasIndex(collection, label, property) {
|
|
800
|
+
const indexes = await this.listIndexes(collection);
|
|
801
|
+
return indexes.some((idx) => idx.label === label && idx.property === property);
|
|
802
|
+
}
|
|
803
|
+
async dropIndex(collection, label, property) {
|
|
804
|
+
this.ensureInitialized();
|
|
805
|
+
const response = await this.request(
|
|
806
|
+
"DELETE",
|
|
807
|
+
`/collections/${encodeURIComponent(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
|
|
808
|
+
);
|
|
809
|
+
if (response.error) {
|
|
810
|
+
if (response.error.code === "NOT_FOUND") {
|
|
811
|
+
return false;
|
|
812
|
+
}
|
|
813
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
814
|
+
}
|
|
815
|
+
return response.data?.dropped ?? true;
|
|
816
|
+
}
|
|
817
|
+
// ========================================================================
|
|
818
|
+
// Knowledge Graph (EPIC-016 US-041)
|
|
819
|
+
// ========================================================================
|
|
820
|
+
async addEdge(collection, edge) {
|
|
821
|
+
this.ensureInitialized();
|
|
822
|
+
const response = await this.request(
|
|
823
|
+
"POST",
|
|
824
|
+
`/collections/${encodeURIComponent(collection)}/graph/edges`,
|
|
825
|
+
{
|
|
826
|
+
id: edge.id,
|
|
827
|
+
source: edge.source,
|
|
828
|
+
target: edge.target,
|
|
829
|
+
label: edge.label,
|
|
830
|
+
properties: edge.properties ?? {}
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
if (response.error) {
|
|
834
|
+
if (response.error.code === "NOT_FOUND") {
|
|
835
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
836
|
+
}
|
|
837
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
async getEdges(collection, options) {
|
|
841
|
+
this.ensureInitialized();
|
|
842
|
+
const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
|
|
843
|
+
const response = await this.request(
|
|
844
|
+
"GET",
|
|
845
|
+
`/collections/${encodeURIComponent(collection)}/graph/edges${queryParams}`
|
|
846
|
+
);
|
|
847
|
+
if (response.error) {
|
|
848
|
+
if (response.error.code === "NOT_FOUND") {
|
|
849
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
850
|
+
}
|
|
851
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
852
|
+
}
|
|
853
|
+
return response.data?.edges ?? [];
|
|
854
|
+
}
|
|
855
|
+
// ========================================================================
|
|
856
|
+
// Graph Traversal (EPIC-016 US-050)
|
|
857
|
+
// ========================================================================
|
|
858
|
+
async traverseGraph(collection, request) {
|
|
859
|
+
this.ensureInitialized();
|
|
860
|
+
const response = await this.request(
|
|
861
|
+
"POST",
|
|
862
|
+
`/collections/${encodeURIComponent(collection)}/graph/traverse`,
|
|
863
|
+
{
|
|
864
|
+
source: request.source,
|
|
865
|
+
strategy: request.strategy ?? "bfs",
|
|
866
|
+
max_depth: request.maxDepth ?? 3,
|
|
867
|
+
limit: request.limit ?? 100,
|
|
868
|
+
cursor: request.cursor,
|
|
869
|
+
rel_types: request.relTypes ?? []
|
|
870
|
+
}
|
|
871
|
+
);
|
|
872
|
+
if (response.error) {
|
|
873
|
+
if (response.error.code === "NOT_FOUND") {
|
|
874
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
875
|
+
}
|
|
876
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
877
|
+
}
|
|
878
|
+
const data = response.data;
|
|
879
|
+
return {
|
|
880
|
+
results: data.results.map((r) => ({
|
|
881
|
+
targetId: r.target_id,
|
|
882
|
+
depth: r.depth,
|
|
883
|
+
path: r.path
|
|
884
|
+
})),
|
|
885
|
+
nextCursor: data.next_cursor ?? void 0,
|
|
886
|
+
hasMore: data.has_more,
|
|
887
|
+
stats: {
|
|
888
|
+
visited: data.stats.visited,
|
|
889
|
+
depthReached: data.stats.depth_reached
|
|
890
|
+
}
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
async getNodeDegree(collection, nodeId) {
|
|
894
|
+
this.ensureInitialized();
|
|
895
|
+
const response = await this.request(
|
|
896
|
+
"GET",
|
|
897
|
+
`/collections/${encodeURIComponent(collection)}/graph/nodes/${nodeId}/degree`
|
|
898
|
+
);
|
|
899
|
+
if (response.error) {
|
|
900
|
+
if (response.error.code === "NOT_FOUND") {
|
|
901
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
902
|
+
}
|
|
903
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
904
|
+
}
|
|
905
|
+
return {
|
|
906
|
+
inDegree: response.data?.in_degree ?? 0,
|
|
907
|
+
outDegree: response.data?.out_degree ?? 0
|
|
908
|
+
};
|
|
909
|
+
}
|
|
671
910
|
};
|
|
672
911
|
|
|
673
912
|
// src/client.ts
|
|
@@ -1007,6 +1246,168 @@ var VelesDB = class {
|
|
|
1007
1246
|
get backendType() {
|
|
1008
1247
|
return this.config.backend;
|
|
1009
1248
|
}
|
|
1249
|
+
// ========================================================================
|
|
1250
|
+
// Index Management (EPIC-009)
|
|
1251
|
+
// ========================================================================
|
|
1252
|
+
/**
|
|
1253
|
+
* Create a property index for O(1) equality lookups or O(log n) range queries
|
|
1254
|
+
*
|
|
1255
|
+
* @param collection - Collection name
|
|
1256
|
+
* @param options - Index configuration (label, property, indexType)
|
|
1257
|
+
*
|
|
1258
|
+
* @example
|
|
1259
|
+
* ```typescript
|
|
1260
|
+
* // Create hash index for fast email lookups
|
|
1261
|
+
* await db.createIndex('users', { label: 'Person', property: 'email' });
|
|
1262
|
+
*
|
|
1263
|
+
* // Create range index for timestamp queries
|
|
1264
|
+
* await db.createIndex('events', { label: 'Event', property: 'timestamp', indexType: 'range' });
|
|
1265
|
+
* ```
|
|
1266
|
+
*/
|
|
1267
|
+
async createIndex(collection, options) {
|
|
1268
|
+
this.ensureInitialized();
|
|
1269
|
+
if (!options.label || !options.property) {
|
|
1270
|
+
throw new ValidationError("Index requires label and property");
|
|
1271
|
+
}
|
|
1272
|
+
await this.backend.createIndex(collection, options);
|
|
1273
|
+
}
|
|
1274
|
+
/**
|
|
1275
|
+
* List all indexes on a collection
|
|
1276
|
+
*
|
|
1277
|
+
* @param collection - Collection name
|
|
1278
|
+
* @returns Array of index information
|
|
1279
|
+
*/
|
|
1280
|
+
async listIndexes(collection) {
|
|
1281
|
+
this.ensureInitialized();
|
|
1282
|
+
return this.backend.listIndexes(collection);
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Check if an index exists
|
|
1286
|
+
*
|
|
1287
|
+
* @param collection - Collection name
|
|
1288
|
+
* @param label - Node label
|
|
1289
|
+
* @param property - Property name
|
|
1290
|
+
* @returns true if index exists
|
|
1291
|
+
*/
|
|
1292
|
+
async hasIndex(collection, label, property) {
|
|
1293
|
+
this.ensureInitialized();
|
|
1294
|
+
return this.backend.hasIndex(collection, label, property);
|
|
1295
|
+
}
|
|
1296
|
+
/**
|
|
1297
|
+
* Drop an index
|
|
1298
|
+
*
|
|
1299
|
+
* @param collection - Collection name
|
|
1300
|
+
* @param label - Node label
|
|
1301
|
+
* @param property - Property name
|
|
1302
|
+
* @returns true if index was dropped, false if it didn't exist
|
|
1303
|
+
*/
|
|
1304
|
+
async dropIndex(collection, label, property) {
|
|
1305
|
+
this.ensureInitialized();
|
|
1306
|
+
return this.backend.dropIndex(collection, label, property);
|
|
1307
|
+
}
|
|
1308
|
+
// ========================================================================
|
|
1309
|
+
// Knowledge Graph (EPIC-016 US-041)
|
|
1310
|
+
// ========================================================================
|
|
1311
|
+
/**
|
|
1312
|
+
* Add an edge to the collection's knowledge graph
|
|
1313
|
+
*
|
|
1314
|
+
* @param collection - Collection name
|
|
1315
|
+
* @param edge - Edge to add (id, source, target, label, properties)
|
|
1316
|
+
*
|
|
1317
|
+
* @example
|
|
1318
|
+
* ```typescript
|
|
1319
|
+
* await db.addEdge('social', {
|
|
1320
|
+
* id: 1,
|
|
1321
|
+
* source: 100,
|
|
1322
|
+
* target: 200,
|
|
1323
|
+
* label: 'FOLLOWS',
|
|
1324
|
+
* properties: { since: '2024-01-01' }
|
|
1325
|
+
* });
|
|
1326
|
+
* ```
|
|
1327
|
+
*/
|
|
1328
|
+
async addEdge(collection, edge) {
|
|
1329
|
+
this.ensureInitialized();
|
|
1330
|
+
if (!edge.label || typeof edge.label !== "string") {
|
|
1331
|
+
throw new ValidationError("Edge label is required and must be a string");
|
|
1332
|
+
}
|
|
1333
|
+
if (typeof edge.source !== "number" || typeof edge.target !== "number") {
|
|
1334
|
+
throw new ValidationError("Edge source and target must be numbers");
|
|
1335
|
+
}
|
|
1336
|
+
await this.backend.addEdge(collection, edge);
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Get edges from the collection's knowledge graph
|
|
1340
|
+
*
|
|
1341
|
+
* @param collection - Collection name
|
|
1342
|
+
* @param options - Query options (filter by label)
|
|
1343
|
+
* @returns Array of edges
|
|
1344
|
+
*
|
|
1345
|
+
* @example
|
|
1346
|
+
* ```typescript
|
|
1347
|
+
* // Get all edges with label "FOLLOWS"
|
|
1348
|
+
* const edges = await db.getEdges('social', { label: 'FOLLOWS' });
|
|
1349
|
+
* ```
|
|
1350
|
+
*/
|
|
1351
|
+
async getEdges(collection, options) {
|
|
1352
|
+
this.ensureInitialized();
|
|
1353
|
+
return this.backend.getEdges(collection, options);
|
|
1354
|
+
}
|
|
1355
|
+
// ========================================================================
|
|
1356
|
+
// Graph Traversal (EPIC-016 US-050)
|
|
1357
|
+
// ========================================================================
|
|
1358
|
+
/**
|
|
1359
|
+
* Traverse the graph using BFS or DFS from a source node
|
|
1360
|
+
*
|
|
1361
|
+
* @param collection - Collection name
|
|
1362
|
+
* @param request - Traversal request options
|
|
1363
|
+
* @returns Traversal response with results and stats
|
|
1364
|
+
*
|
|
1365
|
+
* @example
|
|
1366
|
+
* ```typescript
|
|
1367
|
+
* // BFS traversal from node 100
|
|
1368
|
+
* const result = await db.traverseGraph('social', {
|
|
1369
|
+
* source: 100,
|
|
1370
|
+
* strategy: 'bfs',
|
|
1371
|
+
* maxDepth: 3,
|
|
1372
|
+
* limit: 100,
|
|
1373
|
+
* relTypes: ['FOLLOWS', 'KNOWS']
|
|
1374
|
+
* });
|
|
1375
|
+
*
|
|
1376
|
+
* for (const node of result.results) {
|
|
1377
|
+
* console.log(`Reached node ${node.targetId} at depth ${node.depth}`);
|
|
1378
|
+
* }
|
|
1379
|
+
* ```
|
|
1380
|
+
*/
|
|
1381
|
+
async traverseGraph(collection, request) {
|
|
1382
|
+
this.ensureInitialized();
|
|
1383
|
+
if (typeof request.source !== "number") {
|
|
1384
|
+
throw new ValidationError("Source node ID must be a number");
|
|
1385
|
+
}
|
|
1386
|
+
if (request.strategy && !["bfs", "dfs"].includes(request.strategy)) {
|
|
1387
|
+
throw new ValidationError("Strategy must be 'bfs' or 'dfs'");
|
|
1388
|
+
}
|
|
1389
|
+
return this.backend.traverseGraph(collection, request);
|
|
1390
|
+
}
|
|
1391
|
+
/**
|
|
1392
|
+
* Get the in-degree and out-degree of a node
|
|
1393
|
+
*
|
|
1394
|
+
* @param collection - Collection name
|
|
1395
|
+
* @param nodeId - Node ID
|
|
1396
|
+
* @returns Degree response with inDegree and outDegree
|
|
1397
|
+
*
|
|
1398
|
+
* @example
|
|
1399
|
+
* ```typescript
|
|
1400
|
+
* const degree = await db.getNodeDegree('social', 100);
|
|
1401
|
+
* console.log(`In: ${degree.inDegree}, Out: ${degree.outDegree}`);
|
|
1402
|
+
* ```
|
|
1403
|
+
*/
|
|
1404
|
+
async getNodeDegree(collection, nodeId) {
|
|
1405
|
+
this.ensureInitialized();
|
|
1406
|
+
if (typeof nodeId !== "number") {
|
|
1407
|
+
throw new ValidationError("Node ID must be a number");
|
|
1408
|
+
}
|
|
1409
|
+
return this.backend.getNodeDegree(collection, nodeId);
|
|
1410
|
+
}
|
|
1010
1411
|
};
|
|
1011
1412
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1012
1413
|
0 && (module.exports = {
|