@an5/adapters 0.2.5 → 0.2.9

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.
Files changed (99) hide show
  1. package/README.md +283 -305
  2. package/dist/an5Adapter.d.ts +142 -7
  3. package/dist/an5Adapter.js +998 -68
  4. package/dist/base/metadata.d.ts +10 -0
  5. package/dist/base/metadata.js +107 -0
  6. package/dist/base/sql.d.ts +19 -1
  7. package/dist/base/sql.js +152 -24
  8. package/dist/base/types.d.ts +10 -1
  9. package/dist/browser.d.ts +4 -1
  10. package/dist/browser.js +9 -2
  11. package/dist/googlesheets/adapter.d.ts +1 -0
  12. package/dist/googlesheets/adapter.js +9 -2
  13. package/dist/googlesheets/config.d.ts +4 -4
  14. package/dist/googlesheets/helpers.js +2 -0
  15. package/dist/googlesheets/parseConnectionString.js +5 -2
  16. package/dist/googlesheets/sqlExecutor.js +29 -24
  17. package/dist/googlesheets/tableClient.js +14 -5
  18. package/dist/index.d.ts +5 -1
  19. package/dist/index.js +15 -1
  20. package/dist/mssql/engine.js +8 -4
  21. package/dist/mysql/engine.js +1 -1
  22. package/dist/nbase/client.d.ts +92 -0
  23. package/dist/nbase/client.js +147 -0
  24. package/dist/nbase/index.d.ts +47 -0
  25. package/dist/nbase/index.js +80 -0
  26. package/dist/postgres/engine.js +1 -1
  27. package/dist/sqlite/browserEngine.d.ts +27 -0
  28. package/dist/sqlite/browserEngine.js +161 -0
  29. package/dist/sqlite/engine.js +15 -3
  30. package/dist/sqlite/index.d.ts +1 -0
  31. package/dist/sqlite/index.js +1 -0
  32. package/dist/typed.d.ts +5 -0
  33. package/dist/typed.js +2 -0
  34. package/dotnet/An5Adapter.cs +1112 -275
  35. package/dotnet/Base/Core.cs +380 -82
  36. package/dotnet/Mssql/MssqlEngine.cs +145 -144
  37. package/dotnet/Postgres/PostgresEngine.cs +149 -121
  38. package/dotnet/Sqlite/SqliteEngine.cs +214 -0
  39. package/golang/an5_adapter.go +314 -0
  40. package/golang/base/base.go +100 -0
  41. package/golang/base/metadata.go +116 -0
  42. package/golang/base/where.go +311 -0
  43. package/golang/go.mod +3 -0
  44. package/golang/mssql/mssql.go +32 -0
  45. package/golang/postgres/postgres.go +37 -0
  46. package/golang/sqlite/sqlite.go +14 -0
  47. package/golang/sqlite_test.go +42 -0
  48. package/golang/table_client.go +1329 -0
  49. package/package.json +26 -9
  50. package/python/an5_adapter.py +184 -97
  51. package/python/base/__init__.py +3 -3
  52. package/python/base/dialects.py +11 -8
  53. package/python/base/metadata.py +59 -10
  54. package/python/base/sql.py +202 -121
  55. package/python/mssql/__init__.py +1 -1
  56. package/python/mssql/provider.py +62 -62
  57. package/python/postgres/__init__.py +1 -1
  58. package/python/postgres/provider.py +15 -15
  59. package/python/sqlite/__init__.py +3 -0
  60. package/python/sqlite/provider.py +83 -0
  61. package/python/table_client.py +636 -216
  62. package/rust/Cargo.toml +31 -0
  63. package/rust/README.md +71 -0
  64. package/rust/src/adapter.rs +1312 -0
  65. package/rust/src/base/dialect.rs +172 -0
  66. package/rust/src/base/metadata.rs +134 -0
  67. package/rust/src/base/mod.rs +17 -0
  68. package/rust/src/base/where_builder.rs +405 -0
  69. package/rust/src/lib.rs +75 -0
  70. package/typescript/src/an5Adapter.ts +1515 -505
  71. package/typescript/src/base/index.ts +3 -3
  72. package/typescript/src/base/metadata.ts +148 -28
  73. package/typescript/src/base/sql.ts +264 -98
  74. package/typescript/src/base/types.ts +33 -24
  75. package/typescript/src/browser.ts +10 -6
  76. package/typescript/src/config.ts +95 -95
  77. package/typescript/src/googlesheets/adapter.ts +269 -259
  78. package/typescript/src/googlesheets/an5SheetsAdapter.ts +3 -3
  79. package/typescript/src/googlesheets/config.ts +47 -47
  80. package/typescript/src/googlesheets/helpers.ts +144 -143
  81. package/typescript/src/googlesheets/index.ts +3 -3
  82. package/typescript/src/googlesheets/parseConnectionString.ts +48 -46
  83. package/typescript/src/googlesheets/retry.ts +126 -126
  84. package/typescript/src/googlesheets/sqlExecutor.ts +304 -301
  85. package/typescript/src/googlesheets/tableClient.ts +461 -455
  86. package/typescript/src/googlesheets/types.ts +6 -6
  87. package/typescript/src/index.ts +48 -27
  88. package/typescript/src/mssql/engine.ts +134 -132
  89. package/typescript/src/mssql/index.ts +1 -1
  90. package/typescript/src/mysql/engine.ts +80 -80
  91. package/typescript/src/mysql/index.ts +1 -1
  92. package/typescript/src/nbase/client.ts +214 -0
  93. package/typescript/src/nbase/index.ts +104 -0
  94. package/typescript/src/postgres/engine.ts +87 -87
  95. package/typescript/src/postgres/index.ts +1 -1
  96. package/typescript/src/sqlite/browserEngine.ts +192 -0
  97. package/typescript/src/sqlite/engine.ts +66 -55
  98. package/typescript/src/sqlite/index.ts +3 -1
  99. package/typescript/src/typed.ts +52 -0
