@an5/adapters 0.1.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.
Files changed (91) hide show
  1. package/README.md +263 -0
  2. package/dotnet/An5Adapter.cs +275 -0
  3. package/dotnet/Base/Core.cs +82 -0
  4. package/dotnet/Mssql/MssqlEngine.cs +144 -0
  5. package/dotnet/Postgres/PostgresEngine.cs +121 -0
  6. package/package.json +46 -0
  7. package/python/an5_adapter.py +97 -0
  8. package/python/base/__init__.py +3 -0
  9. package/python/base/dialects.py +8 -0
  10. package/python/base/metadata.py +9 -0
  11. package/python/base/sql.py +121 -0
  12. package/python/mssql/__init__.py +1 -0
  13. package/python/mssql/provider.py +62 -0
  14. package/python/postgres/__init__.py +1 -0
  15. package/python/postgres/provider.py +15 -0
  16. package/python/table_client.py +209 -0
  17. package/typescript/an5Adapter.d.ts +117 -0
  18. package/typescript/an5Adapter.js +447 -0
  19. package/typescript/an5Adapter.ts +474 -0
  20. package/typescript/base/index.d.ts +3 -0
  21. package/typescript/base/index.js +19 -0
  22. package/typescript/base/index.ts +3 -0
  23. package/typescript/base/metadata.d.ts +14 -0
  24. package/typescript/base/metadata.js +17 -0
  25. package/typescript/base/metadata.ts +28 -0
  26. package/typescript/base/sql.d.ts +4 -0
  27. package/typescript/base/sql.js +143 -0
  28. package/typescript/base/sql.ts +98 -0
  29. package/typescript/base/types.d.ts +21 -0
  30. package/typescript/base/types.js +2 -0
  31. package/typescript/base/types.ts +24 -0
  32. package/typescript/config.d.ts +17 -0
  33. package/typescript/config.js +92 -0
  34. package/typescript/config.ts +95 -0
  35. package/typescript/googlesheets/adapter.d.ts +31 -0
  36. package/typescript/googlesheets/adapter.js +138 -0
  37. package/typescript/googlesheets/adapter.ts +167 -0
  38. package/typescript/googlesheets/an5SheetsAdapter.d.ts +3 -0
  39. package/typescript/googlesheets/an5SheetsAdapter.js +8 -0
  40. package/typescript/googlesheets/an5SheetsAdapter.ts +3 -0
  41. package/typescript/googlesheets/config.d.ts +17 -0
  42. package/typescript/googlesheets/config.js +29 -0
  43. package/typescript/googlesheets/config.ts +33 -0
  44. package/typescript/googlesheets/helpers.d.ts +9 -0
  45. package/typescript/googlesheets/helpers.js +155 -0
  46. package/typescript/googlesheets/helpers.ts +124 -0
  47. package/typescript/googlesheets/index.d.ts +2 -0
  48. package/typescript/googlesheets/index.js +9 -0
  49. package/typescript/googlesheets/index.ts +2 -0
  50. package/typescript/googlesheets/parseConnectionString.d.ts +11 -0
  51. package/typescript/googlesheets/parseConnectionString.js +44 -0
  52. package/typescript/googlesheets/parseConnectionString.ts +44 -0
  53. package/typescript/googlesheets/retry.d.ts +3 -0
  54. package/typescript/googlesheets/retry.js +26 -0
  55. package/typescript/googlesheets/retry.ts +23 -0
  56. package/typescript/googlesheets/sqlExecutor.d.ts +12 -0
  57. package/typescript/googlesheets/sqlExecutor.js +306 -0
  58. package/typescript/googlesheets/sqlExecutor.ts +301 -0
  59. package/typescript/googlesheets/tableClient.d.ts +78 -0
  60. package/typescript/googlesheets/tableClient.js +394 -0
  61. package/typescript/googlesheets/tableClient.ts +434 -0
  62. package/typescript/googlesheets/types.d.ts +4 -0
  63. package/typescript/googlesheets/types.js +3 -0
  64. package/typescript/googlesheets/types.ts +6 -0
  65. package/typescript/index.d.ts +2 -0
  66. package/typescript/index.js +14 -0
  67. package/typescript/index.ts +15 -0
  68. package/typescript/mssql/engine.d.ts +16 -0
  69. package/typescript/mssql/engine.js +138 -0
  70. package/typescript/mssql/engine.ts +132 -0
  71. package/typescript/mssql/index.d.ts +1 -0
  72. package/typescript/mssql/index.js +17 -0
  73. package/typescript/mssql/index.ts +1 -0
  74. package/typescript/mysql/engine.d.ts +13 -0
  75. package/typescript/mysql/engine.js +77 -0
  76. package/typescript/mysql/engine.ts +80 -0
  77. package/typescript/mysql/index.d.ts +1 -0
  78. package/typescript/mysql/index.js +17 -0
  79. package/typescript/mysql/index.ts +1 -0
  80. package/typescript/postgres/engine.d.ts +13 -0
  81. package/typescript/postgres/engine.js +86 -0
  82. package/typescript/postgres/engine.ts +87 -0
  83. package/typescript/postgres/index.d.ts +1 -0
  84. package/typescript/postgres/index.js +17 -0
  85. package/typescript/postgres/index.ts +1 -0
  86. package/typescript/sqlite/engine.d.ts +13 -0
  87. package/typescript/sqlite/engine.js +58 -0
  88. package/typescript/sqlite/engine.ts +55 -0
  89. package/typescript/sqlite/index.d.ts +1 -0
  90. package/typescript/sqlite/index.js +17 -0
  91. package/typescript/sqlite/index.ts +1 -0
