@an5/adapters 0.2.10 → 0.2.13
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 +163 -2
- package/dist/an5Adapter.d.ts +37 -0
- package/dist/an5Adapter.js +190 -65
- package/dist/base/metadata.d.ts +2 -0
- package/dist/base/metadata.js +10 -0
- package/dist/base/sql.d.ts +2 -0
- package/dist/base/sql.js +24 -25
- package/dist/base/types.d.ts +14 -0
- package/dist/googlesheets/adapter.js +16 -8
- package/dist/googlesheets/config.d.ts +13 -0
- package/dist/googlesheets/config.js +5 -1
- package/dist/googlesheets/oauth.d.ts +3 -0
- package/dist/googlesheets/oauth.js +43 -0
- package/dist/googlesheets/parseConnectionString.js +12 -0
- package/dist/googlesheets/tableClient.js +5 -23
- package/dist/index.d.ts +2 -0
- package/dist/index.js +8 -1
- package/dist/sqlite/browserEngine.d.ts +13 -0
- package/dist/sqlite/browserEngine.js +13 -0
- package/dist/sqlite/engine.d.ts +5 -0
- package/dist/sqlite/engine.js +10 -0
- package/dist/sqlite/index.d.ts +1 -0
- package/dist/sqlite/index.js +1 -0
- package/dist/sqlite/vector.d.ts +155 -0
- package/dist/sqlite/vector.js +436 -0
- package/dotnet/An5Adapter.cs +85 -27
- package/dotnet/Base/Core.cs +14 -0
- package/dotnet/Mssql/MssqlEngine.cs +44 -1
- package/dotnet/Postgres/PostgresEngine.cs +2 -0
- package/dotnet/Sqlite/SqliteEngine.cs +82 -2
- package/dotnet/Sqlite/SqliteVectors.cs +401 -0
- package/golang/an5_adapter.go +84 -32
- package/golang/base/vectors.go +263 -0
- package/golang/sqlite/vector.go +248 -0
- package/golang/sqlite_vector_test.go +288 -0
- package/golang/table_client.go +67 -3
- package/java/pom.xml +177 -0
- package/java/src/main/java/an5/adapters/An5Adapter.java +374 -0
- package/java/src/main/java/an5/adapters/An5Aggregate.java +116 -0
- package/java/src/main/java/an5/adapters/An5GroupBy.java +97 -0
- package/java/src/main/java/an5/adapters/An5Query.java +100 -0
- package/java/src/main/java/an5/adapters/An5TableClient.java +1031 -0
- package/java/src/main/java/an5/adapters/An5ViewClient.java +107 -0
- package/java/src/main/java/an5/adapters/base/Dialect.java +103 -0
- package/java/src/main/java/an5/adapters/base/FieldMeta.java +42 -0
- package/java/src/main/java/an5/adapters/base/JdbcUrls.java +201 -0
- package/java/src/main/java/an5/adapters/base/Metadata.java +195 -0
- package/java/src/main/java/an5/adapters/base/RelationDef.java +26 -0
- package/java/src/main/java/an5/adapters/base/SqlBuilder.java +422 -0
- package/java/src/main/java/an5/adapters/base/SqliteVectors.java +375 -0
- package/java/src/main/java/an5/adapters/base/Vectors.java +123 -0
- package/java/src/test/java/An5AdaptersSmoke.java +733 -0
- package/java/src/test/java/Json.java +174 -0
- package/java/src/test/java/QuerySemantics.java +111 -0
- package/kotlin/build.gradle.kts +145 -0
- package/kotlin/settings.gradle.kts +10 -0
- package/kotlin/src/main/kotlin/an5/adapters/Aggregate.kt +89 -0
- package/kotlin/src/main/kotlin/an5/adapters/An5.kt +91 -0
- package/kotlin/src/main/kotlin/an5/adapters/Query.kt +130 -0
- package/kotlin/src/main/kotlin/an5/adapters/Rows.kt +166 -0
- package/kotlin/src/main/kotlin/an5/adapters/TableClient.kt +169 -0
- package/kotlin/src/main/kotlin/an5/adapters/Types.kt +77 -0
- package/kotlin/src/test/kotlin/an5/adapters/An5Smoke.kt +329 -0
- package/native/sqlite/CMakeLists.txt +12 -0
- package/native/sqlite/README.md +117 -0
- package/native/sqlite/an5_vector.c +209 -0
- package/package.json +40 -9
- package/python/an5_adapter.py +37 -6
- package/python/base/__init__.py +15 -0
- package/python/base/vectors.py +434 -0
- package/python/sqlite/__init__.py +2 -2
- package/python/sqlite/provider.py +21 -1
- package/python/table_client.py +129 -59
- package/rust/src/adapter.rs +342 -24
- package/rust/src/base/mod.rs +7 -0
- package/rust/src/base/vectors.rs +293 -0
- package/rust/src/lib.rs +3 -3
- package/scripts/sqlite-native-build.js +32 -0
- package/swift/Package.swift +22 -0
- package/swift/Sources/An5Adapters/An5Adapter.swift +160 -0
- package/swift/Sources/An5Adapters/Metadata.swift +183 -0
- package/swift/Sources/An5Adapters/Query.swift +105 -0
- package/swift/Sources/An5Adapters/Row+Accessors.swift +129 -0
- package/swift/Sources/An5Adapters/SQLBuilder.swift +359 -0
- package/swift/Sources/An5Adapters/SQLiteDriver.swift +301 -0
- package/swift/Sources/An5Adapters/SqliteVectors.swift +243 -0
- package/swift/Sources/An5Adapters/TableClient.swift +671 -0
- package/swift/Sources/An5Adapters/Types.swift +127 -0
- package/swift/Sources/An5Adapters/Vectors.swift +86 -0
- package/swift/Sources/An5Adapters/ViewClient.swift +50 -0
- package/swift/Sources/CSQLite/module.modulemap +8 -0
- package/swift/Sources/CSQLite/shim.h +9 -0
- package/swift/Tests/An5AdaptersTests/QuerySemanticsTests.swift +51 -0
- package/swift/Tests/An5AdaptersTests/RuntimeSmokeTests.swift +488 -0
- package/typescript/src/an5Adapter.ts +200 -58
- package/typescript/src/base/metadata.ts +8 -0
- package/typescript/src/base/sql.ts +23 -16
- package/typescript/src/base/types.ts +15 -0
- package/typescript/src/googlesheets/adapter.ts +17 -8
- package/typescript/src/googlesheets/config.ts +10 -1
- package/typescript/src/googlesheets/oauth.ts +31 -0
- package/typescript/src/googlesheets/parseConnectionString.ts +8 -0
- package/typescript/src/googlesheets/tableClient.ts +5 -19
- package/typescript/src/index.ts +13 -0
- package/typescript/src/sqlite/browserEngine.ts +24 -0
- package/typescript/src/sqlite/engine.ts +13 -0
- package/typescript/src/sqlite/index.ts +1 -1
- package/typescript/src/sqlite/vector.ts +541 -0
- package/golang/sqlite/sqlite.go +0 -14
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { An5SheetsAdapterConfig } from './config';
|
|
2
|
+
|
|
3
|
+
/** Deduplicate refresh requests within an adapter; do not expose token responses. */
|
|
4
|
+
export function sheetsTokenProvider(config: An5SheetsAdapterConfig) {
|
|
5
|
+
let token = config.accessToken;
|
|
6
|
+
let expiresAt = config.tokenExpiresAt;
|
|
7
|
+
let pending: Promise<string> | undefined;
|
|
8
|
+
return async (force = false): Promise<string | undefined> => {
|
|
9
|
+
if (token && !force && (expiresAt === undefined || expiresAt > Date.now() + 60000)) return token;
|
|
10
|
+
if (!config.refreshToken || !config.oauthClientId) {
|
|
11
|
+
if (force || expiresAt !== undefined) throw new Error('Google access expired. Reconnect your Google account.');
|
|
12
|
+
return token;
|
|
13
|
+
}
|
|
14
|
+
if (!pending) pending = (async () => {
|
|
15
|
+
try {
|
|
16
|
+
const response = await fetch('https://oauth2.googleapis.com/token', {
|
|
17
|
+
method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
18
|
+
body: new URLSearchParams({ grant_type: 'refresh_token', client_id: config.oauthClientId!, refresh_token: config.refreshToken!, ...(config.oauthClientSecret ? { client_secret: config.oauthClientSecret } : {}) }),
|
|
19
|
+
signal: AbortSignal.timeout(20000),
|
|
20
|
+
});
|
|
21
|
+
if (!response.ok) throw new Error();
|
|
22
|
+
const result = await response.json() as { access_token?: string; expires_in?: number };
|
|
23
|
+
if (!result.access_token || typeof result.access_token !== 'string' || typeof result.expires_in !== 'number' || !Number.isFinite(result.expires_in) || result.expires_in <= 0) throw new Error();
|
|
24
|
+
token = result.access_token; expiresAt = Date.now() + result.expires_in * 1000;
|
|
25
|
+
return token;
|
|
26
|
+
} catch { throw new Error('Google access expired or was revoked. Reconnect your Google account.'); }
|
|
27
|
+
finally { pending = undefined; }
|
|
28
|
+
})();
|
|
29
|
+
return pending;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -33,6 +33,14 @@ export function parseSheetsConnectionString(url: string): An5SheetsAdapterConfig
|
|
|
33
33
|
if (config.clientemail) result.clientEmail = config.clientemail;
|
|
34
34
|
if (config.privatekey) result.privateKey = config.privatekey;
|
|
35
35
|
if (config.accesstoken) result.accessToken = config.accesstoken;
|
|
36
|
+
if (config.refreshtoken) result.refreshToken = config.refreshtoken;
|
|
37
|
+
if (config.oauthclientid) result.oauthClientId = config.oauthclientid;
|
|
38
|
+
if (config.oauthclientsecret) result.oauthClientSecret = config.oauthclientsecret;
|
|
39
|
+
if (config.tokenexpiresat) {
|
|
40
|
+
const expiry = Number(config.tokenexpiresat);
|
|
41
|
+
if (!Number.isFinite(expiry) || expiry < 0) throw new Error('Invalid OAuth token expiry.');
|
|
42
|
+
result.tokenExpiresAt = expiry;
|
|
43
|
+
}
|
|
36
44
|
if (config.apikey) result.apiKey = config.apikey;
|
|
37
45
|
|
|
38
46
|
if (config.sheetmapping) {
|
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { generateUUID } from '../base/uuid';
|
|
2
|
-
import { getFieldsForModel } from '../base/metadata';
|
|
2
|
+
import { getFieldsForModel, resolveIdField } from '../base/metadata';
|
|
3
3
|
import type { An5SheetsAdapter } from './adapter';
|
|
4
4
|
import { buildOrderBy, coerceCell, esc, matchWhere, resolveSheetName, sortRows } from './helpers';
|
|
5
5
|
import { withRetry } from './retry';
|
|
6
6
|
|
|
7
7
|
// ─── Utility ──────────────────────────────────────────────────────────────────
|
|
8
8
|
|
|
9
|
-
function resolveIdField(fields: Record<string, any>): string | undefined {
|
|
10
|
-
if (Object.prototype.hasOwnProperty.call(fields, 'id')) return 'id';
|
|
11
|
-
return Object.keys(fields).find(
|
|
12
|
-
name => name.endsWith('_id') || name.endsWith('Id') || name.toLowerCase() === 'id'
|
|
13
|
-
);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
9
|
// ─── Table Client ─────────────────────────────────────────────────────────────
|
|
17
10
|
|
|
18
11
|
export class SheetsTableClient<T = any> {
|
|
@@ -227,17 +220,10 @@ export class SheetsTableClient<T = any> {
|
|
|
227
220
|
return this.rowToValues(data, headers);
|
|
228
221
|
});
|
|
229
222
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
if (!args.skipDuplicates) throw e;
|
|
235
|
-
let count = 0;
|
|
236
|
-
for (const row of args.data) {
|
|
237
|
-
try { await this.create({ data: row }); count++; } catch { /* skip */ }
|
|
238
|
-
}
|
|
239
|
-
return { count };
|
|
240
|
-
}
|
|
223
|
+
// Sheets has no unique constraints. An append failure must propagate;
|
|
224
|
+
// retrying individual rows could duplicate a batch the service accepted.
|
|
225
|
+
await this.adapter.appendRange(`${this.escSheetName}!A:A`, batchValues);
|
|
226
|
+
return { count: batchValues.length };
|
|
241
227
|
}
|
|
242
228
|
|
|
243
229
|
async update(args: { where: any; data: Partial<T> }): Promise<T> {
|
package/typescript/src/index.ts
CHANGED
|
@@ -29,6 +29,19 @@ export type {
|
|
|
29
29
|
AdapterMetadata,
|
|
30
30
|
Dialect,
|
|
31
31
|
} from './an5Adapter';
|
|
32
|
+
export {
|
|
33
|
+
encodeVector,
|
|
34
|
+
decodeVector,
|
|
35
|
+
vectorDistance,
|
|
36
|
+
isVectorField,
|
|
37
|
+
AN5_VECTOR_FUNCTIONS,
|
|
38
|
+
SQLITE_VEC_FUNCTIONS,
|
|
39
|
+
} from './sqlite/vector';
|
|
40
|
+
export type {
|
|
41
|
+
SqliteVectorStrategy,
|
|
42
|
+
VectorMetric,
|
|
43
|
+
VectorStrategyPreference,
|
|
44
|
+
} from './sqlite/vector';
|
|
32
45
|
export type { TypedAn5Adapter, AdapterAPI } from './typed';
|
|
33
46
|
export {
|
|
34
47
|
getLlmConfig, setLlmConfig,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Dialect, QueryEngine, TransactionHandle } from '../base/types';
|
|
2
|
+
import { SqliteVectorSupport } from './vector';
|
|
2
3
|
|
|
3
4
|
// ─── Browser SQLite Driver & Config ──────────────────────────────────────────────────
|
|
4
5
|
|
|
@@ -13,18 +14,41 @@ export interface SqliteBrowserConfig {
|
|
|
13
14
|
driver?: SqliteDriver;
|
|
14
15
|
exec?: <T = any>(query: string, params?: Record<string, any>) => Promise<T[]> | T[];
|
|
15
16
|
executeRaw?: (query: string, params?: Record<string, any>) => Promise<number> | number;
|
|
17
|
+
/** Path to the sqlite-vec extension, for WASM builds that allow loading one. */
|
|
18
|
+
sqliteVec?: string;
|
|
19
|
+
/** Pin the vector search strategy instead of probing for one. */
|
|
20
|
+
vectorStrategy?: 'auto' | 'sqlite-vec' | 'udf' | 'sql' | 'memory';
|
|
21
|
+
/** Register a scalar function with the driver, when its API allows it. */
|
|
22
|
+
registerFunction?: (name: string, fn: (...args: any[]) => any) => void;
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
// ─── Browser SQLite Engine ──────────────────────────────────────────────────────────
|
|
19
26
|
|
|
20
27
|
export class SqliteBrowserEngine implements QueryEngine {
|
|
21
28
|
dialect: Dialect = 'sqlite';
|
|
29
|
+
/**
|
|
30
|
+
* A WASM build cannot register a JS callback as an SQL function, so this only
|
|
31
|
+
* reaches the `sql` strategy (json_each in plain SQL) and the in-memory one.
|
|
32
|
+
* A custom `registerFunction` hook unlocks `udf` as well.
|
|
33
|
+
*/
|
|
34
|
+
readonly vectorSupport: SqliteVectorSupport;
|
|
22
35
|
private db: any = null;
|
|
23
36
|
private driver: SqliteDriver | null = null;
|
|
24
37
|
private customExec: ((query: string, params?: Record<string, any>) => Promise<any[]> | any[]) | null = null;
|
|
25
38
|
private customExecuteRaw: ((query: string, params?: Record<string, any>) => Promise<number> | number) | null = null;
|
|
26
39
|
|
|
27
40
|
constructor(config?: SqliteBrowserConfig | any) {
|
|
41
|
+
this.vectorSupport = new SqliteVectorSupport({
|
|
42
|
+
native: () => this.db,
|
|
43
|
+
registerFunction: (name, fn) => {
|
|
44
|
+
const register = config?.registerFunction;
|
|
45
|
+
if (typeof register === 'function') return register(name, fn);
|
|
46
|
+
const db = this.db;
|
|
47
|
+
if (db && typeof db.create_function === 'function') return db.create_function(name, fn);
|
|
48
|
+
throw new Error('this SQLite build cannot register functions');
|
|
49
|
+
},
|
|
50
|
+
}, { sqliteVec: config?.sqliteVec, vectorStrategy: config?.vectorStrategy });
|
|
51
|
+
|
|
28
52
|
if (!config) return;
|
|
29
53
|
|
|
30
54
|
if (config.exec && typeof config.exec === 'function') {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { An5AdapterConfig, Dialect, QueryEngine, TransactionHandle } from '../base/types';
|
|
2
|
+
import { SqliteVectorSupport } from './vector';
|
|
2
3
|
|
|
3
4
|
// ─── SQLite Engine ─────────────────────────────────────────────────────────────────
|
|
4
5
|
|
|
@@ -7,6 +8,8 @@ try { BetterSqlite3 = require('better-sqlite3'); } catch { }
|
|
|
7
8
|
|
|
8
9
|
export class SqliteEngine implements QueryEngine {
|
|
9
10
|
dialect: Dialect = 'sqlite';
|
|
11
|
+
/** Registers `an5_vec_*` and loads sqlite-vec for in-database vector search. */
|
|
12
|
+
readonly vectorSupport: SqliteVectorSupport;
|
|
10
13
|
private db: any = null;
|
|
11
14
|
private readonly filePath: string;
|
|
12
15
|
|
|
@@ -16,6 +19,15 @@ export class SqliteEngine implements QueryEngine {
|
|
|
16
19
|
.replace(/^sqlite:\/\/\//i, '/')
|
|
17
20
|
.replace(/^sqlite:\/\//i, '')
|
|
18
21
|
.replace(/^sqlite:/i, '');
|
|
22
|
+
this.vectorSupport = new SqliteVectorSupport({
|
|
23
|
+
native: () => this.getDb(),
|
|
24
|
+
registerFunction: (name, fn) => this.getDb().function(name, { deterministic: true }, fn),
|
|
25
|
+
}, { sqliteVec: adapterConfig.sqliteVec, vectorStrategy: adapterConfig.vectorStrategy });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The better-sqlite3 handle, needed for extension loading. */
|
|
29
|
+
native(): any {
|
|
30
|
+
return this.getDb();
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
private getDb(): any {
|
|
@@ -42,6 +54,7 @@ export class SqliteEngine implements QueryEngine {
|
|
|
42
54
|
|
|
43
55
|
async disconnect(): Promise<void> {
|
|
44
56
|
if (this.db) { this.db.close(); this.db = null; }
|
|
57
|
+
this.vectorSupport.reset();
|
|
45
58
|
}
|
|
46
59
|
|
|
47
60
|
async beginTransaction(): Promise<TransactionHandle> {
|