@@ -46,9 +46,9 @@ function matchRowSQL(row, whereText, values) {
46
46
  const m = part.match(/^(.+?)\s*(=|!=|<>|>|<|>=|<=|LIKE|CONTAINS)\s*(.+)$/i);
47
47
  if (!m)
48
48
  continue;
49
- const field = m[1].trim().replace(/[[\]]/g, '');
50
- const op = m[2].toUpperCase();
51
- const valRef = m[3].trim();
49
+ const field = (m[1] ?? '').trim().replace(/[[\]]/g, '');
50
+ const op = (m[2] ?? '').toUpperCase();
51
+ const valRef = (m[3] ?? '').trim();
52
52
  const val = valRef.startsWith('?') ? values[parseInt(valRef.slice(1))] : valRef.replace(/^['"]|['"]$/g, '');
53
53
  const cell = row[field];
54
54
  switch (op) {
@@ -94,20 +94,26 @@ function extractColsAndVals(sql, params) {
94
94
  const vals = [];
95
95
  const colMatch = sql.match(/\(([^)]+)\)\s*VALUES/i);
96
96
  if (colMatch) {
97
- const rawCols = colMatch[1].split(',').map(c => c.trim().replace(/[[\]]/g, ''));
97
+ const rawCols = (colMatch[1] ?? '').split(',').map(c => c.trim().replace(/[[\]]/g, ''));
98
98
  // Find actual values
99
- const valMatch = sql.substring(colMatch.index + colMatch[0].length).match(/\(([^)]+)\)/);
99
+ const valMatch = sql.substring((colMatch.index ?? 0) + colMatch[0].length).match(/\(([^)]+)\)/);
100
100
  if (valMatch) {
101
- const rawVals = valMatch[1].split(',').map(v => v.trim());
101
+ const rawVals = (valMatch[1] ?? '').split(',').map(v => v.trim());
102
102
  for (let i = 0; i < rawCols.length; i++) {
103
- cols.push(rawCols[i]);
103
+ const col = rawCols[i];
104
+ if (col === undefined)
105
+ continue;
106
+ cols.push(col);
104
107
  const rv = rawVals[i];
105
- if (rv.startsWith('@') && params && params[rv.slice(1)] !== undefined) {
108
+ if (rv !== undefined && rv.startsWith('@') && params && params[rv.slice(1)] !== undefined) {
106
109
  vals.push(params[rv.slice(1)]);
107
110
  }
108
- else {
111
+ else if (rv !== undefined) {
109
112
  vals.push(rv.replace(/^['"]|['"]$/g, ''));
110
113
  }
114
+ else {
115
+ vals.push(null);
116
+ }
111
117
  }
112
118
  }
113
119
  }
@@ -125,13 +131,13 @@ async function execSelect(adapter, query, params) {
125
131
  return obj;
126
132
  });
127
133
  }
128
- const sheetName = m[2] || m[3] || m[4];
134
+ const sheetName = m[2] ?? m[3] ?? m[4] ?? '';
129
135
  const whereClause = m[5];
130
136
  const raw = await adapter.readRange((0, helpers_1.esc)(sheetName) + '!A:ZZ');
131
137
  if (raw.length < 1)
132
138
  return [];
133
139
  const headers = raw[0];
134
- const fieldList = m[1].trim();
140
+ const fieldList = (m[1] ?? '*').trim();
135
141
  const selectAll = fieldList === '*';
136
142
  const rows = raw.slice(1).map(row => {
137
143
  const obj = {};
@@ -139,16 +145,15 @@ async function execSelect(adapter, query, params) {
139
145
  return obj;
140
146
  });
141
147
  if (!whereClause)
142
- return selectAll ? rows : rows.map(r => pickFields(r, fieldList, headers));
143
- const { text, values } = substParams(query, params);
148
+ return selectAll ? rows : rows.map(r => pickFields(r, fieldList));
144
149
  // Extract just the WHERE part
145
150
  const whereParts = query.match(/WHERE\s+(.+)$/i);
146
- const whereText = whereParts ? whereParts[1] : '';
151
+ const whereText = whereParts?.[1] ?? '';
147
152
  const { text: whereSubst, values: whereVals } = substParams(whereText, params);
148
153
  const result = rows.filter(r => matchRowSQL(r, whereSubst, whereVals));
149
- return selectAll ? result : result.map(r => pickFields(r, fieldList, headers));
154
+ return selectAll ? result : result.map(r => pickFields(r, fieldList));
150
155
  }
151
- function pickFields(row, fieldList, headers) {
156
+ function pickFields(row, fieldList) {
152
157
  const fields = fieldList.split(',').map(f => f.trim().replace(/[[\]]/g, ''));
153
158
  const obj = {};
154
159
  fields.forEach(f => { obj[f] = row[f]; });
@@ -158,7 +163,7 @@ async function execInsert(adapter, query, params) {
158
163
  const m = query.match(/INSERT\s+INTO\s+(?:'([^']+)'|"([^"]+)"|(\S+))\s*(.*)/i);
159
164
  if (!m)
160
165
  throw new Error('Invalid INSERT syntax');
161
- const sheetName = m[1] || m[2] || m[3];
166
+ const sheetName = m[1] ?? m[2] ?? m[3] ?? '';
162
167
  const { cols, vals } = extractColsAndVals(query, params);
163
168
  if (cols.length === 0)
164
169
  throw new Error('Cannot extract columns from INSERT');
@@ -198,8 +203,8 @@ async function execUpdate(adapter, query, params) {
198
203
  const m = query.match(/UPDATE\s+(?:'([^']+)'|"([^"]+)"|(\S+))\s+SET\s+(.+?)(?:\s+WHERE\s+(.+))?$/i);
199
204
  if (!m)
200
205
  throw new Error('Invalid UPDATE syntax');
201
- const sheetName = m[1] || m[2] || m[3];
202
- const setClause = m[4];
206
+ const sheetName = m[1] ?? m[2] ?? m[3] ?? '';
207
+ const setClause = m[4] ?? '';
203
208
  const whereClause = m[5];
204
209
  // Parse SET clause
205
210
  const setPairs = setClause.split(',').map(p => p.trim());
@@ -209,8 +214,8 @@ async function execUpdate(adapter, query, params) {
209
214
  const sm = pair.match(/^(\w+)\s*=\s*(.+)$/i);
210
215
  if (!sm)
211
216
  continue;
212
- setFields.push(sm[1].replace(/[[\]]/g, ''));
213
- const rv = sm[2].trim();
217
+ setFields.push((sm[1] ?? '').replace(/[[\]]/g, ''));
218
+ const rv = (sm[2] ?? '').trim();
214
219
  if (rv.startsWith('@') && params && params[rv.slice(1)] !== undefined) {
215
220
  setVals.push(params[rv.slice(1)]);
216
221
  }
@@ -230,7 +235,7 @@ async function execUpdate(adapter, query, params) {
230
235
  let count = 0;
231
236
  for (let i = 0; i < rows.length; i++) {
232
237
  const row = {};
233
- headers.forEach((h, j) => { row[h] = rows[i][j] ?? null; });
238
+ headers.forEach((h, j) => { row[h] = rows[i]?.[j] ?? null; });
234
239
  row.__row = i + 2;
235
240
  if (whereClause && !matchRowSQL(row, whereSubst, whereVals))
236
241
  continue;
@@ -253,7 +258,7 @@ async function execDelete(adapter, query, params) {
253
258
  const m = query.match(/DELETE\s+FROM\s+(?:'([^']+)'|"([^"]+)"|(\S+))\s*(?:WHERE\s+(.+))?$/i);
254
259
  if (!m)
255
260
  throw new Error('Invalid DELETE syntax');
256
- const sheetName = m[1] || m[2] || m[3];
261
+ const sheetName = m[1] ?? m[2] ?? m[3] ?? '';
257
262
  const whereClause = m[4];
258
263
  const meta = await adapter.getSheetMeta(sheetName);
259
264
  if (!meta)
@@ -282,7 +287,7 @@ async function execDelete(adapter, query, params) {
282
287
  const toDelete = [];
283
288
  for (let i = 1; i < raw.length; i++) {
284
289
  const row = {};
285
- headers.forEach((h, j) => { row[h] = raw[i][j] ?? null; });
290
+ headers.forEach((h, j) => { row[h] = raw[i]?.[j] ?? null; });
286
291
  if (matchRowSQL(row, whereSubst, whereVals)) {
287
292
  toDelete.push(i + 1);
288
293
  }
@@ -54,7 +54,10 @@ class SheetsTableClient {
54
54
  for (let i = 1; i < values.length; i++) {
55
55
  const row = { __row: i + 1 };
56
56
  for (let j = 0; j < headers.length; j++) {
57
- row[headers[j]] = (0, helpers_1.coerceCell)(values[i][j], this.fields[headers[j]]);
57
+ const h = headers[j];
58
+ if (h === undefined)
59
+ continue;
60
+ row[h] = (0, helpers_1.coerceCell)(values[i]?.[j], this.fields[h]);
58
61
  }
59
62
  rows.push(row);
60
63
  }
@@ -222,6 +225,8 @@ class SheetsTableClient {
222
225
  if (matching.length === 0)
223
226
  throw new Error('No record found matching where clause');
224
227
  const target = matching[0];
228
+ if (target === undefined)
229
+ throw new Error('No record found matching where clause');
225
230
  const updated = { ...target, ...args.data, __row: target.__row };
226
231
  await this.adapter.writeRange(`${this.escSheetName}!A${target.__row}`, [
227
232
  this.rowToValues(updated, headers),
@@ -256,6 +261,8 @@ class SheetsTableClient {
256
261
  if (matching.length === 0)
257
262
  throw new Error('No record found matching where clause');
258
263
  const target = matching[0];
264
+ if (target === undefined)
265
+ throw new Error('No record found matching where clause');
259
266
  const meta = await this.adapter.getSheetMeta(this.sheetName);
260
267
  if (!meta)
261
268
  throw new Error(`Sheet "${this.sheetName}" not found`);
@@ -395,14 +402,16 @@ class SheetsTableClient {
395
402
  continue;
396
403
  let dot = 0, m1 = 0, m2 = 0;
397
404
  for (let i = 0; i < args.vector.length; i++) {
398
- dot += args.vector[i] * vec[i];
399
- m1 += args.vector[i] ** 2;
400
- m2 += vec[i] ** 2;
405
+ const av = args.vector[i] ?? 0;
406
+ const bv = vec[i] ?? 0;
407
+ dot += av * bv;
408
+ m1 += av ** 2;
409
+ m2 += bv ** 2;
401
410
  }
402
411
  const cosine = m1 && m2 ? dot / (Math.sqrt(m1) * Math.sqrt(m2)) : 0;
403
412
  const dist = metric === 'cosine' ? 1 - cosine
404
413
  : metric === 'dot' ? -dot
405
- : Math.sqrt(args.vector.reduce((s, v, i) => s + (v - vec[i]) ** 2, 0));
414
+ : Math.sqrt(args.vector.reduce((s, v, i) => s + (v - (vec[i] ?? 0)) ** 2, 0));
406
415
  scored.push({ row, dist });
407
416
  }
408
417
  scored.sort((a, b) => a.dist - b.dist);
package/dist/index.d.ts CHANGED
@@ -1,5 +1,9 @@
1
- export { An5Adapter, AdapterTableClient, createAn5Adapter, setAdapterMetadata, } from './an5Adapter';
1
+ export { An5Adapter, AdapterTableClient, createAn5Adapter, createAdapterProxy, executorFromAdapter, setAdapterMetadata, } from './an5Adapter';
2
+ export { parseWhere, buildOrderBy, quote } from './base/sql';
3
+ export { NBaseVectorClient, NBaseError, createNBaseVectorClient, createNBaseClient, parseNBaseConnectionString, buildVectorId, parseVectorId, } from './nbase';
4
+ export type { NBaseAdapterConfig, NBaseClientConfig, NBaseVector, NBaseSearchOptions, NBaseSearchResult, NBaseStats, } from './nbase';
2
5
  export type { An5AdapterConfig, AdapterMetadata, Dialect, } from './an5Adapter';
6
+ export type { TypedAn5Adapter, AdapterAPI } from './typed';
3
7
  export { getLlmConfig, setLlmConfig, getEmbeddingConfig, setEmbeddingConfig, resetAdapter, } from './config';
4
8
  export type { LlmConfigData, EmbeddingConfigData, } from './config';
5
9
  export { An5SheetsAdapter, SheetsTableClient, createAn5SheetsAdapter, parseSheetsConnectionString, } from './googlesheets';
package/dist/index.js CHANGED
@@ -1,11 +1,25 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseSheetsConnectionString = exports.createAn5SheetsAdapter = exports.SheetsTableClient = exports.An5SheetsAdapter = exports.resetAdapter = exports.setEmbeddingConfig = exports.getEmbeddingConfig = exports.setLlmConfig = exports.getLlmConfig = exports.setAdapterMetadata = exports.createAn5Adapter = exports.AdapterTableClient = exports.An5Adapter = void 0;
3
+ exports.parseSheetsConnectionString = exports.createAn5SheetsAdapter = exports.SheetsTableClient = exports.An5SheetsAdapter = exports.resetAdapter = exports.setEmbeddingConfig = exports.getEmbeddingConfig = exports.setLlmConfig = exports.getLlmConfig = exports.parseVectorId = exports.buildVectorId = exports.parseNBaseConnectionString = exports.createNBaseClient = exports.createNBaseVectorClient = exports.NBaseError = exports.NBaseVectorClient = exports.quote = exports.buildOrderBy = exports.parseWhere = exports.setAdapterMetadata = exports.executorFromAdapter = exports.createAdapterProxy = exports.createAn5Adapter = exports.AdapterTableClient = exports.An5Adapter = void 0;
4
4
  var an5Adapter_1 = require("./an5Adapter");
5
5
  Object.defineProperty(exports, "An5Adapter", { enumerable: true, get: function () { return an5Adapter_1.An5Adapter; } });
6
6
  Object.defineProperty(exports, "AdapterTableClient", { enumerable: true, get: function () { return an5Adapter_1.AdapterTableClient; } });
7
7
  Object.defineProperty(exports, "createAn5Adapter", { enumerable: true, get: function () { return an5Adapter_1.createAn5Adapter; } });
8
+ Object.defineProperty(exports, "createAdapterProxy", { enumerable: true, get: function () { return an5Adapter_1.createAdapterProxy; } });
9
+ Object.defineProperty(exports, "executorFromAdapter", { enumerable: true, get: function () { return an5Adapter_1.executorFromAdapter; } });
8
10
  Object.defineProperty(exports, "setAdapterMetadata", { enumerable: true, get: function () { return an5Adapter_1.setAdapterMetadata; } });
11
+ var sql_1 = require("./base/sql");
12
+ Object.defineProperty(exports, "parseWhere", { enumerable: true, get: function () { return sql_1.parseWhere; } });
13
+ Object.defineProperty(exports, "buildOrderBy", { enumerable: true, get: function () { return sql_1.buildOrderBy; } });
14
+ Object.defineProperty(exports, "quote", { enumerable: true, get: function () { return sql_1.quote; } });
15
+ var nbase_1 = require("./nbase");
16
+ Object.defineProperty(exports, "NBaseVectorClient", { enumerable: true, get: function () { return nbase_1.NBaseVectorClient; } });
17
+ Object.defineProperty(exports, "NBaseError", { enumerable: true, get: function () { return nbase_1.NBaseError; } });
18
+ Object.defineProperty(exports, "createNBaseVectorClient", { enumerable: true, get: function () { return nbase_1.createNBaseVectorClient; } });
19
+ Object.defineProperty(exports, "createNBaseClient", { enumerable: true, get: function () { return nbase_1.createNBaseClient; } });
20
+ Object.defineProperty(exports, "parseNBaseConnectionString", { enumerable: true, get: function () { return nbase_1.parseNBaseConnectionString; } });
21
+ Object.defineProperty(exports, "buildVectorId", { enumerable: true, get: function () { return nbase_1.buildVectorId; } });
22
+ Object.defineProperty(exports, "parseVectorId", { enumerable: true, get: function () { return nbase_1.parseVectorId; } });
9
23
  var config_1 = require("./config");
10
24
  Object.defineProperty(exports, "getLlmConfig", { enumerable: true, get: function () { return config_1.getLlmConfig; } });
11
25
  Object.defineProperty(exports, "setLlmConfig", { enumerable: true, get: function () { return config_1.setLlmConfig; } });
@@ -12,7 +12,7 @@ function parseMssqlConnectionString(url) {
12
12
  // Strip scheme prefix
13
13
  cleanUrl = cleanUrl.replace(/^(?:sqlserver|mssql):\/\//i, '');
14
14
  const parts = cleanUrl.split(';');
15
- const firstPart = parts[0].trim();
15
+ const firstPart = (parts[0] ?? '').trim();
16
16
  // host:port or host,port (SQL Server native style)
17
17
  let server = firstPart;
18
18
  let port = 1433;
@@ -35,7 +35,10 @@ function parseMssqlConnectionString(url) {
35
35
  connectionTimeout: 15000,
36
36
  };
37
37
  for (let i = 1; i < parts.length; i++) {
38
- const part = parts[i].trim();
38
+ const rawPart = parts[i];
39
+ if (rawPart === undefined)
40
+ continue;
41
+ const part = rawPart.trim();
39
42
  if (!part)
40
43
  continue;
41
44
  const eqIdx = part.indexOf('=');
@@ -52,7 +55,8 @@ function parseMssqlConnectionString(url) {
52
55
  else if (key === 'server' || key === 'data source') {
53
56
  // SERVER=host,port style (key-value form)
54
57
  const [s, p] = value.split(',');
55
- config.server = s.trim();
58
+ if (s !== undefined)
59
+ config.server = s.trim();
56
60
  if (p)
57
61
  config.port = parseInt(p.trim(), 10) || 1433;
58
62
  }
@@ -70,7 +74,7 @@ class MssqlEngine {
70
74
  constructor(adapterConfig) {
71
75
  this.dialect = 'mssql';
72
76
  this.pool = null;
73
- this.config = parseMssqlConnectionString(adapterConfig.connectionString);
77
+ this.config = parseMssqlConnectionString(adapterConfig.connectionString ?? '');
74
78
  if (adapterConfig.poolMax)
75
79
  this.config.pool = { ...this.config.pool, max: adapterConfig.poolMax };
76
80
  if (adapterConfig.requestTimeout)
@@ -14,7 +14,7 @@ class MysqlEngine {
14
14
  if (!Mysql2)
15
15
  throw new Error('mysql2 package is required for MySQL support. Run: npm install mysql2');
16
16
  this.pool = Mysql2.createPool({
17
- uri: adapterConfig.connectionString,
17
+ uri: adapterConfig.connectionString ?? '',
18
18
  connectionLimit: adapterConfig.poolMax ?? 10,
19
19
  connectTimeout: adapterConfig.connectionTimeout ?? 15000,
20
20
  });
@@ -0,0 +1,92 @@
1
+ /**
2
+ * NBase — Neural Vector Database client.
3
+ *
4
+ * NBase is a vector database with HNSW/LSH/KNN indexing exposed over a REST
5
+ * API. an5 uses it to move similarity search out of the database process: the
6
+ * vectors live in NBase, the rows stay in the relational table, and the search
7
+ * results are hydrated from the table by the id kept in vector metadata.
8
+ *
9
+ * The client only uses the global `fetch`, so it works in Node 18+ and in the
10
+ * browser without pulling in a dependency.
11
+ */
12
+ export interface NBaseVector {
13
+ id: string | number;
14
+ vector: number[];
15
+ metadata?: Record<string, unknown>;
16
+ }
17
+ export interface NBaseSearchOptions {
18
+ /** Number of neighbours to return. */
19
+ k?: number;
20
+ /** Distance metric; NBase accepts `cosine` and `euclidean`. */
21
+ distanceMetric?: 'cosine' | 'euclidean';
22
+ /** `hnsw` uses the graph index, anything else uses the clustered path. */
23
+ method?: 'hnsw' | 'clustered';
24
+ efSearch?: number;
25
+ filters?: Record<string, unknown> | Array<Record<string, unknown>>;
26
+ includeMetadata?: boolean;
27
+ includeVectors?: boolean;
28
+ skipCache?: boolean;
29
+ searchTimeoutMs?: number;
30
+ rerank?: boolean;
31
+ rerankingMethod?: string;
32
+ rerankLambda?: number;
33
+ partitionIds?: string[];
34
+ }
35
+ export interface NBaseSearchResult {
36
+ id: string | number;
37
+ /** Distance from the query vector. Lower is more similar. */
38
+ dist: number;
39
+ metadata?: Record<string, unknown>;
40
+ vector?: number[];
41
+ dimension?: number;
42
+ }
43
+ export interface NBaseClientConfig {
44
+ /** Base URL, e.g. `http://localhost:1307`. */
45
+ url: string;
46
+ /** Optional bearer token, for deployments behind an auth proxy. */
47
+ token?: string;
48
+ /** Per-request timeout in milliseconds. */
49
+ timeoutMs?: number;
50
+ /** Injectable for tests and for runtimes without a global fetch. */
51
+ fetchImpl?: typeof fetch;
52
+ }
53
+ export interface NBaseStats {
54
+ vectorCount?: number;
55
+ metadataCount?: number;
56
+ memoryUsage?: number;
57
+ [key: string]: unknown;
58
+ }
59
+ export declare class NBaseError extends Error {
60
+ readonly status?: number | undefined;
61
+ readonly body?: unknown;
62
+ constructor(message: string, status?: number | undefined, body?: unknown);
63
+ }
64
+ export declare class NBaseVectorClient {
65
+ private readonly config;
66
+ private readonly baseUrl;
67
+ private readonly fetchImpl;
68
+ private readonly timeoutMs;
69
+ constructor(config: NBaseClientConfig);
70
+ private request;
71
+ /** Liveness probe. NBase mounts its status routes at the server root. */
72
+ health(): Promise<{
73
+ status: string;
74
+ version?: string;
75
+ }>;
76
+ /** Vector count, memory use and other database statistics. */
77
+ stats(): Promise<NBaseStats>;
78
+ /** Adds or replaces vectors. NBase accepts a single vector or a bulk array. */
79
+ addVectors(vectors: NBaseVector[]): Promise<{
80
+ count: number;
81
+ ids?: Array<string | number>;
82
+ }>;
83
+ /** Nearest-neighbour search. Results are ordered by ascending `dist`. */
84
+ search(vector: number[], options?: NBaseSearchOptions): Promise<NBaseSearchResult[]>;
85
+ /** Reads a single vector. Returns undefined when the id is unknown. */
86
+ get(id: string | number): Promise<NBaseSearchResult | undefined>;
87
+ exists(id: string | number): Promise<boolean>;
88
+ delete(id: string | number): Promise<boolean>;
89
+ updateMetadata(id: string | number, metadata: Record<string, unknown>): Promise<boolean>;
90
+ }
91
+ /** Creates an NBase vector client. */
92
+ export declare function createNBaseVectorClient(config: NBaseClientConfig): NBaseVectorClient;
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ /**
3
+ * NBase — Neural Vector Database client.
4
+ *
5
+ * NBase is a vector database with HNSW/LSH/KNN indexing exposed over a REST
6
+ * API. an5 uses it to move similarity search out of the database process: the
7
+ * vectors live in NBase, the rows stay in the relational table, and the search
8
+ * results are hydrated from the table by the id kept in vector metadata.
9
+ *
10
+ * The client only uses the global `fetch`, so it works in Node 18+ and in the
11
+ * browser without pulling in a dependency.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.NBaseVectorClient = exports.NBaseError = void 0;
15
+ exports.createNBaseVectorClient = createNBaseVectorClient;
16
+ const DEFAULT_TIMEOUT_MS = 30000;
17
+ class NBaseError extends Error {
18
+ constructor(message, status, body) {
19
+ super(message);
20
+ this.status = status;
21
+ this.body = body;
22
+ this.name = 'NBaseError';
23
+ }
24
+ }
25
+ exports.NBaseError = NBaseError;
26
+ class NBaseVectorClient {
27
+ constructor(config) {
28
+ this.config = config;
29
+ this.baseUrl = String(config.url).replace(/\/+$/, '');
30
+ const impl = config.fetchImpl ?? globalThis.fetch;
31
+ if (!impl) {
32
+ throw new NBaseError('No fetch implementation available. Node 18+ or a fetch polyfill is required.');
33
+ }
34
+ this.fetchImpl = impl;
35
+ this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
36
+ }
37
+ async request(path, init) {
38
+ const controller = new AbortController();
39
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
40
+ try {
41
+ const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
42
+ ...init,
43
+ signal: controller.signal,
44
+ headers: {
45
+ 'content-type': 'application/json',
46
+ ...(this.config.token ? { authorization: `Bearer ${this.config.token}` } : {}),
47
+ ...init?.headers,
48
+ },
49
+ });
50
+ const text = await response.text();
51
+ let body;
52
+ try {
53
+ body = text ? JSON.parse(text) : undefined;
54
+ }
55
+ catch {
56
+ body = text;
57
+ }
58
+ if (!response.ok) {
59
+ const message = (body && typeof body === 'object' && 'error' in body
60
+ ? String(body.error)
61
+ : undefined) ?? `HTTP ${response.status}`;
62
+ throw new NBaseError(`NBase request ${path} failed: ${message}`, response.status, body);
63
+ }
64
+ return body;
65
+ }
66
+ catch (err) {
67
+ if (err instanceof NBaseError)
68
+ throw err;
69
+ if (err?.name === 'AbortError') {
70
+ throw new NBaseError(`NBase request ${path} timed out after ${this.timeoutMs}ms`);
71
+ }
72
+ throw new NBaseError(`Could not reach NBase at ${this.baseUrl}: ${err instanceof Error ? err.message : String(err)}`);
73
+ }
74
+ finally {
75
+ clearTimeout(timer);
76
+ }
77
+ }
78
+ /** Liveness probe. NBase mounts its status routes at the server root. */
79
+ async health() {
80
+ return this.request('/health');
81
+ }
82
+ /** Vector count, memory use and other database statistics. */
83
+ async stats() {
84
+ return this.request('/stats');
85
+ }
86
+ /** Adds or replaces vectors. NBase accepts a single vector or a bulk array. */
87
+ async addVectors(vectors) {
88
+ if (vectors.length === 0)
89
+ return { count: 0 };
90
+ const result = await this.request('/api/vectors', { method: 'POST', body: JSON.stringify({ vectors: vectors.length === 1 ? vectors[0] : vectors }) });
91
+ return { count: result.count ?? vectors.length, ...(result.addedIds ? { ids: result.addedIds } : {}) };
92
+ }
93
+ /** Nearest-neighbour search. Results are ordered by ascending `dist`. */
94
+ async search(vector, options = {}) {
95
+ const body = await this.request('/api/search', {
96
+ method: 'POST',
97
+ body: JSON.stringify({
98
+ query: vector,
99
+ k: options.k ?? 10,
100
+ ...(options.distanceMetric ? { distanceMetric: options.distanceMetric } : {}),
101
+ ...(options.method ? { method: options.method } : {}),
102
+ ...(options.efSearch !== undefined ? { efSearch: options.efSearch } : {}),
103
+ ...(options.filters ? { filters: options.filters } : {}),
104
+ ...(options.includeMetadata !== undefined ? { includeMetadata: options.includeMetadata } : {}),
105
+ ...(options.includeVectors !== undefined ? { includeVectors: options.includeVectors } : {}),
106
+ ...(options.skipCache !== undefined ? { skipCache: options.skipCache } : {}),
107
+ ...(options.searchTimeoutMs !== undefined ? { searchTimeoutMs: options.searchTimeoutMs } : {}),
108
+ ...(options.rerank !== undefined ? { rerank: options.rerank } : {}),
109
+ ...(options.rerankingMethod ? { rerankingMethod: options.rerankingMethod } : {}),
110
+ ...(options.rerankLambda !== undefined ? { rerankLambda: options.rerankLambda } : {}),
111
+ ...(options.partitionIds ? { partitionIds: options.partitionIds } : {}),
112
+ }),
113
+ });
114
+ return body.results ?? [];
115
+ }
116
+ /** Reads a single vector. Returns undefined when the id is unknown. */
117
+ async get(id) {
118
+ try {
119
+ return await this.request(`/api/vectors/${encodeURIComponent(String(id))}`);
120
+ }
121
+ catch (err) {
122
+ if (err instanceof NBaseError && err.status === 404)
123
+ return undefined;
124
+ throw err;
125
+ }
126
+ }
127
+ async exists(id) {
128
+ const result = await this.request(`/api/vectors/${encodeURIComponent(String(id))}/exists`);
129
+ return result.exists === true;
130
+ }
131
+ async delete(id) {
132
+ await this.request(`/api/vectors/${encodeURIComponent(String(id))}`, { method: 'DELETE' });
133
+ return true;
134
+ }
135
+ async updateMetadata(id, metadata) {
136
+ await this.request(`/api/vectors/${encodeURIComponent(String(id))}/metadata`, {
137
+ method: 'PATCH',
138
+ body: JSON.stringify(metadata),
139
+ });
140
+ return true;
141
+ }
142
+ }
143
+ exports.NBaseVectorClient = NBaseVectorClient;
144
+ /** Creates an NBase vector client. */
145
+ function createNBaseVectorClient(config) {
146
+ return new NBaseVectorClient(config);
147
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * NBase connection-string and adapter configuration.
3
+ *
4
+ * NBase is configured through the connection string alone:
5
+ *
6
+ * createAn5Adapter({ connectionString: 'nbase://localhost:1307' })
7
+ *
8
+ * Accepted forms are `nbase://host:port`, `nbase:host:port` and an explicit
9
+ * scheme, with optional `?token=…&timeoutMs=…&method=…` options. An adapter
10
+ * created this way is vector-only: it has no relational database, so a search
11
+ * returns the NBase hits instead of table rows.
12
+ */
13
+ import { NBaseVectorClient, NBaseError, createNBaseVectorClient, type NBaseClientConfig, type NBaseVector, type NBaseSearchOptions, type NBaseSearchResult, type NBaseStats } from './client';
14
+ export interface NBaseAdapterConfig extends NBaseClientConfig {
15
+ url: string;
16
+ /**
17
+ * Metadata key that holds the relational row id. `an5Id` by default, which
18
+ * is what `indexTableVectors` writes.
19
+ */
20
+ idField?: string;
21
+ /** Metadata key holding the model name, used to keep collections apart. */
22
+ modelField?: string;
23
+ /** Force the search method instead of letting NBase choose. */
24
+ method?: 'hnsw' | 'clustered';
25
+ }
26
+ /** True when the whole connection string points at NBase. */
27
+ export declare function isNBaseConnectionString(connectionString: string): boolean;
28
+ /**
29
+ * Parses an NBase connection string.
30
+ *
31
+ * Accepts `nbase:http://host:port`, `nbase://host:port` and
32
+ * `nbase:host:port`, with optional `?token=…&timeoutMs=…` options.
33
+ */
34
+ export declare function parseNBaseConnectionString(connectionString: string): NBaseAdapterConfig;
35
+ /** Builds the vector id used to link a NBase vector back to a relational row. */
36
+ export declare function buildVectorId(model: string, id: string | number): string;
37
+ /** Splits a vector id produced by {@link buildVectorId}. */
38
+ export declare function parseVectorId(vectorId: string | number): {
39
+ model: string;
40
+ id: string;
41
+ };
42
+ export declare function createNBaseClient(config: NBaseAdapterConfig | string): {
43
+ client: NBaseVectorClient;
44
+ options: NBaseAdapterConfig;
45
+ };
46
+ export { NBaseVectorClient, NBaseError, createNBaseVectorClient };
47
+ export type { NBaseClientConfig, NBaseVector, NBaseSearchOptions, NBaseSearchResult, NBaseStats };
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNBaseVectorClient = exports.NBaseError = exports.NBaseVectorClient = void 0;
4
+ exports.isNBaseConnectionString = isNBaseConnectionString;
5
+ exports.parseNBaseConnectionString = parseNBaseConnectionString;
6
+ exports.buildVectorId = buildVectorId;
7
+ exports.parseVectorId = parseVectorId;
8
+ exports.createNBaseClient = createNBaseClient;
9
+ /**
10
+ * NBase connection-string and adapter configuration.
11
+ *
12
+ * NBase is configured through the connection string alone:
13
+ *
14
+ * createAn5Adapter({ connectionString: 'nbase://localhost:1307' })
15
+ *
16
+ * Accepted forms are `nbase://host:port`, `nbase:host:port` and an explicit
17
+ * scheme, with optional `?token=…&timeoutMs=…&method=…` options. An adapter
18
+ * created this way is vector-only: it has no relational database, so a search
19
+ * returns the NBase hits instead of table rows.
20
+ */
21
+ const client_1 = require("./client");
22
+ Object.defineProperty(exports, "NBaseVectorClient", { enumerable: true, get: function () { return client_1.NBaseVectorClient; } });
23
+ Object.defineProperty(exports, "NBaseError", { enumerable: true, get: function () { return client_1.NBaseError; } });
24
+ Object.defineProperty(exports, "createNBaseVectorClient", { enumerable: true, get: function () { return client_1.createNBaseVectorClient; } });
25
+ /** Matches `nbase:`, `nbase://` and `nbase=http(s)://` at the start of a value. */
26
+ const NBASE_PREFIX = /^nbase:(?:\/\/)?/i;
27
+ /** True when the whole connection string points at NBase. */
28
+ function isNBaseConnectionString(connectionString) {
29
+ return NBASE_PREFIX.test(connectionString.trim());
30
+ }
31
+ /** Builds options from a URL and an optional query string. */
32
+ function buildOptions(url, query) {
33
+ const params = new URLSearchParams(query ?? '');
34
+ const trimmed = url.trim().replace(/\/+$/, '');
35
+ // `nbase://host:port` is shorthand for the same host over HTTP; an explicit
36
+ // scheme is used as-is so https deployments work.
37
+ const withScheme = !trimmed
38
+ ? 'http://localhost:1307'
39
+ : /^[a-z]+:\/\//i.test(trimmed)
40
+ ? trimmed
41
+ : `http://${trimmed}`;
42
+ return {
43
+ url: withScheme,
44
+ ...(params.get('token') ? { token: params.get('token') } : {}),
45
+ ...(params.get('timeoutMs') ? { timeoutMs: Number(params.get('timeoutMs')) } : {}),
46
+ ...(params.get('idField') ? { idField: params.get('idField') } : {}),
47
+ ...(params.get('modelField') ? { modelField: params.get('modelField') } : {}),
48
+ ...(params.get('method') ? { method: params.get('method') } : {}),
49
+ };
50
+ }
51
+ /**
52
+ * Parses an NBase connection string.
53
+ *
54
+ * Accepts `nbase:http://host:port`, `nbase://host:port` and
55
+ * `nbase:host:port`, with optional `?token=…&timeoutMs=…` options.
56
+ */
57
+ function parseNBaseConnectionString(connectionString) {
58
+ const raw = connectionString.trim();
59
+ if (!isNBaseConnectionString(raw)) {
60
+ throw new Error(`Not an NBase connection string: ${raw}`);
61
+ }
62
+ const [url, query] = raw.replace(NBASE_PREFIX, '').split('?');
63
+ return buildOptions(url ?? '', query);
64
+ }
65
+ /** Builds the vector id used to link a NBase vector back to a relational row. */
66
+ function buildVectorId(model, id) {
67
+ return `${model}:${id}`;
68
+ }
69
+ /** Splits a vector id produced by {@link buildVectorId}. */
70
+ function parseVectorId(vectorId) {
71
+ const raw = String(vectorId);
72
+ const separator = raw.indexOf(':');
73
+ if (separator === -1)
74
+ return { model: '', id: raw };
75
+ return { model: raw.slice(0, separator), id: raw.slice(separator + 1) };
76
+ }
77
+ function createNBaseClient(config) {
78
+ const options = typeof config === 'string' ? parseNBaseConnectionString(config) : config;
79
+ return { client: (0, client_1.createNBaseVectorClient)(options), options };
80
+ }
@@ -14,7 +14,7 @@ class PostgresEngine {
14
14
  if (!PgPool)
15
15
  throw new Error('pg package is required for PostgreSQL support. Run: npm install pg');
16
16
  this.pool = new PgPool({
17
- connectionString: adapterConfig.connectionString,
17
+ connectionString: adapterConfig.connectionString ?? '',
18
18
  max: adapterConfig.poolMax ?? 10,
19
19
  });
20
20
  }