@wiscale/velesdb-sdk 1.18.0 → 2.0.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
@@ -60,7 +60,7 @@ var WASM_CAPABILITIES = Object.freeze({
60
60
  agentMemory: false,
61
61
  streamInsert: false,
62
62
  pqTraining: false,
63
- velesqlQuery: true,
63
+ velesqlQuery: false,
64
64
  collectionIntrospection: false
65
65
  });
66
66
 
@@ -298,20 +298,47 @@ async function wasmMultiQuerySearch(ctx, collectionName, vectors, options) {
298
298
  );
299
299
  return raw.map((r) => mapWasmResult(ctx, collection, r));
300
300
  }
301
- async function wasmQuery(ctx, collectionName, _queryString, params, _options) {
301
+ var PURE_NEAR_QUERY = /^\s*select\s+\*\s+from\s+([a-z_]\w*)\s+where\s+vector\s+near\s+\$([a-z_]\w*)\s*(?:limit\s+(\d+))?\s*;?\s*$/i;
302
+ function parsePureNearQuery(queryString) {
303
+ const match = PURE_NEAR_QUERY.exec(queryString);
304
+ if (!match) {
305
+ throw new VelesDBError(
306
+ `The WASM backend only executes pure top-k NEAR queries of the form "SELECT * FROM <collection> WHERE vector NEAR $param [LIMIT n]". WHERE predicates, JOIN, GROUP BY, MATCH, set operations and FUSION are not evaluated in WASM \u2014 use the REST backend (velesdb-server) for full VelesQL. Received: ${queryString}`,
307
+ "NOT_SUPPORTED"
308
+ );
309
+ }
310
+ const parsed = { from: match[1], param: match[2] };
311
+ if (match[3] !== void 0) {
312
+ parsed.limit = Number(match[3]);
313
+ }
314
+ return parsed;
315
+ }
316
+ function resolveQueryK(limit, requestedK) {
317
+ if (limit !== void 0) {
318
+ return limit;
319
+ }
320
+ return typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
321
+ }
322
+ async function wasmQuery(ctx, collectionName, queryString, params, _options) {
302
323
  const collection = ctx.getCollection(collectionName);
303
324
  if (!collection) {
304
325
  throw new NotFoundError(`Collection '${collectionName}'`);
305
326
  }
306
- const paramsVector = params?.q;
327
+ const parsed = parsePureNearQuery(queryString);
328
+ if (parsed.from !== collectionName) {
329
+ throw new VelesDBError(
330
+ `Query targets collection '${parsed.from}' but was executed against '${collectionName}'.`,
331
+ "BAD_REQUEST"
332
+ );
333
+ }
334
+ const paramsVector = params?.[parsed.param];
307
335
  if (!Array.isArray(paramsVector) && !(paramsVector instanceof Float32Array)) {
308
336
  throw new VelesDBError(
309
- "WASM query() expects params.q to contain the query embedding vector.",
337
+ `WASM query() expects params.${parsed.param} to contain the query embedding vector.`,
310
338
  "BAD_REQUEST"
311
339
  );
312
340
  }
313
- const requestedK = params?.k;
314
- const k = typeof requestedK === "number" && Number.isInteger(requestedK) && requestedK > 0 ? requestedK : 10;
341
+ const k = resolveQueryK(parsed.limit, params?.k);
315
342
  const raw = collection.store.query(
316
343
  paramsVector instanceof Float32Array ? paramsVector : new Float32Array(paramsVector),
317
344
  k
@@ -811,6 +838,12 @@ async function wasmRecordEpisodicEvent(_collection, _event) {
811
838
  async function wasmRecallEpisodicEvents(_collection, _embedding, _k) {
812
839
  wasmNotSupported("Agent memory");
813
840
  }
841
+ async function wasmRecallRecentEvents(_collection, _since) {
842
+ wasmNotSupported("Agent memory");
843
+ }
844
+ async function wasmRecallOlderThanEvents(_collection, _before) {
845
+ wasmNotSupported("Agent memory");
846
+ }
814
847
  async function wasmStoreProceduralPattern(_collection, _pattern) {
815
848
  wasmNotSupported("Agent memory");
816
849
  }
@@ -858,6 +891,18 @@ function wasmUpsertNodePayload(_c, _id, _p) {
858
891
  function wasmGraphSearch(_c, _r) {
859
892
  return Promise.resolve(wasmNotSupported("Graph search"));
860
893
  }
894
+ function wasmRelate(_c, _req) {
895
+ return Promise.resolve(wasmNotSupported("Relation edges"));
896
+ }
897
+ function wasmUnrelate(_c, _id) {
898
+ return Promise.resolve(wasmNotSupported("Relation edge removal"));
899
+ }
900
+ function wasmGetRelations(_c, _id) {
901
+ return Promise.resolve(wasmNotSupported("Relation edges"));
902
+ }
903
+ function wasmSetTtlDurable(_c, _id, _ttl) {
904
+ return Promise.resolve(wasmNotSupported("Durable TTL"));
905
+ }
861
906
 
862
907
  // src/backends/wasm.ts
863
908
  var WasmBackend = class {
@@ -1212,6 +1257,14 @@ var WasmBackend = class {
1212
1257
  this.ensureInitialized();
1213
1258
  return wasmRecallEpisodicEvents(c, e, k);
1214
1259
  }
1260
+ async recallRecentEvents(c, since) {
1261
+ this.ensureInitialized();
1262
+ return wasmRecallRecentEvents(c, since);
1263
+ }
1264
+ async recallOlderThanEvents(c, before) {
1265
+ this.ensureInitialized();
1266
+ return wasmRecallOlderThanEvents(c, before);
1267
+ }
1215
1268
  async storeProceduralPattern(c, p) {
1216
1269
  this.ensureInitialized();
1217
1270
  return wasmStoreProceduralPattern(c, p);
@@ -1273,16 +1326,43 @@ var WasmBackend = class {
1273
1326
  this.ensureInitialized();
1274
1327
  return wasmSparseSearchNamed(c, q, idx, o);
1275
1328
  }
1329
+ async relate(c, req) {
1330
+ this.ensureInitialized();
1331
+ return wasmRelate(c, req);
1332
+ }
1333
+ async unrelate(c, edgeId) {
1334
+ this.ensureInitialized();
1335
+ return wasmUnrelate(c, edgeId);
1336
+ }
1337
+ async getRelations(c, pointId) {
1338
+ this.ensureInitialized();
1339
+ return wasmGetRelations(c, pointId);
1340
+ }
1341
+ async setTtlDurable(c, pointId, ttlSeconds) {
1342
+ this.ensureInitialized();
1343
+ return wasmSetTtlDurable(c, pointId, ttlSeconds);
1344
+ }
1276
1345
  };
1277
1346
 
1278
1347
  // src/backends/crud-backend.ts
1348
+ var U64_MAX = 18446744073709551615n;
1349
+ function coerceDecimalStringId(id) {
1350
+ if (!/^\d+$/.test(id)) return NaN;
1351
+ const big = BigInt(id);
1352
+ if (big > U64_MAX) return NaN;
1353
+ return big > BigInt(Number.MAX_SAFE_INTEGER) ? id : Number(id);
1354
+ }
1279
1355
  function parseRestPointId(id) {
1280
- if (typeof id !== "number" || !Number.isFinite(id) || id < 0 || !Number.isInteger(id) || id > Number.MAX_SAFE_INTEGER) {
1356
+ const coerced = typeof id === "string" ? coerceDecimalStringId(id) : id;
1357
+ if (typeof coerced === "string") {
1358
+ return coerced;
1359
+ }
1360
+ if (!Number.isFinite(coerced) || coerced < 0 || !Number.isInteger(coerced) || coerced > Number.MAX_SAFE_INTEGER) {
1281
1361
  throw new ValidationError(
1282
- `REST backend requires numeric u64-compatible IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER}). Received: ${String(id)}`
1362
+ `REST backend requires numeric u64-compatible IDs: a non-negative integer in the JS safe integer range (0..${Number.MAX_SAFE_INTEGER}) or a decimal string up to u64::MAX (${U64_MAX}). Received: ${String(id)}`
1283
1363
  );
1284
1364
  }
1285
- return id;
1365
+ return coerced;
1286
1366
  }
1287
1367
  function sparseVectorToRestFormat(sv) {
1288
1368
  const result = {};
@@ -1737,6 +1817,31 @@ async function graphSearch(transport, collection, request2) {
1737
1817
  return { results: items };
1738
1818
  }
1739
1819
 
1820
+ // src/backends/scroll-backend.ts
1821
+ async function scroll(transport, collection, request2) {
1822
+ const body = {};
1823
+ if (request2?.cursor !== void 0) {
1824
+ body.cursor = request2.cursor;
1825
+ }
1826
+ if (request2?.batchSize !== void 0) {
1827
+ body.batch_size = request2.batchSize;
1828
+ }
1829
+ if (request2?.filter !== void 0) {
1830
+ body.filter = request2.filter;
1831
+ }
1832
+ const response = await transport.requestJson(
1833
+ "POST",
1834
+ `${collectionPath(collection)}/points/scroll`,
1835
+ body
1836
+ );
1837
+ throwOnError(response, `Collection '${collection}'`);
1838
+ const data = response.data;
1839
+ return {
1840
+ points: data.points,
1841
+ nextCursor: data.next_cursor
1842
+ };
1843
+ }
1844
+
1740
1845
  // src/backends/agent-memory-backend.ts
1741
1846
  var _idCounter = 0;
1742
1847
  var _lastTimestamp = 0;
@@ -1754,20 +1859,28 @@ function generateUniqueId() {
1754
1859
  }
1755
1860
  return _lastTimestamp * 1e3 + _idCounter;
1756
1861
  }
1862
+ function memoryIdToString(id) {
1863
+ return String(id);
1864
+ }
1865
+ function nowUnixSeconds() {
1866
+ return Math.floor(Date.now() / 1e3);
1867
+ }
1757
1868
  async function storeSemanticFact(transport, collection, entry) {
1758
1869
  const response = await transport.requestJson(
1759
1870
  "POST",
1760
1871
  `${collectionPath(collection)}/points`,
1761
1872
  {
1762
1873
  points: [{
1763
- id: entry.id,
1874
+ id: parseRestPointId(entry.id),
1764
1875
  vector: entry.embedding,
1765
1876
  payload: {
1766
1877
  // Caller metadata is spread first so the reserved keys below
1767
- // (`_memory_type`, `text`) always win and cannot be clobbered.
1878
+ // (`_memory_type`, `content`) always win and cannot be clobbered.
1768
1879
  ...entry.metadata,
1769
1880
  _memory_type: "semantic",
1770
- text: entry.text
1881
+ // `content` matches the core semantic store and the server/Python
1882
+ // payload field (BREAKING: was `text` before this change).
1883
+ content: entry.text
1771
1884
  }
1772
1885
  }]
1773
1886
  }
@@ -1778,7 +1891,8 @@ async function searchSemanticMemory(transport, collection, embedding, k = 5) {
1778
1891
  return transport.searchVectors(collection, embedding, k, { _memory_type: "semantic" });
1779
1892
  }
1780
1893
  async function recordEpisodicEvent(transport, collection, event) {
1781
- const id = generateUniqueId();
1894
+ const id = event.id !== void 0 ? parseRestPointId(event.id) : generateUniqueId();
1895
+ const timestamp = event.timestamp ?? nowUnixSeconds();
1782
1896
  const response = await transport.requestJson(
1783
1897
  "POST",
1784
1898
  `${collectionPath(collection)}/points`,
@@ -1794,19 +1908,21 @@ async function recordEpisodicEvent(transport, collection, event) {
1794
1908
  ...event.metadata,
1795
1909
  _memory_type: "episodic",
1796
1910
  event_type: event.eventType,
1797
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1911
+ // NUMERIC unix-seconds, mirroring the core episodic store so
1912
+ // recallRecent/recallOlderThan can range-filter on it.
1913
+ timestamp
1798
1914
  }
1799
1915
  }]
1800
1916
  }
1801
1917
  );
1802
1918
  throwOnError(response);
1803
- return id;
1919
+ return memoryIdToString(id);
1804
1920
  }
1805
1921
  async function recallEpisodicEvents(transport, collection, embedding, k = 5) {
1806
1922
  return transport.searchVectors(collection, embedding, k, { _memory_type: "episodic" });
1807
1923
  }
1808
1924
  async function storeProceduralPattern(transport, collection, pattern) {
1809
- const id = generateUniqueId();
1925
+ const id = pattern.id !== void 0 ? parseRestPointId(pattern.id) : generateUniqueId();
1810
1926
  const response = await transport.requestJson(
1811
1927
  "POST",
1812
1928
  `${collectionPath(collection)}/points`,
@@ -1827,11 +1943,41 @@ async function storeProceduralPattern(transport, collection, pattern) {
1827
1943
  }
1828
1944
  );
1829
1945
  throwOnError(response);
1830
- return id;
1946
+ return memoryIdToString(id);
1831
1947
  }
1832
1948
  async function matchProceduralPatterns(transport, collection, embedding, k = 5) {
1833
1949
  return transport.searchVectors(collection, embedding, k, { _memory_type: "procedural" });
1834
1950
  }
1951
+ function toEpisodicRecord(point) {
1952
+ const payload = point.payload ?? {};
1953
+ if (payload._memory_type !== "episodic") return void 0;
1954
+ if (typeof payload.timestamp !== "number") return void 0;
1955
+ return { id: String(point.id), timestamp: payload.timestamp, payload };
1956
+ }
1957
+ async function scrollEpisodicRecords(transport, collection) {
1958
+ const records = [];
1959
+ let cursor = null;
1960
+ do {
1961
+ const page = await scroll(transport, collection, {
1962
+ cursor: cursor ?? void 0,
1963
+ filter: { _memory_type: "episodic" }
1964
+ });
1965
+ for (const point of page.points) {
1966
+ const record = toEpisodicRecord(point);
1967
+ if (record !== void 0) records.push(record);
1968
+ }
1969
+ cursor = page.nextCursor;
1970
+ } while (cursor !== null && cursor !== void 0);
1971
+ return records;
1972
+ }
1973
+ async function recallRecentEvents(transport, collection, since) {
1974
+ const records = await scrollEpisodicRecords(transport, collection);
1975
+ return records.filter((r) => since === void 0 || r.timestamp >= since).sort((a, b) => b.timestamp - a.timestamp);
1976
+ }
1977
+ async function recallOlderThanEvents(transport, collection, before) {
1978
+ const records = await scrollEpisodicRecords(transport, collection);
1979
+ return records.filter((r) => r.timestamp < before).sort((a, b) => b.timestamp - a.timestamp);
1980
+ }
1835
1981
 
1836
1982
  // src/search-quality.ts
1837
1983
  function searchQualityToMode(quality) {
@@ -2064,6 +2210,64 @@ async function traverseParallel(transport, collection, request2) {
2064
2210
  throwOnError(response, `Collection '${collection}'`);
2065
2211
  return toTraverseResponse(response.data);
2066
2212
  }
2213
+ async function relate(transport, collection, req) {
2214
+ const response = await transport.requestJson(
2215
+ "POST",
2216
+ `${collectionPath(collection)}/relations`,
2217
+ {
2218
+ source: req.source,
2219
+ target: req.target,
2220
+ rel_type: req.relType,
2221
+ properties: req.properties ?? {}
2222
+ }
2223
+ );
2224
+ throwOnError(response, `Collection '${collection}'`);
2225
+ return { edgeId: response.data.edge_id };
2226
+ }
2227
+ async function unrelate(transport, collection, edgeId) {
2228
+ const response = await transport.requestJson(
2229
+ "DELETE",
2230
+ `${collectionPath(collection)}/relations/${encodeURIComponent(String(edgeId))}`
2231
+ );
2232
+ if (response.error !== void 0) {
2233
+ const { code, message } = response.error;
2234
+ const err = parseVelesError(code, message);
2235
+ if (err instanceof EdgeNotFoundError) {
2236
+ return false;
2237
+ }
2238
+ if (code === "NOT_FOUND") {
2239
+ return false;
2240
+ }
2241
+ throwOnError(response, `Collection '${collection}'`);
2242
+ }
2243
+ return true;
2244
+ }
2245
+ async function getRelations(transport, collection, pointId) {
2246
+ const response = await transport.requestJson(
2247
+ "GET",
2248
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/relations`
2249
+ );
2250
+ throwOnError(response, `Collection '${collection}'`);
2251
+ const raw = response.data;
2252
+ return {
2253
+ edges: raw.edges.map((e) => ({
2254
+ id: e.id,
2255
+ source: e.source,
2256
+ target: e.target,
2257
+ relType: e.rel_type,
2258
+ properties: e.properties
2259
+ })),
2260
+ count: raw.count
2261
+ };
2262
+ }
2263
+ async function setTtlDurable(transport, collection, pointId, ttlSeconds) {
2264
+ const response = await transport.requestJson(
2265
+ "PATCH",
2266
+ `${collectionPath(collection)}/points/${encodeURIComponent(String(pointId))}/ttl`,
2267
+ { ttl_seconds: ttlSeconds }
2268
+ );
2269
+ throwOnError(response, `Collection '${collection}'`);
2270
+ }
2067
2271
 
2068
2272
  // src/backends/query-backend.ts
2069
2273
  function isLikelyAggregationQuery(queryString) {
@@ -2196,31 +2400,6 @@ async function collectionSanity(transport, collection) {
2196
2400
  };
2197
2401
  }
2198
2402
 
2199
- // src/backends/scroll-backend.ts
2200
- async function scroll(transport, collection, request2) {
2201
- const body = {};
2202
- if (request2?.cursor !== void 0) {
2203
- body.cursor = request2.cursor;
2204
- }
2205
- if (request2?.batchSize !== void 0) {
2206
- body.batch_size = request2.batchSize;
2207
- }
2208
- if (request2?.filter !== void 0) {
2209
- body.filter = request2.filter;
2210
- }
2211
- const response = await transport.requestJson(
2212
- "POST",
2213
- `${collectionPath(collection)}/points/scroll`,
2214
- body
2215
- );
2216
- throwOnError(response, `Collection '${collection}'`);
2217
- const data = response.data;
2218
- return {
2219
- points: data.points,
2220
- nextCursor: data.next_cursor
2221
- };
2222
- }
2223
-
2224
2403
  // src/backends/admin-backend.ts
2225
2404
  function mapStatsResponse(data) {
2226
2405
  let columnStats;
@@ -2406,9 +2585,17 @@ async function streamInsert(transport, collection, docs) {
2406
2585
  }
2407
2586
  }
2408
2587
  }
2588
+ function requireSafeRangeId(restId) {
2589
+ if (typeof restId === "string") {
2590
+ throw new ValidationError(
2591
+ `streamUpsertPoints requires ids in the JS safe integer range (0..${Number.MAX_SAFE_INTEGER}); use upsert/upsertBatch for string ids above it. Received: ${restId}`
2592
+ );
2593
+ }
2594
+ return restId;
2595
+ }
2409
2596
  async function streamUpsertPoints(transport, collection, docs) {
2410
2597
  const ndjsonLines = docs.map((doc) => {
2411
- const restId = transport.parseRestPointId(doc.id);
2598
+ const restId = requireSafeRangeId(transport.parseRestPointId(doc.id));
2412
2599
  const vector = toNumberArray(doc.vector);
2413
2600
  const point = {
2414
2601
  id: restId,
@@ -2599,6 +2786,22 @@ var RestBackend = class {
2599
2786
  this.ensureInitialized();
2600
2787
  return graphSearch(buildBaseTransport(this.httpConfig), c, r);
2601
2788
  }
2789
+ async relate(c, req) {
2790
+ this.ensureInitialized();
2791
+ return relate(buildCrudTransport(this.httpConfig), c, req);
2792
+ }
2793
+ async unrelate(c, edgeId) {
2794
+ this.ensureInitialized();
2795
+ return unrelate(buildCrudTransport(this.httpConfig), c, edgeId);
2796
+ }
2797
+ async getRelations(c, pointId) {
2798
+ this.ensureInitialized();
2799
+ return getRelations(buildCrudTransport(this.httpConfig), c, pointId);
2800
+ }
2801
+ async setTtlDurable(c, pointId, ttlSeconds) {
2802
+ this.ensureInitialized();
2803
+ return setTtlDurable(buildCrudTransport(this.httpConfig), c, pointId, ttlSeconds);
2804
+ }
2602
2805
  // Search
2603
2806
  async search(c, q, o) {
2604
2807
  this.ensureInitialized();
@@ -2731,6 +2934,14 @@ var RestBackend = class {
2731
2934
  this.ensureInitialized();
2732
2935
  return recallEpisodicEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, e, k);
2733
2936
  }
2937
+ async recallRecentEvents(c, since) {
2938
+ this.ensureInitialized();
2939
+ return recallRecentEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, since);
2940
+ }
2941
+ async recallOlderThanEvents(c, before) {
2942
+ this.ensureInitialized();
2943
+ return recallOlderThanEvents(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, before);
2944
+ }
2734
2945
  async storeProceduralPattern(c, p) {
2735
2946
  this.ensureInitialized();
2736
2947
  return storeProceduralPattern(buildAgentMemoryTransport(this.httpConfig, (col, emb, opts) => this.search(col, emb, opts)), c, p);
@@ -2768,15 +2979,29 @@ var AgentMemoryClient = class {
2768
2979
  async searchFacts(collection, embedding, k = 5) {
2769
2980
  return this.backend.searchSemanticMemory(collection, embedding, k);
2770
2981
  }
2771
- /** Record an episodic event. Returns the generated point ID. */
2982
+ /** Record an episodic event. Returns the point ID (string, u64-safe). */
2772
2983
  async recordEvent(collection, event) {
2773
2984
  return this.backend.recordEpisodicEvent(collection, event);
2774
2985
  }
2775
- /** Recall episodic events */
2986
+ /** Recall episodic events by vector similarity. */
2776
2987
  async recallEvents(collection, embedding, k = 5) {
2777
2988
  return this.backend.recallEpisodicEvents(collection, embedding, k);
2778
2989
  }
2779
- /** Store a procedural pattern. Returns the generated point ID. */
2990
+ /**
2991
+ * Recall episodic events most-recent-first, optionally bounded below by
2992
+ * `since` (inclusive unix-seconds). Mirrors core `episodic.recent(since)`.
2993
+ */
2994
+ async recallRecent(collection, since) {
2995
+ return this.backend.recallRecentEvents(collection, since);
2996
+ }
2997
+ /**
2998
+ * Recall episodic events strictly older than `before` (unix-seconds),
2999
+ * most-recent-first. Mirrors core `episodic.older_than(before)`.
3000
+ */
3001
+ async recallOlderThan(collection, before) {
3002
+ return this.backend.recallOlderThanEvents(collection, before);
3003
+ }
3004
+ /** Store a procedural pattern. Returns the point ID (string, u64-safe). */
2780
3005
  async learnProcedure(collection, pattern) {
2781
3006
  return this.backend.storeProceduralPattern(collection, pattern);
2782
3007
  }
@@ -2784,7 +3009,12 @@ var AgentMemoryClient = class {
2784
3009
  async recallProcedures(collection, embedding, k = 5) {
2785
3010
  return this.backend.matchProceduralPatterns(collection, embedding, k);
2786
3011
  }
2787
- /** Delete a memory entry (fact, event, or procedure) by its point ID. */
3012
+ /**
3013
+ * Delete a memory entry (fact, event, or procedure) by its point ID.
3014
+ *
3015
+ * Accepts the `string` ids returned by `recordEvent` / `learnProcedure`
3016
+ * (u64-safe decimal strings) as well as numeric ids.
3017
+ */
2788
3018
  async deleteMemory(collection, id) {
2789
3019
  return this.backend.delete(collection, id);
2790
3020
  }
@@ -2818,11 +3048,10 @@ function validateDocument(doc, config) {
2818
3048
  validateRestPointId(doc.id, config);
2819
3049
  }
2820
3050
  function validateRestPointId(id, config) {
2821
- if (config.backend === "rest" && (typeof id !== "number" || !Number.isInteger(id) || id < 0 || id > Number.MAX_SAFE_INTEGER)) {
2822
- throw new ValidationError(
2823
- `REST backend requires numeric u64-compatible document IDs in JS safe integer range (0..${Number.MAX_SAFE_INTEGER})`
2824
- );
3051
+ if (config.backend !== "rest") {
3052
+ return;
2825
3053
  }
3054
+ parseRestPointId(id);
2826
3055
  }
2827
3056
 
2828
3057
  // src/client/search-methods.ts
@@ -3003,6 +3232,40 @@ function graphSearch2(backend, collection, request2) {
3003
3232
  requireNonEmptyString(collection, "Collection");
3004
3233
  return backend.graphSearch(collection, request2);
3005
3234
  }
3235
+ function relate2(backend, collection, req) {
3236
+ requireNonEmptyString(collection, "Collection");
3237
+ if (!req.relType || typeof req.relType !== "string") {
3238
+ throw new ValidationError("Relation type is required and must be a string");
3239
+ }
3240
+ if (!isGraphNodeId(req.source) || !isGraphNodeId(req.target)) {
3241
+ throw new ValidationError("Source and target must be numbers or strings");
3242
+ }
3243
+ return backend.relate(collection, req);
3244
+ }
3245
+ function unrelate2(backend, collection, edgeId) {
3246
+ requireNonEmptyString(collection, "Collection");
3247
+ if (!isGraphNodeId(edgeId)) {
3248
+ throw new ValidationError("Edge ID must be a number or string");
3249
+ }
3250
+ return backend.unrelate(collection, edgeId);
3251
+ }
3252
+ function getRelations2(backend, collection, pointId) {
3253
+ requireNonEmptyString(collection, "Collection");
3254
+ if (!isGraphNodeId(pointId)) {
3255
+ throw new ValidationError("Point ID must be a number or string");
3256
+ }
3257
+ return backend.getRelations(collection, pointId);
3258
+ }
3259
+ function setTtlDurable2(backend, collection, pointId, ttlSeconds) {
3260
+ requireNonEmptyString(collection, "Collection");
3261
+ if (!isGraphNodeId(pointId)) {
3262
+ throw new ValidationError("Point ID must be a number or string");
3263
+ }
3264
+ if (typeof ttlSeconds !== "number" || ttlSeconds < 0) {
3265
+ throw new ValidationError("ttlSeconds must be a non-negative number");
3266
+ }
3267
+ return backend.setTtlDurable(collection, pointId, ttlSeconds);
3268
+ }
3006
3269
 
3007
3270
  // src/client.ts
3008
3271
  var VelesDB = class {
@@ -3300,6 +3563,22 @@ var VelesDB = class {
3300
3563
  this.ensureInitialized();
3301
3564
  return graphSearch2(this.backend, collection, request2);
3302
3565
  }
3566
+ async relate(collection, req) {
3567
+ this.ensureInitialized();
3568
+ return relate2(this.backend, collection, req);
3569
+ }
3570
+ async unrelate(collection, edgeId) {
3571
+ this.ensureInitialized();
3572
+ return unrelate2(this.backend, collection, edgeId);
3573
+ }
3574
+ async getRelations(collection, pointId) {
3575
+ this.ensureInitialized();
3576
+ return getRelations2(this.backend, collection, pointId);
3577
+ }
3578
+ async setTtlDurable(collection, pointId, ttlSeconds) {
3579
+ this.ensureInitialized();
3580
+ return setTtlDurable2(this.backend, collection, pointId, ttlSeconds);
3581
+ }
3303
3582
  // ========================================================================
3304
3583
  // Capabilities & Backend Info
3305
3584
  // ========================================================================
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wiscale/velesdb-sdk",
3
- "version": "1.18.0",
3
+ "version": "2.0.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",
@@ -26,7 +26,7 @@
26
26
  "lint": "eslint src",
27
27
  "lint:fix": "eslint src --fix",
28
28
  "format": "prettier --write src",
29
- "typecheck": "tsc --noEmit",
29
+ "typecheck": "tsc --noEmit && tsc -p tsconfig.examples.json",
30
30
  "prepublishOnly": "npm run build"
31
31
  },
32
32
  "keywords": [