@wiscale/velesdb-sdk 1.1.1 → 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.mjs CHANGED
@@ -289,6 +289,60 @@ var WasmBackend = class {
289
289
  }
290
290
  return Math.abs(hash);
291
291
  }
292
+ // ========================================================================
293
+ // Index Management (EPIC-009) - Stubs for WASM backend
294
+ // Note: Full implementation requires velesdb-wasm support
295
+ // ========================================================================
296
+ async createIndex(_collection, _options) {
297
+ this.ensureInitialized();
298
+ throw new Error(
299
+ "WasmBackend: createIndex is not yet supported. Index operations require the REST backend with velesdb-server."
300
+ );
301
+ }
302
+ async listIndexes(_collection) {
303
+ this.ensureInitialized();
304
+ return [];
305
+ }
306
+ async hasIndex(_collection, _label, _property) {
307
+ this.ensureInitialized();
308
+ return false;
309
+ }
310
+ async dropIndex(_collection, _label, _property) {
311
+ this.ensureInitialized();
312
+ return false;
313
+ }
314
+ // ========================================================================
315
+ // Knowledge Graph (EPIC-016 US-041) - Stubs for WASM backend
316
+ // Note: Graph operations require server-side EdgeStore
317
+ // ========================================================================
318
+ async addEdge(_collection, _edge) {
319
+ this.ensureInitialized();
320
+ throw new VelesDBError(
321
+ "Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
322
+ "NOT_SUPPORTED"
323
+ );
324
+ }
325
+ async getEdges(_collection, _options) {
326
+ this.ensureInitialized();
327
+ throw new VelesDBError(
328
+ "Knowledge Graph operations are not supported in WASM backend. Use REST backend for graph features.",
329
+ "NOT_SUPPORTED"
330
+ );
331
+ }
332
+ async traverseGraph(_collection, _request) {
333
+ this.ensureInitialized();
334
+ throw new VelesDBError(
335
+ "Graph traversal is not supported in WASM backend. Use REST backend for graph features.",
336
+ "NOT_SUPPORTED"
337
+ );
338
+ }
339
+ async getNodeDegree(_collection, _nodeId) {
340
+ this.ensureInitialized();
341
+ throw new VelesDBError(
342
+ "Graph degree query is not supported in WASM backend. Use REST backend for graph features.",
343
+ "NOT_SUPPORTED"
344
+ );
345
+ }
292
346
  };
293
347
 
294
348
  // src/backends/rest.ts
@@ -324,6 +378,38 @@ var RestBackend = class {
324
378
  throw new ConnectionError("REST backend not initialized");
325
379
  }
326
380
  }
