@wiscale/velesdb-sdk 0.5.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/README.md +160 -0
- package/dist/index.d.mts +299 -0
- package/dist/index.d.ts +299 -0
- package/dist/index.js +690 -0
- package/dist/index.mjs +647 -0
- package/package.json +71 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,647 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
var VelesDBError = class extends Error {
|
|
3
|
+
constructor(message, code, cause) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.cause = cause;
|
|
7
|
+
this.name = "VelesDBError";
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
var ConnectionError = class extends VelesDBError {
|
|
11
|
+
constructor(message, cause) {
|
|
12
|
+
super(message, "CONNECTION_ERROR", cause);
|
|
13
|
+
this.name = "ConnectionError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var ValidationError = class extends VelesDBError {
|
|
17
|
+
constructor(message) {
|
|
18
|
+
super(message, "VALIDATION_ERROR");
|
|
19
|
+
this.name = "ValidationError";
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var NotFoundError = class extends VelesDBError {
|
|
23
|
+
constructor(resource) {
|
|
24
|
+
super(`${resource} not found`, "NOT_FOUND");
|
|
25
|
+
this.name = "NotFoundError";
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
// src/backends/wasm.ts
|
|
30
|
+
var WasmBackend = class {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.wasmModule = null;
|
|
33
|
+
this.collections = /* @__PURE__ */ new Map();
|
|
34
|
+
this._initialized = false;
|
|
35
|
+
}
|
|
36
|
+
async init() {
|
|
37
|
+
if (this._initialized) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
this.wasmModule = await import("velesdb-wasm");
|
|
42
|
+
await this.wasmModule.default();
|
|
43
|
+
this._initialized = true;
|
|
44
|
+
} catch (error) {
|
|
45
|
+
throw new ConnectionError(
|
|
46
|
+
"Failed to initialize WASM module",
|
|
47
|
+
error instanceof Error ? error : void 0
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
isInitialized() {
|
|
52
|
+
return this._initialized;
|
|
53
|
+
}
|
|
54
|
+
ensureInitialized() {
|
|
55
|
+
if (!this._initialized || !this.wasmModule) {
|
|
56
|
+
throw new ConnectionError("WASM backend not initialized");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async createCollection(name, config) {
|
|
60
|
+
this.ensureInitialized();
|
|
61
|
+
if (this.collections.has(name)) {
|
|
62
|
+
throw new VelesDBError(`Collection '${name}' already exists`, "COLLECTION_EXISTS");
|
|
63
|
+
}
|
|
64
|
+
const metric = config.metric ?? "cosine";
|
|
65
|
+
const store = new this.wasmModule.VectorStore(config.dimension, metric);
|
|
66
|
+
this.collections.set(name, {
|
|
67
|
+
config: { ...config, metric },
|
|
68
|
+
store,
|
|
69
|
+
payloads: /* @__PURE__ */ new Map(),
|
|
70
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
async deleteCollection(name) {
|
|
74
|
+
this.ensureInitialized();
|
|
75
|
+
const collection = this.collections.get(name);
|
|
76
|
+
if (!collection) {
|
|
77
|
+
throw new NotFoundError(`Collection '${name}'`);
|
|
78
|
+
}
|
|
79
|
+
collection.store.free();
|
|
80
|
+
this.collections.delete(name);
|
|
81
|
+
}
|
|
82
|
+
async getCollection(name) {
|
|
83
|
+
this.ensureInitialized();
|
|
84
|
+
const collection = this.collections.get(name);
|
|
85
|
+
if (!collection) {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
name,
|
|
90
|
+
dimension: collection.config.dimension,
|
|
91
|
+
metric: collection.config.metric ?? "cosine",
|
|
92
|
+
count: collection.store.len,
|
|
93
|
+
createdAt: collection.createdAt
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
async listCollections() {
|
|
97
|
+
this.ensureInitialized();
|
|
98
|
+
const result = [];
|
|
99
|
+
for (const [name, data] of this.collections) {
|
|
100
|
+
result.push({
|
|
101
|
+
name,
|
|
102
|
+
dimension: data.config.dimension,
|
|
103
|
+
metric: data.config.metric ?? "cosine",
|
|
104
|
+
count: data.store.len,
|
|
105
|
+
createdAt: data.createdAt
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
async insert(collectionName, doc) {
|
|
111
|
+
this.ensureInitialized();
|
|
112
|
+
const collection = this.collections.get(collectionName);
|
|
113
|
+
if (!collection) {
|
|
114
|
+
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
115
|
+
}
|
|
116
|
+
const id = this.toNumericId(doc.id);
|
|
117
|
+
const vector = doc.vector instanceof Float32Array ? doc.vector : new Float32Array(doc.vector);
|
|
118
|
+
if (vector.length !== collection.config.dimension) {
|
|
119
|
+
throw new VelesDBError(
|
|
120
|
+
`Vector dimension mismatch: expected ${collection.config.dimension}, got ${vector.length}`,
|
|
121
|
+
"DIMENSION_MISMATCH"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
collection.store.insert(BigInt(id), vector);
|
|
125
|
+
if (doc.payload) {
|
|
126
|
+
collection.payloads.set(String(doc.id), doc.payload);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async insertBatch(collectionName, docs) {
|
|
130
|
+
this.ensureInitialized();
|
|
131
|
+
const collection = this.collections.get(collectionName);
|
|
132
|
+
if (!collection) {
|
|
133
|
+
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
134
|
+
}
|
|
135
|
+
for (const doc of docs) {
|
|
136
|
+
const vectorLen = Array.isArray(doc.vector) ? doc.vector.length : doc.vector.length;
|
|
137
|
+
if (vectorLen !== collection.config.dimension) {
|
|
138
|
+
throw new VelesDBError(
|
|
139
|
+
`Vector dimension mismatch for doc ${doc.id}: expected ${collection.config.dimension}, got ${vectorLen}`,
|
|
140
|
+
"DIMENSION_MISMATCH"
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
collection.store.reserve(docs.length);
|
|
145
|
+
const batch = docs.map((doc) => [
|
|
146
|
+
BigInt(this.toNumericId(doc.id)),
|
|
147
|
+
Array.isArray(doc.vector) ? doc.vector : Array.from(doc.vector)
|
|
148
|
+
]);
|
|
149
|
+
collection.store.insert_batch(batch);
|
|
150
|
+
for (const doc of docs) {
|
|
151
|
+
if (doc.payload) {
|
|
152
|
+
collection.payloads.set(String(doc.id), doc.payload);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
async search(collectionName, query, options) {
|
|
157
|
+
this.ensureInitialized();
|
|
158
|
+
const collection = this.collections.get(collectionName);
|
|
159
|
+
if (!collection) {
|
|
160
|
+
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
161
|
+
}
|
|
162
|
+
const queryVector = query instanceof Float32Array ? query : new Float32Array(query);
|
|
163
|
+
if (queryVector.length !== collection.config.dimension) {
|
|
164
|
+
throw new VelesDBError(
|
|
165
|
+
`Query dimension mismatch: expected ${collection.config.dimension}, got ${queryVector.length}`,
|
|
166
|
+
"DIMENSION_MISMATCH"
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
const k = options?.k ?? 10;
|
|
170
|
+
const rawResults = collection.store.search(queryVector, k);
|
|
171
|
+
return rawResults.map(([id, score]) => {
|
|
172
|
+
const stringId = String(id);
|
|
173
|
+
const result = {
|
|
174
|
+
id: stringId,
|
|
175
|
+
score
|
|
176
|
+
};
|
|
177
|
+
const payload = collection.payloads.get(stringId);
|
|
178
|
+
if (payload) {
|
|
179
|
+
result.payload = payload;
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
async delete(collectionName, id) {
|
|
185
|
+
this.ensureInitialized();
|
|
186
|
+
const collection = this.collections.get(collectionName);
|
|
187
|
+
if (!collection) {
|
|
188
|
+
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
189
|
+
}
|
|
190
|
+
const numericId = this.toNumericId(id);
|
|
191
|
+
const removed = collection.store.remove(BigInt(numericId));
|
|
192
|
+
if (removed) {
|
|
193
|
+
collection.payloads.delete(String(id));
|
|
194
|
+
}
|
|
195
|
+
return removed;
|
|
196
|
+
}
|
|
197
|
+
async get(collectionName, id) {
|
|
198
|
+
this.ensureInitialized();
|
|
199
|
+
const collection = this.collections.get(collectionName);
|
|
200
|
+
if (!collection) {
|
|
201
|
+
throw new NotFoundError(`Collection '${collectionName}'`);
|
|
202
|
+
}
|
|
203
|
+
const payload = collection.payloads.get(String(id));
|
|
204
|
+
if (!payload) {
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
return {
|
|
208
|
+
id,
|
|
209
|
+
vector: [],
|
|
210
|
+
// Not available in current WASM impl
|
|
211
|
+
payload
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
async close() {
|
|
215
|
+
for (const [, data] of this.collections) {
|
|
216
|
+
data.store.free();
|
|
217
|
+
}
|
|
218
|
+
this.collections.clear();
|
|
219
|
+
this._initialized = false;
|
|
220
|
+
}
|
|
221
|
+
toNumericId(id) {
|
|
222
|
+
if (typeof id === "number") {
|
|
223
|
+
return id;
|
|
224
|
+
}
|
|
225
|
+
const parsed = parseInt(id, 10);
|
|
226
|
+
if (!isNaN(parsed)) {
|
|
227
|
+
return parsed;
|
|
228
|
+
}
|
|
229
|
+
let hash = 0;
|
|
230
|
+
for (let i = 0; i < id.length; i++) {
|
|
231
|
+
const char = id.charCodeAt(i);
|
|
232
|
+
hash = (hash << 5) - hash + char;
|
|
233
|
+
hash = hash & hash;
|
|
234
|
+
}
|
|
235
|
+
return Math.abs(hash);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
// src/backends/rest.ts
|
|
240
|
+
var RestBackend = class {
|
|
241
|
+
constructor(url, apiKey, timeout = 3e4) {
|
|
242
|
+
this._initialized = false;
|
|
243
|
+
this.baseUrl = url.replace(/\/$/, "");
|
|
244
|
+
this.apiKey = apiKey;
|
|
245
|
+
this.timeout = timeout;
|
|
246
|
+
}
|
|
247
|
+
async init() {
|
|
248
|
+
if (this._initialized) {
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
const response = await this.request("GET", "/health");
|
|
253
|
+
if (response.error) {
|
|
254
|
+
throw new Error(response.error.message);
|
|
255
|
+
}
|
|
256
|
+
this._initialized = true;
|
|
257
|
+
} catch (error) {
|
|
258
|
+
throw new ConnectionError(
|
|
259
|
+
`Failed to connect to VelesDB server at ${this.baseUrl}`,
|
|
260
|
+
error instanceof Error ? error : void 0
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
isInitialized() {
|
|
265
|
+
return this._initialized;
|
|
266
|
+
}
|
|
267
|
+
ensureInitialized() {
|
|
268
|
+
if (!this._initialized) {
|
|
269
|
+
throw new ConnectionError("REST backend not initialized");
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async request(method, path, body) {
|
|
273
|
+
const url = `${this.baseUrl}${path}`;
|
|
274
|
+
const headers = {
|
|
275
|
+
"Content-Type": "application/json"
|
|
276
|
+
};
|
|
277
|
+
if (this.apiKey) {
|
|
278
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
279
|
+
}
|
|
280
|
+
const controller = new AbortController();
|
|
281
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
282
|
+
try {
|
|
283
|
+
const response = await fetch(url, {
|
|
284
|
+
method,
|
|
285
|
+
headers,
|
|
286
|
+
body: body ? JSON.stringify(body) : void 0,
|
|
287
|
+
signal: controller.signal
|
|
288
|
+
});
|
|
289
|
+
clearTimeout(timeoutId);
|
|
290
|
+
const data = await response.json();
|
|
291
|
+
if (!response.ok) {
|
|
292
|
+
return {
|
|
293
|
+
error: {
|
|
294
|
+
code: data.code ?? "UNKNOWN_ERROR",
|
|
295
|
+
message: data.message ?? `HTTP ${response.status}`
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
return { data };
|
|
300
|
+
} catch (error) {
|
|
301
|
+
clearTimeout(timeoutId);
|
|
302
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
303
|
+
throw new ConnectionError("Request timeout");
|
|
304
|
+
}
|
|
305
|
+
throw new ConnectionError(
|
|
306
|
+
`Request failed: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
307
|
+
error instanceof Error ? error : void 0
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
async createCollection(name, config) {
|
|
312
|
+
this.ensureInitialized();
|
|
313
|
+
const response = await this.request("POST", "/collections", {
|
|
314
|
+
name,
|
|
315
|
+
dimension: config.dimension,
|
|
316
|
+
metric: config.metric ?? "cosine",
|
|
317
|
+
description: config.description
|
|
318
|
+
});
|
|
319
|
+
if (response.error) {
|
|
320
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
async deleteCollection(name) {
|
|
324
|
+
this.ensureInitialized();
|
|
325
|
+
const response = await this.request("DELETE", `/collections/${encodeURIComponent(name)}`);
|
|
326
|
+
if (response.error) {
|
|
327
|
+
if (response.error.code === "NOT_FOUND") {
|
|
328
|
+
throw new NotFoundError(`Collection '${name}'`);
|
|
329
|
+
}
|
|
330
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async getCollection(name) {
|
|
334
|
+
this.ensureInitialized();
|
|
335
|
+
const response = await this.request(
|
|
336
|
+
"GET",
|
|
337
|
+
`/collections/${encodeURIComponent(name)}`
|
|
338
|
+
);
|
|
339
|
+
if (response.error) {
|
|
340
|
+
if (response.error.code === "NOT_FOUND") {
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
344
|
+
}
|
|
345
|
+
return response.data ?? null;
|
|
346
|
+
}
|
|
347
|
+
async listCollections() {
|
|
348
|
+
this.ensureInitialized();
|
|
349
|
+
const response = await this.request("GET", "/collections");
|
|
350
|
+
if (response.error) {
|
|
351
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
352
|
+
}
|
|
353
|
+
return response.data ?? [];
|
|
354
|
+
}
|
|
355
|
+
async insert(collection, doc) {
|
|
356
|
+
this.ensureInitialized();
|
|
357
|
+
const vector = doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector;
|
|
358
|
+
const response = await this.request(
|
|
359
|
+
"POST",
|
|
360
|
+
`/collections/${encodeURIComponent(collection)}/vectors`,
|
|
361
|
+
{
|
|
362
|
+
id: doc.id,
|
|
363
|
+
vector,
|
|
364
|
+
payload: doc.payload
|
|
365
|
+
}
|
|
366
|
+
);
|
|
367
|
+
if (response.error) {
|
|
368
|
+
if (response.error.code === "NOT_FOUND") {
|
|
369
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
370
|
+
}
|
|
371
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
async insertBatch(collection, docs) {
|
|
375
|
+
this.ensureInitialized();
|
|
376
|
+
const vectors = docs.map((doc) => ({
|
|
377
|
+
id: doc.id,
|
|
378
|
+
vector: doc.vector instanceof Float32Array ? Array.from(doc.vector) : doc.vector,
|
|
379
|
+
payload: doc.payload
|
|
380
|
+
}));
|
|
381
|
+
const response = await this.request(
|
|
382
|
+
"POST",
|
|
383
|
+
`/collections/${encodeURIComponent(collection)}/vectors/batch`,
|
|
384
|
+
{ vectors }
|
|
385
|
+
);
|
|
386
|
+
if (response.error) {
|
|
387
|
+
if (response.error.code === "NOT_FOUND") {
|
|
388
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
389
|
+
}
|
|
390
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
async search(collection, query, options) {
|
|
394
|
+
this.ensureInitialized();
|
|
395
|
+
const queryVector = query instanceof Float32Array ? Array.from(query) : query;
|
|
396
|
+
const response = await this.request(
|
|
397
|
+
"POST",
|
|
398
|
+
`/collections/${encodeURIComponent(collection)}/search`,
|
|
399
|
+
{
|
|
400
|
+
vector: queryVector,
|
|
401
|
+
k: options?.k ?? 10,
|
|
402
|
+
filter: options?.filter,
|
|
403
|
+
include_vectors: options?.includeVectors ?? false
|
|
404
|
+
}
|
|
405
|
+
);
|
|
406
|
+
if (response.error) {
|
|
407
|
+
if (response.error.code === "NOT_FOUND") {
|
|
408
|
+
throw new NotFoundError(`Collection '${collection}'`);
|
|
409
|
+
}
|
|
410
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
411
|
+
}
|
|
412
|
+
return response.data ?? [];
|
|
413
|
+
}
|
|
414
|
+
async delete(collection, id) {
|
|
415
|
+
this.ensureInitialized();
|
|
416
|
+
const response = await this.request(
|
|
417
|
+
"DELETE",
|
|
418
|
+
`/collections/${encodeURIComponent(collection)}/vectors/${encodeURIComponent(String(id))}`
|
|
419
|
+
);
|
|
420
|
+
if (response.error) {
|
|
421
|
+
if (response.error.code === "NOT_FOUND") {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
425
|
+
}
|
|
426
|
+
return response.data?.deleted ?? false;
|
|
427
|
+
}
|
|
428
|
+
async get(collection, id) {
|
|
429
|
+
this.ensureInitialized();
|
|
430
|
+
const response = await this.request(
|
|
431
|
+
"GET",
|
|
432
|
+
`/collections/${encodeURIComponent(collection)}/vectors/${encodeURIComponent(String(id))}`
|
|
433
|
+
);
|
|
434
|
+
if (response.error) {
|
|
435
|
+
if (response.error.code === "NOT_FOUND") {
|
|
436
|
+
return null;
|
|
437
|
+
}
|
|
438
|
+
throw new VelesDBError(response.error.message, response.error.code);
|
|
439
|
+
}
|
|
440
|
+
return response.data ?? null;
|
|
441
|
+
}
|
|
442
|
+
async close() {
|
|
443
|
+
this._initialized = false;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
|
|
447
|
+
// src/client.ts
|
|
448
|
+
var VelesDB = class {
|
|
449
|
+
/**
|
|
450
|
+
* Create a new VelesDB client
|
|
451
|
+
*
|
|
452
|
+
* @param config - Client configuration
|
|
453
|
+
* @throws {ValidationError} If configuration is invalid
|
|
454
|
+
*/
|
|
455
|
+
constructor(config) {
|
|
456
|
+
this.initialized = false;
|
|
457
|
+
this.validateConfig(config);
|
|
458
|
+
this.config = config;
|
|
459
|
+
this.backend = this.createBackend(config);
|
|
460
|
+
}
|
|
461
|
+
validateConfig(config) {
|
|
462
|
+
if (!config.backend) {
|
|
463
|
+
throw new ValidationError("Backend type is required");
|
|
464
|
+
}
|
|
465
|
+
if (config.backend !== "wasm" && config.backend !== "rest") {
|
|
466
|
+
throw new ValidationError(`Invalid backend type: ${config.backend}. Use 'wasm' or 'rest'`);
|
|
467
|
+
}
|
|
468
|
+
if (config.backend === "rest" && !config.url) {
|
|
469
|
+
throw new ValidationError("URL is required for REST backend");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
createBackend(config) {
|
|
473
|
+
switch (config.backend) {
|
|
474
|
+
case "wasm":
|
|
475
|
+
return new WasmBackend();
|
|
476
|
+
case "rest":
|
|
477
|
+
return new RestBackend(config.url, config.apiKey, config.timeout);
|
|
478
|
+
default:
|
|
479
|
+
throw new ValidationError(`Unknown backend: ${config.backend}`);
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Initialize the client
|
|
484
|
+
* Must be called before any other operations
|
|
485
|
+
*/
|
|
486
|
+
async init() {
|
|
487
|
+
if (this.initialized) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
await this.backend.init();
|
|
491
|
+
this.initialized = true;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Check if client is initialized
|
|
495
|
+
*/
|
|
496
|
+
isInitialized() {
|
|
497
|
+
return this.initialized;
|
|
498
|
+
}
|
|
499
|
+
ensureInitialized() {
|
|
500
|
+
if (!this.initialized) {
|
|
501
|
+
throw new ValidationError("Client not initialized. Call init() first.");
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
/**
|
|
505
|
+
* Create a new collection
|
|
506
|
+
*
|
|
507
|
+
* @param name - Collection name
|
|
508
|
+
* @param config - Collection configuration
|
|
509
|
+
*/
|
|
510
|
+
async createCollection(name, config) {
|
|
511
|
+
this.ensureInitialized();
|
|
512
|
+
if (!name || typeof name !== "string") {
|
|
513
|
+
throw new ValidationError("Collection name must be a non-empty string");
|
|
514
|
+
}
|
|
515
|
+
if (!config.dimension || config.dimension <= 0) {
|
|
516
|
+
throw new ValidationError("Dimension must be a positive integer");
|
|
517
|
+
}
|
|
518
|
+
await this.backend.createCollection(name, config);
|
|
519
|
+
}
|
|
520
|
+
/**
|
|
521
|
+
* Delete a collection
|
|
522
|
+
*
|
|
523
|
+
* @param name - Collection name
|
|
524
|
+
*/
|
|
525
|
+
async deleteCollection(name) {
|
|
526
|
+
this.ensureInitialized();
|
|
527
|
+
await this.backend.deleteCollection(name);
|
|
528
|
+
}
|
|
529
|
+
/**
|
|
530
|
+
* Get collection information
|
|
531
|
+
*
|
|
532
|
+
* @param name - Collection name
|
|
533
|
+
* @returns Collection info or null if not found
|
|
534
|
+
*/
|
|
535
|
+
async getCollection(name) {
|
|
536
|
+
this.ensureInitialized();
|
|
537
|
+
return this.backend.getCollection(name);
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* List all collections
|
|
541
|
+
*
|
|
542
|
+
* @returns Array of collections
|
|
543
|
+
*/
|
|
544
|
+
async listCollections() {
|
|
545
|
+
this.ensureInitialized();
|
|
546
|
+
return this.backend.listCollections();
|
|
547
|
+
}
|
|
548
|
+
/**
|
|
549
|
+
* Insert a vector document
|
|
550
|
+
*
|
|
551
|
+
* @param collection - Collection name
|
|
552
|
+
* @param doc - Document to insert
|
|
553
|
+
*/
|
|
554
|
+
async insert(collection, doc) {
|
|
555
|
+
this.ensureInitialized();
|
|
556
|
+
this.validateDocument(doc);
|
|
557
|
+
await this.backend.insert(collection, doc);
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Insert multiple vector documents
|
|
561
|
+
*
|
|
562
|
+
* @param collection - Collection name
|
|
563
|
+
* @param docs - Documents to insert
|
|
564
|
+
*/
|
|
565
|
+
async insertBatch(collection, docs) {
|
|
566
|
+
this.ensureInitialized();
|
|
567
|
+
if (!Array.isArray(docs)) {
|
|
568
|
+
throw new ValidationError("Documents must be an array");
|
|
569
|
+
}
|
|
570
|
+
for (const doc of docs) {
|
|
571
|
+
this.validateDocument(doc);
|
|
572
|
+
}
|
|
573
|
+
await this.backend.insertBatch(collection, docs);
|
|
574
|
+
}
|
|
575
|
+
validateDocument(doc) {
|
|
576
|
+
if (doc.id === void 0 || doc.id === null) {
|
|
577
|
+
throw new ValidationError("Document ID is required");
|
|
578
|
+
}
|
|
579
|
+
if (!doc.vector) {
|
|
580
|
+
throw new ValidationError("Document vector is required");
|
|
581
|
+
}
|
|
582
|
+
if (!Array.isArray(doc.vector) && !(doc.vector instanceof Float32Array)) {
|
|
583
|
+
throw new ValidationError("Vector must be an array or Float32Array");
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
/**
|
|
587
|
+
* Search for similar vectors
|
|
588
|
+
*
|
|
589
|
+
* @param collection - Collection name
|
|
590
|
+
* @param query - Query vector
|
|
591
|
+
* @param options - Search options
|
|
592
|
+
* @returns Search results sorted by relevance
|
|
593
|
+
*/
|
|
594
|
+
async search(collection, query, options) {
|
|
595
|
+
this.ensureInitialized();
|
|
596
|
+
if (!query || !Array.isArray(query) && !(query instanceof Float32Array)) {
|
|
597
|
+
throw new ValidationError("Query must be an array or Float32Array");
|
|
598
|
+
}
|
|
599
|
+
return this.backend.search(collection, query, options);
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Delete a vector by ID
|
|
603
|
+
*
|
|
604
|
+
* @param collection - Collection name
|
|
605
|
+
* @param id - Document ID
|
|
606
|
+
* @returns true if deleted, false if not found
|
|
607
|
+
*/
|
|
608
|
+
async delete(collection, id) {
|
|
609
|
+
this.ensureInitialized();
|
|
610
|
+
return this.backend.delete(collection, id);
|
|
611
|
+
}
|
|
612
|
+
/**
|
|
613
|
+
* Get a vector by ID
|
|
614
|
+
*
|
|
615
|
+
* @param collection - Collection name
|
|
616
|
+
* @param id - Document ID
|
|
617
|
+
* @returns Document or null if not found
|
|
618
|
+
*/
|
|
619
|
+
async get(collection, id) {
|
|
620
|
+
this.ensureInitialized();
|
|
621
|
+
return this.backend.get(collection, id);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Close the client and release resources
|
|
625
|
+
*/
|
|
626
|
+
async close() {
|
|
627
|
+
if (this.initialized) {
|
|
628
|
+
await this.backend.close();
|
|
629
|
+
this.initialized = false;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Get the current backend type
|
|
634
|
+
*/
|
|
635
|
+
get backendType() {
|
|
636
|
+
return this.config.backend;
|
|
637
|
+
}
|
|
638
|
+
};
|
|
639
|
+
export {
|
|
640
|
+
ConnectionError,
|
|
641
|
+
NotFoundError,
|
|
642
|
+
RestBackend,
|
|
643
|
+
ValidationError,
|
|
644
|
+
VelesDB,
|
|
645
|
+
VelesDBError,
|
|
646
|
+
WasmBackend
|
|
647
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wiscale/velesdb-sdk",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Official TypeScript SDK for VelesDB - Vector Search in Microseconds",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.mjs",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
21
|
+
"dev": "tsup src/index.ts --format cjs,esm --dts --watch",
|
|
22
|
+
"test": "vitest run",
|
|
23
|
+
"test:watch": "vitest",
|
|
24
|
+
"test:coverage": "vitest run --coverage",
|
|
25
|
+
"lint": "eslint src --ext .ts",
|
|
26
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
27
|
+
"format": "prettier --write src",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"velesdb",
|
|
33
|
+
"vector",
|
|
34
|
+
"database",
|
|
35
|
+
"similarity-search",
|
|
36
|
+
"embeddings",
|
|
37
|
+
"ai",
|
|
38
|
+
"machine-learning",
|
|
39
|
+
"wasm",
|
|
40
|
+
"typescript"
|
|
41
|
+
],
|
|
42
|
+
"author": "Wiscale France",
|
|
43
|
+
"license": "Elastic-2.0",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "https://github.com/cyberlife-coder/VelesDB.git",
|
|
47
|
+
"directory": "sdks/typescript"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/cyberlife-coder/VelesDB#readme",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/cyberlife-coder/VelesDB/issues"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@types/node": "^20.10.0",
|
|
55
|
+
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
56
|
+
"@typescript-eslint/parser": "^6.0.0",
|
|
57
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
58
|
+
"eslint": "^8.0.0",
|
|
59
|
+
"eslint-config-prettier": "^9.0.0",
|
|
60
|
+
"prettier": "^3.0.0",
|
|
61
|
+
"tsup": "^8.0.0",
|
|
62
|
+
"typescript": "^5.3.0",
|
|
63
|
+
"vitest": "^4.0.16"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"velesdb-wasm": "^0.5.0"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18.0.0"
|
|
70
|
+
}
|
|
71
|
+
}
|