@@ -0,0 +1,447 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAdapter = exports.AdapterTableClient = exports.An5AdapterTx = exports.An5Adapter = exports.setAdapterMetadata = void 0;
4
+ exports.createAn5Adapter = createAn5Adapter;
5
+ const crypto_1 = require("crypto");
6
+ const googlesheets_1 = require("./googlesheets");
7
+ const parseConnectionString_1 = require("./googlesheets/parseConnectionString");
8
+ const sql_1 = require("./base/sql");
9
+ const metadata_1 = require("./base/metadata");
10
+ const mssql_1 = require("./mssql");
11
+ const postgres_1 = require("./postgres");
12
+ const mysql_1 = require("./mysql");
13
+ const sqlite_1 = require("./sqlite");
14
+ var metadata_2 = require("./base/metadata");
15
+ Object.defineProperty(exports, "setAdapterMetadata", { enumerable: true, get: function () { return metadata_2.setAdapterMetadata; } });
16
+ function isSheetsConfig(config) {
17
+ return config.spreadsheetId !== undefined;
18
+ }
19
+ // ─── An5Adapter ────────────────────────────────────────────────────────────────────
20
+ class An5Adapter {
21
+ get dialect() { return this.sheetsAdapter ? 'googlesheets' : this.requireEngine().dialect; }
22
+ constructor(adapterConfig) {
23
+ this.engine = null;
24
+ this.sheetsAdapter = null;
25
+ if (isSheetsConfig(adapterConfig)) {
26
+ this.sheetsAdapter = new googlesheets_1.An5SheetsAdapter(adapterConfig);
27
+ return;
28
+ }
29
+ const cs = adapterConfig.connectionString.trim();
30
+ if (cs.startsWith('googlesheets://')) {
31
+ this.sheetsAdapter = new googlesheets_1.An5SheetsAdapter((0, parseConnectionString_1.parseSheetsConnectionString)(cs));
32
+ return;
33
+ }
34
+ if (cs.startsWith('postgres://') || cs.startsWith('postgresql://')) {
35
+ this.engine = new postgres_1.PostgresEngine(adapterConfig);
36
+ }
37
+ else if (cs.startsWith('mysql://') || cs.startsWith('mariadb://')) {
38
+ this.engine = new mysql_1.MysqlEngine(adapterConfig);
39
+ }
40
+ else if (cs.startsWith('sqlite://') || cs.endsWith('.sqlite') || cs.endsWith('.db')) {
41
+ this.engine = new sqlite_1.SqliteEngine(adapterConfig);
42
+ }
43
+ else {
44
+ this.engine = new mssql_1.MssqlEngine(adapterConfig);
45
+ }
46
+ }
47
+ requireEngine() {
48
+ if (!this.engine)
49
+ throw new Error('SQL engine is not available for Google Sheets adapter');
50
+ return this.engine;
51
+ }
52
+ requireSheetsAdapter() {
53
+ if (!this.sheetsAdapter)
54
+ throw new Error('Google Sheets methods require a googlesheets:// connection or Sheets config');
55
+ return this.sheetsAdapter;
56
+ }
57
+ async exec(query, params) {
58
+ if (this.sheetsAdapter)
59
+ return this.sheetsAdapter.exec(query, params);
60
+ return this.requireEngine().exec(query, params);
61
+ }
62
+ /** INTERNAL: used by AdapterTableClient for DML statements needing row count */
63
+ async _executeRaw(query, params) {
64
+ return this.requireEngine().executeRaw(query, params);
65
+ }
66
+ /** Execute a raw query with positional values → @p_0, @p_1, ... per dialect */
67
+ async $queryRawUnsafe(query, ...values) {
68
+ if (this.sheetsAdapter)
69
+ return this.sheetsAdapter.$queryRawUnsafe(query, ...values);
70
+ const params = {};
71
+ values.forEach((v, i) => { params[`p_${i}`] = v; });
72
+ // Normalise external positional placeholders → @p_N so each engine can re-transform
73
+ let q = query;
74
+ if (this.dialect === 'postgres') {
75
+ let idx = 0;
76
+ q = q.replace(/\$\d+/g, () => `@p_${idx++}`);
77
+ }
78
+ else if (this.dialect === 'mysql') {
79
+ let idx = 0;
80
+ q = q.replace(/\?/g, () => `@p_${idx++}`);
81
+ }
82
+ return this.exec(q, params);
83
+ }
84
+ async $executeRaw(query, ...values) {
85
+ if (this.sheetsAdapter)
86
+ return this.sheetsAdapter.$executeRaw(query, ...values);
87
+ const params = {};
88
+ values.forEach((v, i) => { params[`p_${i}`] = v; });
89
+ let q = query;
90
+ if (this.dialect === 'postgres') {
91
+ let idx = 0;
92
+ q = q.replace(/\$\d+/g, () => `@p_${idx++}`);
93
+ }
94
+ else if (this.dialect === 'mysql') {
95
+ let idx = 0;
96
+ q = q.replace(/\?/g, () => `@p_${idx++}`);
97
+ }
98
+ return this.requireEngine().executeRaw(q, params);
99
+ }
100
+ async $executeRawUnsafe(query, ...values) {
101
+ return this.$executeRaw(query, ...values);
102
+ }
103
+ async $connect() {
104
+ if (this.sheetsAdapter)
105
+ return this.sheetsAdapter.$connect();
106
+ await this.requireEngine().connect();
107
+ }
108
+ async $disconnect() {
109
+ if (this.sheetsAdapter)
110
+ return this.sheetsAdapter.$disconnect();
111
+ await this.requireEngine().disconnect();
112
+ }
113
+ async $transaction(fn, _options) {
114
+ if (this.sheetsAdapter)
115
+ return this.sheetsAdapter.$transaction(fn, _options);
116
+ if (Array.isArray(fn))
117
+ return Promise.all(fn);
118
+ const handle = await this.requireEngine().beginTransaction();
119
+ const txAdapter = new An5AdapterTx(handle, this.dialect);
120
+ try {
121
+ const result = await fn(txAdapter);
122
+ await handle.commit();
123
+ return result;
124
+ }
125
+ catch (err) {
126
+ await handle.rollback();
127
+ throw err;
128
+ }
129
+ }
130
+ table(modelName) {
131
+ if (this.sheetsAdapter)
132
+ return this.sheetsAdapter.table(modelName);
133
+ return new AdapterTableClient(this, modelName);
134
+ }
135
+ async readRange(range) {
136
+ return this.requireSheetsAdapter().readRange(range);
137
+ }
138
+ async writeRange(range, values) {
139
+ await this.requireSheetsAdapter().writeRange(range, values);
140
+ }
141
+ async appendRange(range, values) {
142
+ await this.requireSheetsAdapter().appendRange(range, values);
143
+ }
144
+ async listSheets() {
145
+ return this.requireSheetsAdapter().listSheets();
146
+ }
147
+ async deleteSheet(name) {
148
+ await this.requireSheetsAdapter().deleteSheet(name);
149
+ }
150
+ }
151
+ exports.An5Adapter = An5Adapter;
152
+ // ─── Transaction-scoped adapter ────────────────────────────────────────────────────
153
+ class An5AdapterTx {
154
+ constructor(handle, dialect) {
155
+ this.handle = handle;
156
+ this.dialect = dialect;
157
+ }
158
+ async exec(query, params) {
159
+ return this.handle.exec(query, params);
160
+ }
161
+ async _executeRaw(query, params) {
162
+ return this.handle.executeRaw(query, params);
163
+ }
164
+ table(modelName) {
165
+ return new AdapterTableClient(this, modelName);
166
+ }
167
+ }
168
+ exports.An5AdapterTx = An5AdapterTx;
169
+ // ─── Table Client ──────────────────────────────────────────────────────────────────
170
+ class AdapterTableClient {
171
+ constructor(adapter, modelName) {
172
+ this.adapter = adapter;
173
+ this.modelName = modelName;
174
+ }
175
+ get dialect() { return this.adapter.dialect; }
176
+ get tableName() {
177
+ const name = this.modelName;
178
+ let t = name;
179
+ const modelToTable = (0, metadata_1.getModelToTable)();
180
+ if (modelToTable[name])
181
+ t = modelToTable[name];
182
+ else {
183
+ const camel = name.charAt(0).toLowerCase() + name.slice(1);
184
+ if (modelToTable[camel])
185
+ t = modelToTable[camel];
186
+ else {
187
+ const lower = name.toLowerCase();
188
+ if (modelToTable[lower])
189
+ t = modelToTable[lower];
190
+ }
191
+ }
192
+ if (t.startsWith('[') || t.startsWith('"') || t.startsWith('`'))
193
+ return t;
194
+ if (t.includes('.'))
195
+ return t.split('.').map(p => (0, sql_1.quote)(p, this.dialect)).join('.');
196
+ return (0, sql_1.quote)(t, this.dialect);
197
+ }
198
+ get nolock() {
199
+ return this.dialect === 'mssql' ? ' WITH (NOLOCK)' : '';
200
+ }
201
+ async doExec(query, params) {
202
+ return this.adapter.exec(query, params);
203
+ }
204
+ async doExecuteRaw(query, params) {
205
+ return this.adapter._executeRaw(query, params);
206
+ }
207
+ async findMany(args) {
208
+ const params = {};
209
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args?.where, params, this.dialect);
210
+ const orderSql = (0, sql_1.buildOrderBy)(args?.orderBy, this.dialect);
211
+ const take = args?.take;
212
+ const skip = args?.skip ?? 0;
213
+ let query;
214
+ if (take !== undefined) {
215
+ if (this.dialect === 'postgres' || this.dialect === 'mysql' || this.dialect === 'sqlite') {
216
+ query = `SELECT * FROM ${this.tableName}`;
217
+ if (whereSql)
218
+ query += ` WHERE ${whereSql}`;
219
+ if (orderSql)
220
+ query += ` ${orderSql}`;
221
+ query += ` LIMIT ${take} OFFSET ${skip}`;
222
+ }
223
+ else {
224
+ // mssql requires ORDER BY before OFFSET/FETCH
225
+ query = `SELECT * FROM ${this.tableName}${this.nolock}`;
226
+ if (whereSql)
227
+ query += ` WHERE ${whereSql}`;
228
+ query += ` ${orderSql || 'ORDER BY (SELECT NULL)'}`;
229
+ query += ` OFFSET ${skip} ROWS FETCH NEXT ${take} ROWS ONLY`;
230
+ }
231
+ }
232
+ else {
233
+ query = `SELECT * FROM ${this.tableName}${this.nolock}`;
234
+ if (whereSql)
235
+ query += ` WHERE ${whereSql}`;
236
+ if (orderSql)
237
+ query += ` ${orderSql}`;
238
+ }
239
+ return this.doExec(query, params);
240
+ }
241
+ async findFirst(args) {
242
+ const rows = await this.findMany({ ...args, take: 1 });
243
+ return rows[0] ?? null;
244
+ }
245
+ async findUnique(args) {
246
+ return this.findFirst({ where: args.where });
247
+ }
248
+ async count(args) {
249
+ const params = {};
250
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args?.where, params, this.dialect);
251
+ // Add NOLOCK for mssql count too
252
+ let query = `SELECT COUNT(*) AS cnt FROM ${this.tableName}${this.nolock}`;
253
+ if (whereSql)
254
+ query += ` WHERE ${whereSql}`;
255
+ const rows = await this.doExec(query, params);
256
+ return Number(rows[0]?.cnt ?? rows[0]?.CNT ?? 0);
257
+ }
258
+ async create(args) {
259
+ const fields = (0, metadata_1.getFieldsForModel)(this.modelName);
260
+ const idFieldName = Object.prototype.hasOwnProperty.call(fields, 'id')
261
+ ? 'id'
262
+ : Object.keys(fields).find((name) => name.endsWith('_id') || name.endsWith('Id') || name.toLowerCase() === 'id');
263
+ const data = { ...args.data };
264
+ if (idFieldName) {
265
+ const fieldDef = fields[idFieldName];
266
+ const rawType = typeof fieldDef === 'string' ? fieldDef : (fieldDef?.ts || fieldDef?.sql || fieldDef?.type || '');
267
+ const normalizedType = String(rawType).toLowerCase();
268
+ const isStringType = ['string', 'uuid', 'uniqueidentifier', 'nvarchar', 'varchar', 'text'].includes(normalizedType);
269
+ if (isStringType && !data[idFieldName])
270
+ data[idFieldName] = (0, crypto_1.randomUUID)();
271
+ }
272
+ const cols = Object.keys(data).filter(k => data[k] !== undefined);
273
+ const params = {};
274
+ const vals = [];
275
+ for (const col of cols) {
276
+ const p = `c_${col}`;
277
+ params[p] = data[col];
278
+ vals.push(`@${p}`);
279
+ }
280
+ const query = `INSERT INTO ${this.tableName} (${cols.map(c => (0, sql_1.quote)(c, this.dialect)).join(', ')}) VALUES (${vals.join(', ')})`;
281
+ await this.doExec(query, params);
282
+ return (await this.findFirst({ where: idFieldName ? { [idFieldName]: data[idFieldName] } : data }));
283
+ }
284
+ async createMany(args) {
285
+ if (args.data.length === 0)
286
+ return { count: 0 };
287
+ const firstCols = Object.keys(args.data[0]).filter(k => args.data[0][k] !== undefined);
288
+ // Bulk INSERT for the common case (same columns, no skipDuplicates)
289
+ if (firstCols.length > 0 && !args.skipDuplicates) {
290
+ const params = {};
291
+ const rowPlaceholders = [];
292
+ for (let r = 0; r < args.data.length; r++) {
293
+ const row = args.data[r];
294
+ const vals = firstCols.map(col => {
295
+ const p = `r${r}_${col}`;
296
+ params[p] = row[col] ?? null;
297
+ return `@${p}`;
298
+ });
299
+ rowPlaceholders.push(`(${vals.join(', ')})`);
300
+ }
301
+ const query = `INSERT INTO ${this.tableName} (${firstCols.map(c => (0, sql_1.quote)(c, this.dialect)).join(', ')}) VALUES ${rowPlaceholders.join(', ')}`;
302
+ try {
303
+ await this.doExecuteRaw(query, params);
304
+ return { count: args.data.length };
305
+ }
306
+ catch {
307
+ // fallback below if bulk fails (e.g. mixed column sets)
308
+ }
309
+ }
310
+ // Row-by-row fallback
311
+ let count = 0;
312
+ for (const row of args.data) {
313
+ try {
314
+ await this.create({ data: row });
315
+ count++;
316
+ }
317
+ catch (e) {
318
+ if (!args.skipDuplicates)
319
+ throw e;
320
+ }
321
+ }
322
+ return { count };
323
+ }
324
+ async update(args) {
325
+ const params = {};
326
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args.where, params, this.dialect, 'w_');
327
+ const setCols = Object.keys(args.data).filter(k => args.data[k] !== undefined);
328
+ const sets = [];
329
+ for (const col of setCols) {
330
+ const p = `s_${col}`;
331
+ params[p] = args.data[col];
332
+ sets.push(`${(0, sql_1.quote)(col, this.dialect)} = @${p}`);
333
+ }
334
+ const query = `UPDATE ${this.tableName} SET ${sets.join(', ')}${whereSql ? ` WHERE ${whereSql}` : ''}`;
335
+ await this.doExecuteRaw(query, params);
336
+ return (await this.findFirst({ where: args.where }));
337
+ }
338
+ async updateMany(args) {
339
+ const params = {};
340
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args.where, params, this.dialect, 'w_');
341
+ const setCols = Object.keys(args.data).filter(k => args.data[k] !== undefined);
342
+ const sets = [];
343
+ for (const col of setCols) {
344
+ const p = `s_${col}`;
345
+ params[p] = args.data[col];
346
+ sets.push(`${(0, sql_1.quote)(col, this.dialect)} = @${p}`);
347
+ }
348
+ const query = `UPDATE ${this.tableName} SET ${sets.join(', ')}${whereSql ? ` WHERE ${whereSql}` : ''}`;
349
+ const count = await this.doExecuteRaw(query, params);
350
+ return { count };
351
+ }
352
+ async delete(args) {
353
+ const existing = await this.findFirst({ where: args.where });
354
+ const params = {};
355
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args.where, params, this.dialect);
356
+ await this.doExecuteRaw(`DELETE FROM ${this.tableName} WHERE ${whereSql}`, params);
357
+ return existing;
358
+ }
359
+ async deleteMany(args) {
360
+ const params = {};
361
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args?.where, params, this.dialect);
362
+ const query = `DELETE FROM ${this.tableName}${whereSql ? ` WHERE ${whereSql}` : ''}`;
363
+ const count = await this.doExecuteRaw(query, params);
364
+ return { count };
365
+ }
366
+ async upsert(args) {
367
+ const existing = await this.findFirst({ where: args.where });
368
+ return existing
369
+ ? this.update({ where: args.where, data: args.update })
370
+ : this.create({ data: args.create });
371
+ }
372
+ async aggregate(args) {
373
+ const params = {};
374
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args?.where, params, this.dialect);
375
+ const aggs = [];
376
+ if (args._count)
377
+ aggs.push('COUNT(*) AS _count');
378
+ if (args._sum)
379
+ for (const f of Object.keys(args._sum))
380
+ aggs.push(`SUM(${(0, sql_1.quote)(f, this.dialect)}) AS _sum_${f}`);
381
+ if (args._avg)
382
+ for (const f of Object.keys(args._avg))
383
+ aggs.push(`AVG(${(0, sql_1.quote)(f, this.dialect)}) AS _avg_${f}`);
384
+ if (args._min)
385
+ for (const f of Object.keys(args._min))
386
+ aggs.push(`MIN(${(0, sql_1.quote)(f, this.dialect)}) AS _min_${f}`);
387
+ if (args._max)
388
+ for (const f of Object.keys(args._max))
389
+ aggs.push(`MAX(${(0, sql_1.quote)(f, this.dialect)}) AS _max_${f}`);
390
+ const query = `SELECT ${aggs.join(', ')} FROM ${this.tableName}${whereSql ? ` WHERE ${whereSql}` : ''}`;
391
+ const rows = await this.doExec(query, params);
392
+ return rows[0] ?? {};
393
+ }
394
+ async groupBy(args) {
395
+ const params = {};
396
+ const whereSql = (0, sql_1.parseWhere)(this.modelName, args?.where, params, this.dialect);
397
+ const byCols = (args.by || []).map((b) => (0, sql_1.quote)(b, this.dialect)).join(', ');
398
+ const query = `SELECT ${byCols}, COUNT(*) AS _count FROM ${this.tableName}${whereSql ? ` WHERE ${whereSql}` : ''} GROUP BY ${byCols}`;
399
+ return this.doExec(query, params);
400
+ }
401
+ async vectorSearch(args) {
402
+ const rows = await this.findMany({ where: args.where });
403
+ const vectorField = args.vectorField || 'embedding';
404
+ const metric = args.distanceMetric || 'cosine';
405
+ const scored = [];
406
+ for (const row of rows) {
407
+ const raw = row[vectorField];
408
+ if (!raw)
409
+ continue;
410
+ let vec = [];
411
+ try {
412
+ vec = typeof raw === 'string' ? JSON.parse(raw) : raw;
413
+ }
414
+ catch {
415
+ continue;
416
+ }
417
+ if (!Array.isArray(vec) || vec.length !== args.vector.length)
418
+ continue;
419
+ let dot = 0, m1 = 0, m2 = 0;
420
+ for (let i = 0; i < args.vector.length; i++) {
421
+ dot += args.vector[i] * vec[i];
422
+ m1 += args.vector[i] ** 2;
423
+ m2 += vec[i] ** 2;
424
+ }
425
+ const cosine = m1 && m2 ? dot / (Math.sqrt(m1) * Math.sqrt(m2)) : 0;
426
+ const dist = metric === 'cosine'
427
+ ? 1 - cosine
428
+ : metric === 'dot'
429
+ ? -dot
430
+ : Math.sqrt(args.vector.reduce((s, v, i) => s + (v - vec[i]) ** 2, 0));
431
+ scored.push({ row, dist });
432
+ }
433
+ scored.sort((a, b) => a.dist - b.dist);
434
+ return scored.slice(0, args.take ?? 10).map(s => ({ ...s.row, distance: s.dist }));
435
+ }
436
+ }
437
+ exports.AdapterTableClient = AdapterTableClient;
438
+ // ─── Factory ──────────────────────────────────────────────────────────────────────
439
+ function createAn5Adapter(config) {
440
+ if (isSheetsConfig(config))
441
+ return new googlesheets_1.An5SheetsAdapter(config);
442
+ if (config.connectionString.trim().startsWith('googlesheets://')) {
443
+ return new googlesheets_1.An5SheetsAdapter((0, parseConnectionString_1.parseSheetsConnectionString)(config.connectionString));
444
+ }
445
+ return new An5Adapter(config);
446
+ }
447
+ exports.createAdapter = createAn5Adapter;