@pineliner/odb-client 1.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.
@@ -0,0 +1,151 @@
1
+ /**
2
+ * Service Client - High-level client for managing tenant databases via ODB-Lite Tenant API
3
+ *
4
+ * This client provides automatic database provisioning and management for multi-tenant applications.
5
+ * It handles:
6
+ * - Automatic database creation on first use
7
+ * - Database hash caching for performance
8
+ * - Tenant API integration with ODB-Lite
9
+ * - Query execution via ODB-Lite's query API
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * const service = new ServiceClient({
14
+ * baseUrl: 'http://localhost:8671',
15
+ * apiKey: 'odblite_tenant_key'
16
+ * });
17
+ *
18
+ * // Automatically creates database if it doesn't exist
19
+ * const dbHash = await service.ensureDatabaseForTenant('wallet', 'tenant-123');
20
+ *
21
+ * // Execute queries
22
+ * const result = await service.query(dbHash, 'SELECT * FROM wallets', []);
23
+ * ```
24
+ */
25
+ export interface ODBLiteDatabase {
26
+ hash: string;
27
+ name: string;
28
+ tenantId: string;
29
+ nodeId: string;
30
+ createdAt: string;
31
+ updatedAt: string;
32
+ }
33
+ export interface ODBLiteNode {
34
+ nodeId: string;
35
+ url: string;
36
+ status: string;
37
+ lastHealthCheck: string;
38
+ }
39
+ export interface ServiceClientConfig {
40
+ /** Base URL of ODB-Lite server (e.g., http://localhost:8671) */
41
+ baseUrl: string;
42
+ /** Tenant API key for authentication */
43
+ apiKey: string;
44
+ }
45
+ /**
46
+ * Service Client for managing tenant databases in ODB-Lite
47
+ *
48
+ * This is a higher-level client that sits on top of the base ODB client.
49
+ * It provides automatic database provisioning and management for services
50
+ * that need per-tenant database isolation.
51
+ */
52
+ export declare class ServiceClient {
53
+ private apiUrl;
54
+ private apiKey;
55
+ private databaseCache;
56
+ constructor(config: ServiceClientConfig);
57
+ /**
58
+ * Get or create a database for a tenant
59
+ *
60
+ * This is the main method used by services. It will:
61
+ * 1. Check the cache for an existing database hash
62
+ * 2. Query ODB-Lite to see if the database exists
63
+ * 3. Create the database if it doesn't exist
64
+ * 4. Cache and return the database hash
65
+ *
66
+ * @param prefix - Database name prefix (e.g., 'wallet', 'tracking')
67
+ * @param tenantId - Tenant identifier
68
+ * @returns Database hash for querying
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const hash = await service.ensureDatabaseForTenant('wallet', 'tenant-123');
73
+ * // Returns hash for database named 'wallet_tenant-123'
74
+ * ```
75
+ */
76
+ ensureDatabaseForTenant(prefix: string, tenantId: string): Promise<string>;
77
+ /**
78
+ * List all databases owned by this tenant
79
+ *
80
+ * Queries ODB-Lite's tenant API to get all databases accessible with the current API key.
81
+ *
82
+ * @returns Array of database objects
83
+ */
84
+ listDatabases(): Promise<ODBLiteDatabase[]>;
85
+ /**
86
+ * Create a new database
87
+ *
88
+ * @param name - Database name (should be unique)
89
+ * @param nodeId - ID of the node to host the database
90
+ * @returns Created database object with hash
91
+ */
92
+ createDatabase(name: string, nodeId: string): Promise<ODBLiteDatabase>;
93
+ /**
94
+ * Get database details by hash
95
+ *
96
+ * @param hash - Database hash
97
+ * @returns Database object
98
+ */
99
+ getDatabase(hash: string): Promise<ODBLiteDatabase>;
100
+ /**
101
+ * Delete a database
102
+ *
103
+ * @param hash - Database hash to delete
104
+ */
105
+ deleteDatabase(hash: string): Promise<void>;
106
+ /**
107
+ * List available nodes
108
+ *
109
+ * @returns Array of node objects
110
+ */
111
+ listNodes(): Promise<ODBLiteNode[]>;
112
+ /**
113
+ * Execute a query on a specific database
114
+ *
115
+ * This is a low-level query method. For most use cases, you should use
116
+ * the full ODB client (`odblite()` function) instead, which provides
117
+ * template tag support and better ergonomics.
118
+ *
119
+ * @param databaseHash - Hash of the database to query
120
+ * @param sql - SQL query string
121
+ * @param params - Query parameters
122
+ * @returns Query result
123
+ */
124
+ query<T = any>(databaseHash: string, sql: string, params?: any[]): Promise<T>;
125
+ /**
126
+ * Clear the database hash cache
127
+ *
128
+ * Useful when database mappings have changed or for testing.
129
+ */
130
+ clearCache(): void;
131
+ /**
132
+ * Get cached database hash for a specific tenant (if exists)
133
+ *
134
+ * @param prefix - Database name prefix
135
+ * @param tenantId - Tenant identifier
136
+ * @returns Cached hash or undefined
137
+ */
138
+ getCachedHash(prefix: string, tenantId: string): string | undefined;
139
+ /**
140
+ * Pre-cache a database hash
141
+ *
142
+ * Useful when you know the mapping ahead of time and want to avoid
143
+ * the initial lookup.
144
+ *
145
+ * @param prefix - Database name prefix
146
+ * @param tenantId - Tenant identifier
147
+ * @param hash - Database hash
148
+ */
149
+ setCachedHash(prefix: string, tenantId: string, hash: string): void;
150
+ }
151
+ //# sourceMappingURL=service-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-client.d.ts","sourceRoot":"","sources":["../../src/service/service-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;GAMG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,aAAa,CAAsB;gBAE/B,MAAM,EAAE,mBAAmB;IAMvC;;;;;;;;;;;;;;;;;;OAkBG;IACG,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkDhF;;;;;;OAMG;IACG,aAAa,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAejD;;;;;;OAMG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAkB5E;;;;;OAKG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAezD;;;;OAIG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBjD;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAezC;;;;;;;;;;;OAWG;IACG,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,EAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAkBvF;;;;OAIG;IACH,UAAU,IAAI,IAAI;IAIlB;;;;;;OAMG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKnE;;;;;;;;;OASG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;CAIpE"}
@@ -0,0 +1,75 @@
1
+ export interface ODBLiteConfig {
2
+ baseUrl: string;
3
+ apiKey: string;
4
+ databaseId?: string;
5
+ timeout?: number;
6
+ retries?: number;
7
+ }
8
+ export interface QueryResult<T = any> {
9
+ rows: T[];
10
+ rowsAffected: number;
11
+ executionTime: number;
12
+ databaseName?: string;
13
+ }
14
+ export interface ODBLiteResponse<T = any> {
15
+ success: boolean;
16
+ data?: T[];
17
+ rows?: T[];
18
+ rowsAffected?: number;
19
+ executionTime?: number;
20
+ dbId?: string;
21
+ databaseName?: string;
22
+ error?: string;
23
+ }
24
+ export interface SQLFragment {
25
+ text: string;
26
+ values: any[];
27
+ }
28
+ export interface PreparedQuery {
29
+ sql: string;
30
+ params: any[];
31
+ }
32
+ export interface Transaction {
33
+ <T = any>(sql: TemplateStringsArray, ...values: any[]): Promise<QueryResult<T>>;
34
+ <T = any>(input: any): SQLFragment;
35
+ raw(text: string): string;
36
+ identifier(name: string): string;
37
+ query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
38
+ execute<T = any>(sql: string | {
39
+ sql: string;
40
+ args?: any[];
41
+ }, args?: any[]): Promise<QueryResult<T>>;
42
+ savepoint<T = any>(callback: (sql: Transaction) => Promise<T>): Promise<T>;
43
+ rollback(): Promise<void>;
44
+ commit(): Promise<void>;
45
+ }
46
+ export type TransactionCallback<T = any> = (sql: Transaction) => Promise<T> | T | Promise<T[]> | T[];
47
+ export type TransactionMode = 'read write' | 'read only' | string;
48
+ export interface ODBLiteConnection {
49
+ sql<T = any>(sql: TemplateStringsArray, ...values: any[]): Promise<QueryResult<T>>;
50
+ query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
51
+ begin(): Promise<Transaction>;
52
+ ping(): Promise<boolean>;
53
+ end(): Promise<void>;
54
+ config: ODBLiteConfig;
55
+ }
56
+ export type PrimitiveType = string | number | boolean | null | Date | Buffer;
57
+ export type QueryParameter = PrimitiveType | PrimitiveType[];
58
+ export interface Row {
59
+ [column: string]: any;
60
+ }
61
+ export declare class ODBLiteError extends Error {
62
+ code?: string | undefined;
63
+ query?: string | undefined;
64
+ params?: any[] | undefined;
65
+ constructor(message: string, code?: string | undefined, query?: string | undefined, params?: any[] | undefined);
66
+ }
67
+ export declare class ConnectionError extends ODBLiteError {
68
+ originalError?: Error | undefined;
69
+ constructor(message: string, originalError?: Error | undefined);
70
+ }
71
+ export declare class QueryError extends ODBLiteError {
72
+ originalError?: Error | undefined;
73
+ constructor(message: string, query?: string, params?: any[], originalError?: Error | undefined);
74
+ }
75
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "@pineliner/odb-client",
3
+ "version": "1.0.0",
4
+ "description": "Isomorphic client for ODB-Lite with postgres.js-like template string SQL support",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "type": "module",
9
+ "scripts": {
10
+ "dev": "rslib build --watch",
11
+ "build": "rslib build",
12
+ "test": "bun test",
13
+ "typecheck": "tsc --noEmit",
14
+ "deploy": "bun run build && npm publish --access public"
15
+ },
16
+ "dependencies": {
17
+ "@libsql/client": "^0.15.15"
18
+ },
19
+ "devDependencies": {
20
+ "@types/bun": "latest",
21
+ "@rslib/core": "^0.0.14",
22
+ "typescript": "^5.9.2"
23
+ },
24
+ "exports": {
25
+ ".": {
26
+ "import": "./dist/index.js",
27
+ "require": "./dist/index.cjs",
28
+ "types": "./dist/index.d.ts"
29
+ }
30
+ },
31
+ "files": [
32
+ "dist/**/*",
33
+ "src/**/*"
34
+ ],
35
+ "keywords": [
36
+ "libsql",
37
+ "sqlite",
38
+ "sql",
39
+ "database",
40
+ "client",
41
+ "template-strings",
42
+ "postgres-like"
43
+ ]
44
+ }
@@ -0,0 +1,277 @@
1
+ import type { ODBLiteConfig, ODBLiteConnection, QueryResult, Transaction, TransactionCallback, TransactionMode, SQLFragment } from '../types.ts';
2
+ import { HTTPClient } from './http-client.ts';
3
+ import { SQLParser } from './sql-parser.ts';
4
+ import { createSimpleTransaction, SimpleTransaction } from './transaction.ts';
5
+ import { ConnectionError } from '../types.ts';
6
+
7
+ // Define the callable SQL function type
8
+ interface SQLFunction {
9
+ // Primary call signatures - context-aware like postgres.js
10
+ <T = any>(sql: TemplateStringsArray, ...values: any[]): Promise<QueryResult<T>>;
11
+ <T = any>(input: any): SQLFragment; // For sql(object), sql(array), etc.
12
+
13
+ // Keep minimal utility methods for edge cases
14
+ raw(text: string): string;
15
+ identifier(name: string): string;
16
+
17
+ // Additional postgres.js-like methods
18
+ query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
19
+
20
+ // libsql-compatible execute method (for backward compatibility)
21
+ execute<T = any>(sql: string | { sql: string; args?: any[] }, args?: any[]): Promise<QueryResult<T>>;
22
+
23
+ // Transaction support - multiple overloads like postgres.js
24
+ begin(): Promise<Transaction>;
25
+ begin<T>(callback: TransactionCallback<T>): Promise<T>;
26
+ begin<T>(mode: TransactionMode, callback: TransactionCallback<T>): Promise<T>;
27
+
28
+ ping(): Promise<boolean>;
29
+ end(): Promise<void>;
30
+ setDatabase(databaseId: string): SQLFunction;
31
+ getDatabaseInfo(): Promise<any>;
32
+ configure(updates: Partial<ODBLiteConfig>): SQLFunction;
33
+ }
34
+
35
+ /**
36
+ * Main ODBLite client that provides postgres.js-like interface
37
+ */
38
+ export class ODBLiteClient implements ODBLiteConnection {
39
+ private httpClient: HTTPClient;
40
+ public config: ODBLiteConfig;
41
+ public sql: SQLFunction;
42
+
43
+ constructor(config: ODBLiteConfig) {
44
+ this.config = config;
45
+ this.httpClient = new HTTPClient(config);
46
+
47
+ // Create the callable sql function with attached utility methods
48
+ const sqlFunction = <T = any>(sql: TemplateStringsArray | any, ...values: any[]): Promise<QueryResult<T>> | SQLFragment => {
49
+ // Handle template string queries (returns Promise)
50
+ if (Array.isArray(sql) && (('raw' in sql) || (typeof sql[0] === 'string' && values.length >= 0))) {
51
+ const parsed = SQLParser.parse(sql, values);
52
+ return this.httpClient.query<T>(parsed.sql, parsed.params);
53
+ }
54
+
55
+ // Handle direct object/array inputs (returns SQLFragment for composing)
56
+ const parsed = SQLParser.parse(sql, values);
57
+ return {
58
+ text: parsed.sql,
59
+ values: parsed.params
60
+ };
61
+ };
62
+
63
+ // Attach minimal utility methods to the function
64
+ sqlFunction.raw = (text: string): string => SQLParser.raw(text);
65
+ sqlFunction.identifier = (name: string): string => SQLParser.identifier(name);
66
+
67
+ // Attach client methods to the function
68
+ sqlFunction.query = async <T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> => {
69
+ return await this.httpClient.query<T>(sql, params);
70
+ };
71
+
72
+ // libsql-compatible execute method (for backward compatibility)
73
+ sqlFunction.execute = async <T = any>(sql: string | { sql: string; args?: any[] }, args?: any[]): Promise<QueryResult<T>> => {
74
+ if (typeof sql === 'string') {
75
+ return await this.httpClient.query<T>(sql, args || []);
76
+ } else {
77
+ return await this.httpClient.query<T>(sql.sql, sql.args || []);
78
+ }
79
+ };
80
+
81
+ // Enhanced begin method with callback support
82
+ sqlFunction.begin = async <T = any>(
83
+ modeOrCallback?: TransactionMode | TransactionCallback<T>,
84
+ callback?: TransactionCallback<T>
85
+ ): Promise<Transaction | T> => {
86
+ // Determine if this is callback-style or traditional
87
+ if (typeof modeOrCallback === 'function') {
88
+ // begin(callback)
89
+ return this.executeTransactionWithCallback(modeOrCallback);
90
+ } else if (typeof modeOrCallback === 'string' && callback) {
91
+ // begin(mode, callback)
92
+ return this.executeTransactionWithCallback(callback, modeOrCallback);
93
+ } else {
94
+ // begin() - traditional style
95
+ return createSimpleTransaction(this.httpClient);
96
+ }
97
+ };
98
+ sqlFunction.ping = async (): Promise<boolean> => {
99
+ return await this.httpClient.ping();
100
+ };
101
+ sqlFunction.end = async (): Promise<void> => {
102
+ // No-op for HTTP-based client
103
+ };
104
+ sqlFunction.setDatabase = (databaseId: string): SQLFunction => {
105
+ this.httpClient.setDatabase(databaseId);
106
+ this.config.databaseId = databaseId;
107
+ return sqlFunction as SQLFunction;
108
+ };
109
+ sqlFunction.getDatabaseInfo = async (): Promise<any> => {
110
+ return await this.httpClient.getDatabaseInfo();
111
+ };
112
+ sqlFunction.configure = (updates: Partial<ODBLiteConfig>): SQLFunction => {
113
+ const newConfig = { ...this.config, ...updates };
114
+ return new ODBLiteClient(newConfig).sql;
115
+ };
116
+
117
+ this.sql = sqlFunction as SQLFunction;
118
+ }
119
+
120
+ /**
121
+ * Execute a transaction with callback (postgres.js style)
122
+ */
123
+ private async executeTransactionWithCallback<T>(
124
+ callback: TransactionCallback<T>,
125
+ mode?: TransactionMode
126
+ ): Promise<T> {
127
+ const tx = createSimpleTransaction(this.httpClient);
128
+
129
+ try {
130
+ // Execute the callback with the transaction
131
+ const result = await callback(tx);
132
+
133
+ // Commit the transaction
134
+ await tx.commit();
135
+
136
+ return result as T;
137
+ } catch (error) {
138
+ // Rollback on any error
139
+ await tx.rollback();
140
+ throw error;
141
+ }
142
+ }
143
+
144
+
145
+ /**
146
+ * Raw query method
147
+ * Usage: client.query('SELECT * FROM users WHERE id = ?', [123])
148
+ */
149
+ async query<T = any>(sql: string, params: any[] = []): Promise<QueryResult<T>> {
150
+ return await this.httpClient.query<T>(sql, params);
151
+ }
152
+
153
+ /**
154
+ * Begin a transaction
155
+ * Note: Uses simple transaction model suitable for HTTP-based access
156
+ */
157
+ async begin(): Promise<Transaction> {
158
+ return createSimpleTransaction(this.httpClient);
159
+ }
160
+
161
+ /**
162
+ * Health check
163
+ */
164
+ async ping(): Promise<boolean> {
165
+ return await this.httpClient.ping();
166
+ }
167
+
168
+ /**
169
+ * Close connection (no-op for HTTP client)
170
+ */
171
+ async end(): Promise<void> {
172
+ // No-op for HTTP-based client
173
+ }
174
+
175
+ /**
176
+ * Set the database ID for queries
177
+ */
178
+ setDatabase(databaseId: string): this {
179
+ this.httpClient.setDatabase(databaseId);
180
+ this.config.databaseId = databaseId;
181
+ return this;
182
+ }
183
+
184
+ /**
185
+ * Get database information
186
+ */
187
+ async getDatabaseInfo(): Promise<any> {
188
+ return await this.httpClient.getDatabaseInfo();
189
+ }
190
+
191
+ /**
192
+ * Create a new client instance with updated configuration
193
+ */
194
+ configure(updates: Partial<ODBLiteConfig>): ODBLiteClient {
195
+ const newConfig = { ...this.config, ...updates };
196
+ return new ODBLiteClient(newConfig);
197
+ }
198
+
199
+
200
+ /**
201
+ * Create raw SQL that won't be parameterized
202
+ * Usage: sql`SELECT * FROM ${raw('users')}`
203
+ */
204
+ static raw(text: string): string {
205
+ return SQLParser.raw(text);
206
+ }
207
+
208
+ /**
209
+ * Escape identifier (table/column names)
210
+ * Usage: sql`SELECT * FROM ${identifier('user-table')}`
211
+ */
212
+ static identifier(name: string): string {
213
+ return SQLParser.identifier(name);
214
+ }
215
+
216
+ /**
217
+ * Build WHERE clause from object
218
+ * Usage: const whereClause = ODBLiteClient.where({ id: 1, name: 'John' });
219
+ */
220
+ static where(conditions: Record<string, any>) {
221
+ return SQLParser.where(conditions);
222
+ }
223
+
224
+ /**
225
+ * Build INSERT VALUES from object(s)
226
+ * Usage: const insertClause = ODBLiteClient.insertValues({ name: 'John', age: 30 });
227
+ */
228
+ static insertValues(data: Record<string, any> | Record<string, any>[]) {
229
+ return SQLParser.insertValues(data);
230
+ }
231
+
232
+ /**
233
+ * Build UPDATE SET clause from object
234
+ * Usage: const setClause = ODBLiteClient.updateSet({ name: 'John', age: 30 });
235
+ */
236
+ static updateSet(data: Record<string, any>) {
237
+ return SQLParser.updateSet(data);
238
+ }
239
+
240
+ /**
241
+ * Join SQL fragments
242
+ * Usage: const query = ODBLiteClient.join([baseQuery, whereClause], ' WHERE ');
243
+ */
244
+ static join(fragments: Array<{ text: string; values: any[] }>, separator = ' ') {
245
+ return SQLParser.join(fragments, separator);
246
+ }
247
+ }
248
+
249
+ /**
250
+ * Factory function to create ODBLite client (postgres.js style)
251
+ * Returns the sql function directly, not the client class
252
+ */
253
+ export function odblite(config: ODBLiteConfig): SQLFunction;
254
+ export function odblite(baseUrl: string, apiKey: string, databaseId?: string): SQLFunction;
255
+ export function odblite(
256
+ configOrBaseUrl: ODBLiteConfig | string,
257
+ apiKey?: string,
258
+ databaseId?: string
259
+ ): SQLFunction {
260
+ const client = typeof configOrBaseUrl === 'string'
261
+ ? new ODBLiteClient({
262
+ baseUrl: configOrBaseUrl,
263
+ apiKey: apiKey!,
264
+ databaseId
265
+ })
266
+ : new ODBLiteClient(configOrBaseUrl);
267
+
268
+ return client.sql;
269
+ }
270
+
271
+ // Export static utility functions
272
+ export const raw = ODBLiteClient.raw;
273
+ export const identifier = ODBLiteClient.identifier;
274
+ export const where = ODBLiteClient.where;
275
+ export const insertValues = ODBLiteClient.insertValues;
276
+ export const updateSet = ODBLiteClient.updateSet;
277
+ export const join = ODBLiteClient.join;