@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.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.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:
|
|
350
|
-
message:
|
|
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
|
-
|
|
587
|
-
|
|
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.
|
|
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",
|