381
+ mapStatusToErrorCode(status) {
382
+ switch (status) {
383
+ case 400:
384
+ return "BAD_REQUEST";
385
+ case 401:
386
+ return "UNAUTHORIZED";
387
+ case 403:
388
+ return "FORBIDDEN";
389
+ case 404:
390
+ return "NOT_FOUND";
391
+ case 409:
392
+ return "CONFLICT";
393
+ case 429:
394
+ return "RATE_LIMITED";
395
+ case 500:
396
+ return "INTERNAL_ERROR";
397
+ case 503:
398
+ return "SERVICE_UNAVAILABLE";
399
+ default:
400
+ return "UNKNOWN_ERROR";
401
+ }
402
+ }
403
+ extractErrorPayload(data) {
404
+ if (!data || typeof data !== "object") {
405
+ return {};
406
+ }
407
+ const payload = data;
408
+ const code = typeof payload.code === "string" ? payload.code : void 0;
409
+ const messageField = payload.message ?? payload.error;
410
+ const message = typeof messageField === "string" ? messageField : void 0;
411
+ return { code, message };
412
+ }
327
413
  async request(method, path, body) {
328
414
  const url = `${this.baseUrl}${path}`;
329
415
  const headers = {
@@ -342,12 +428,13 @@ var RestBackend = class {
342
428
  signal: controller.signal
343
429
  });
344
430
  clearTimeout(timeoutId);
345
- const data = await response.json();
431
+ const data = await response.json().catch(() => ({}));
346
432
  if (!response.ok) {
433
+ const errorPayload = this.extractErrorPayload(data);
347
434
  return {
348
435
  error: {
349
- code: data.code ?? "UNKNOWN_ERROR",
350
- message: data.message ?? `HTTP ${response.status}`
436
+ code: errorPayload.code ?? this.mapStatusToErrorCode(response.status),
437
+ message: errorPayload.message ?? `HTTP ${response.status}`
351
438
  }
352
439
  };
353
440
  }
@@ -583,8 +670,8 @@ var RestBackend = class {
583
670
  {
584
671
  vectors: formattedVectors,
585
672
  top_k: options?.k ?? 10,
586
- fusion: options?.fusion ?? "rrf",
587
- fusion_params: options?.fusionParams ?? { k: 60 },
673
+ strategy: options?.fusion ?? "rrf",
674
+ rrf_k: options?.fusionParams?.k ?? 60,
588
675
  filter: options?.filter
589
676
  }
590
677
  );
@@ -626,6 +713,158 @@ var RestBackend = class {
626
713
  async close() {
627
714
  this._initialized = false;
628
715
  }
716
+ // ========================================================================
717
+ // Index Management (EPIC-009)
718
+ // ========================================================================
719
+ async createIndex(collection, options) {
720
+ this.ensureInitialized();
721
+ const response = await this.request(
722
+ "POST",
723
+ `/collections/${encodeURIComponent(collection)}/indexes`,
724
+ {
725
+ label: options.label,
726
+ property: options.property,
727
+ index_type: options.indexType ?? "hash"
728
+ }
729
+ );
730
+ if (response.error) {
731
+ if (response.error.code === "NOT_FOUND") {
732
+ throw new NotFoundError(`Collection '${collection}'`);
733
+ }
734
+ throw new VelesDBError(response.error.message, response.error.code);
735
+ }
736
+ }
737
+ async listIndexes(collection) {
738
+ this.ensureInitialized();
739
+ const response = await this.request(
740
+ "GET",
741
+ `/collections/${encodeURIComponent(collection)}/indexes`
742
+ );
743
+ if (response.error) {
744
+ if (response.error.code === "NOT_FOUND") {
745
+ throw new NotFoundError(`Collection '${collection}'`);
746
+ }
747
+ throw new VelesDBError(response.error.message, response.error.code);
748
+ }
749
+ return (response.data?.indexes ?? []).map((idx) => ({
750
+ label: idx.label,
751
+ property: idx.property,
752
+ indexType: idx.index_type,
753
+ cardinality: idx.cardinality,
754
+ memoryBytes: idx.memory_bytes
755
+ }));
756
+ }
757
+ async hasIndex(collection, label, property) {
758
+ const indexes = await this.listIndexes(collection);
759
+ return indexes.some((idx) => idx.label === label && idx.property === property);
760
+ }
761
+ async dropIndex(collection, label, property) {
762
+ this.ensureInitialized();
763
+ const response = await this.request(
764
+ "DELETE",
765
+ `/collections/${encodeURIComponent(collection)}/indexes/${encodeURIComponent(label)}/${encodeURIComponent(property)}`
766
+ );
767
+ if (response.error) {
768
+ if (response.error.code === "NOT_FOUND") {
769
+ return false;
770
+ }
771
+ throw new VelesDBError(response.error.message, response.error.code);
772
+ }
773
+ return response.data?.dropped ?? true;
774
+ }
775
+ // ========================================================================
776
+ // Knowledge Graph (EPIC-016 US-041)
777
+ // ========================================================================
778
+ async addEdge(collection, edge) {
779
+ this.ensureInitialized();
780
+ const response = await this.request(
781
+ "POST",
782
+ `/collections/${encodeURIComponent(collection)}/graph/edges`,
783
+ {
784
+ id: edge.id,
785
+ source: edge.source,
786
+ target: edge.target,
787
+ label: edge.label,
788
+ properties: edge.properties ?? {}
789
+ }
790
+ );
791
+ if (response.error) {
792
+ if (response.error.code === "NOT_FOUND") {
793
+ throw new NotFoundError(`Collection '${collection}'`);
794
+ }
795
+ throw new VelesDBError(response.error.message, response.error.code);
796
+ }
797
+ }
798
+ async getEdges(collection, options) {
799
+ this.ensureInitialized();
800
+ const queryParams = options?.label ? `?label=${encodeURIComponent(options.label)}` : "";
801
+ const response = await this.request(
802
+ "GET",
803
+ `/collections/${encodeURIComponent(collection)}/graph/edges${queryParams}`
804
+ );
805
+ if (response.error) {
806
+ if (response.error.code === "NOT_FOUND") {
807
+ throw new NotFoundError(`Collection '${collection}'`);
808
+ }
809
+ throw new VelesDBError(response.error.message, response.error.code);
810
+ }
811
+ return response.data?.edges ?? [];
812
+ }
813
+ // ========================================================================
814
+ // Graph Traversal (EPIC-016 US-050)
815
+ // ========================================================================
816
+ async traverseGraph(collection, request) {
817
+ this.ensureInitialized();
818
+ const response = await this.request(
819
+ "POST",
820
+ `/collections/${encodeURIComponent(collection)}/graph/traverse`,
821
+ {
822
+ source: request.source,
823
+ strategy: request.strategy ?? "bfs",
824
+ max_depth: request.maxDepth ?? 3,
825
+ limit: request.limit ?? 100,
826
+ cursor: request.cursor,
827
+ rel_types: request.relTypes ?? []
828
+ }
829
+ );
830
+ if (response.error) {
831
+ if (response.error.code === "NOT_FOUND") {
832
+ throw new NotFoundError(`Collection '${collection}'`);
833
+ }
834
+ throw new VelesDBError(response.error.message, response.error.code);
835
+ }
836
+ const data = response.data;
837
+ return {
838
+ results: data.results.map((r) => ({
839
+ targetId: r.target_id,
840
+ depth: r.depth,
841
+ path: r.path
842
+ })),
843
+ nextCursor: data.next_cursor ?? void 0,
844
+ hasMore: data.has_more,
845
+ stats: {
846
+ visited: data.stats.visited,
847
+ depthReached: data.stats.depth_reached
848
+ }
849
+ };
850
+ }
851
+ async getNodeDegree(collection, nodeId) {
852
+ this.ensureInitialized();
853
+ const response = await this.request(
854
+ "GET",
855
+ `/collections/${encodeURIComponent(collection)}/graph/nodes/${nodeId}/degree`
856
+ );
857
+ if (response.error) {
858
+ if (response.error.code === "NOT_FOUND") {
859
+ throw new NotFoundError(`Collection '${collection}'`);
860
+ }
861
+ throw new VelesDBError(response.error.message, response.error.code);
862
+ }
863
+ return {
864
+ inDegree: response.data?.in_degree ?? 0,
865
+ outDegree: response.data?.out_degree ?? 0
866
+ };
867
+ }
629
868
  };
630
869
 
631
870
  // src/client.ts
@@ -965,6 +1204,168 @@ var VelesDB = class {
965
1204
  get backendType() {
966
1205
  return this.config.backend;
967
1206
  }
1207
+ // ========================================================================
1208
+ // Index Management (EPIC-009)
1209
+ // ========================================================================
1210
+ /**
1211
+ * Create a property index for O(1) equality lookups or O(log n) range queries
1212
+ *
1213
+ * @param collection - Collection name
1214
+ * @param options - Index configuration (label, property, indexType)
1215
+ *
1216
+ * @example
1217
+ * ```typescript
1218
+ * // Create hash index for fast email lookups
1219
+ * await db.createIndex('users', { label: 'Person', property: 'email' });
1220
+ *
1221
+ * // Create range index for timestamp queries
1222
+ * await db.createIndex('events', { label: 'Event', property: 'timestamp', indexType: 'range' });
1223
+ * ```
1224
+ */
1225
+ async createIndex(collection, options) {
1226
+ this.ensureInitialized();
1227
+ if (!options.label || !options.property) {
1228
+ throw new ValidationError("Index requires label and property");
1229
+ }
1230
+ await this.backend.createIndex(collection, options);
1231
+ }
1232
+ /**
1233
+ * List all indexes on a collection
1234
+ *
1235
+ * @param collection - Collection name
1236
+ * @returns Array of index information
1237
+ */
1238
+ async listIndexes(collection) {
1239
+ this.ensureInitialized();
1240
+ return this.backend.listIndexes(collection);
1241
+ }
1242
+ /**
1243
+ * Check if an index exists
1244
+ *
1245
+ * @param collection - Collection name
1246
+ * @param label - Node label
1247
+ * @param property - Property name
1248
+ * @returns true if index exists
1249
+ */
1250
+ async hasIndex(collection, label, property) {
1251
+ this.ensureInitialized();
1252
+ return this.backend.hasIndex(collection, label, property);
1253
+ }
1254
+ /**
1255
+ * Drop an index
1256
+ *
1257
+ * @param collection - Collection name
1258
+ * @param label - Node label
1259
+ * @param property - Property name
1260
+ * @returns true if index was dropped, false if it didn't exist
1261
+ */
1262
+ async dropIndex(collection, label, property) {
1263
+ this.ensureInitialized();
1264
+ return this.backend.dropIndex(collection, label, property);
1265
+ }
1266
+ // ========================================================================
1267
+ // Knowledge Graph (EPIC-016 US-041)
1268
+ // ========================================================================
1269
+ /**
1270
+ * Add an edge to the collection's knowledge graph
1271
+ *
1272
+ * @param collection - Collection name
1273
+ * @param edge - Edge to add (id, source, target, label, properties)
1274
+ *
1275
+ * @example
1276
+ * ```typescript
1277
+ * await db.addEdge('social', {
1278
+ * id: 1,
1279
+ * source: 100,
1280
+ * target: 200,
1281
+ * label: 'FOLLOWS',
1282
+ * properties: { since: '2024-01-01' }
1283
+ * });
1284
+ * ```
1285
+ */
1286
+ async addEdge(collection, edge) {
1287
+ this.ensureInitialized();
1288
+ if (!edge.label || typeof edge.label !== "string") {
1289
+ throw new ValidationError("Edge label is required and must be a string");
1290
+ }
1291
+ if (typeof edge.source !== "number" || typeof edge.target !== "number") {
1292
+ throw new ValidationError("Edge source and target must be numbers");
1293
+ }
1294
+ await this.backend.addEdge(collection, edge);
1295
+ }
1296
+ /**
1297
+ * Get edges from the collection's knowledge graph
1298
+ *
1299
+ * @param collection - Collection name
1300
+ * @param options - Query options (filter by label)
1301
+ * @returns Array of edges
1302
+ *
1303
+ * @example
1304
+ * ```typescript
1305
+ * // Get all edges with label "FOLLOWS"
1306
+ * const edges = await db.getEdges('social', { label: 'FOLLOWS' });
1307
+ * ```
1308
+ */
1309
+ async getEdges(collection, options) {
1310
+ this.ensureInitialized();
1311
+ return this.backend.getEdges(collection, options);
1312
+ }
1313
+ // ========================================================================
1314
+ // Graph Traversal (EPIC-016 US-050)
1315
+ // ========================================================================
1316
+ /**
1317
+ * Traverse the graph using BFS or DFS from a source node
1318
+ *
1319
+ * @param collection - Collection name
1320
+ * @param request - Traversal request options
1321
+ * @returns Traversal response with results and stats
1322
+ *
1323
+ * @example
1324
+ * ```typescript
1325
+ * // BFS traversal from node 100
1326
+ * const result = await db.traverseGraph('social', {
1327
+ * source: 100,
1328
+ * strategy: 'bfs',
1329
+ * maxDepth: 3,
1330
+ * limit: 100,
1331
+ * relTypes: ['FOLLOWS', 'KNOWS']
1332
+ * });
1333
+ *
1334
+ * for (const node of result.results) {
1335
+ * console.log(`Reached node ${node.targetId} at depth ${node.depth}`);
1336
+ * }
1337
+ * ```
1338
+ */
1339
+ async traverseGraph(collection, request) {
1340
+ this.ensureInitialized();
1341
+ if (typeof request.source !== "number") {
1342
+ throw new ValidationError("Source node ID must be a number");
1343
+ }
1344
+ if (request.strategy && !["bfs", "dfs"].includes(request.strategy)) {
1345
+ throw new ValidationError("Strategy must be 'bfs' or 'dfs'");
1346
+ }
1347
+ return this.backend.traverseGraph(collection, request);
1348
+ }
1349
+ /**
1350
+ * Get the in-degree and out-degree of a node
1351
+ *
1352
+ * @param collection - Collection name
1353
+ * @param nodeId - Node ID
1354
+ * @returns Degree response with inDegree and outDegree
1355
+ *
1356
+ * @example
1357
+ * ```typescript
1358
+ * const degree = await db.getNodeDegree('social', 100);
1359
+ * console.log(`In: ${degree.inDegree}, Out: ${degree.outDegree}`);
1360
+ * ```
1361
+ */
1362
+ async getNodeDegree(collection, nodeId) {
1363
+ this.ensureInitialized();
1364
+ if (typeof nodeId !== "number") {
1365
+ throw new ValidationError("Node ID must be a number");
1366
+ }
1367
+ return this.backend.getNodeDegree(collection, nodeId);
1368
+ }
968
1369
  };
969
1370
  export {
970
1371
  ConnectionError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wiscale/velesdb-sdk",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "VelesDB TypeScript SDK: The Local Vector Database for AI & RAG. Microsecond semantic search in Browser & Node.js.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",