@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.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: data.code ?? "UNKNOWN_ERROR",
392
- message: data.message ?? `HTTP ${response.status}`
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
- fusion: options?.fusion ?? "rrf",
629
- fusion_params: options?.fusionParams ?? { k: 60 },
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 = {