@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.
- package/README.md +316 -0
- package/dist/core/client.d.ts +110 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/http-client.d.ts +37 -0
- package/dist/core/http-client.d.ts.map +1 -0
- package/dist/core/sql-parser.d.ts +70 -0
- package/dist/core/sql-parser.d.ts.map +1 -0
- package/dist/core/transaction.d.ts +60 -0
- package/dist/core/transaction.d.ts.map +1 -0
- package/dist/index.cjs +1052 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +992 -0
- package/dist/service/service-client.d.ts +151 -0
- package/dist/service/service-client.d.ts.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +44 -0
- package/src/core/client.ts +277 -0
- package/src/core/http-client.ts +200 -0
- package/src/core/sql-parser.ts +330 -0
- package/src/core/transaction.ts +425 -0
- package/src/index.ts +51 -0
- package/src/service/service-client.ts +316 -0
- package/src/types.ts +127 -0
|
@@ -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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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;
|