@telorun/sql 0.7.2 → 0.8.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 +2 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/{sql-exec-controller.d.ts → sql-command-controller.d.ts} +4 -4
- package/dist/{sql-exec-controller.js → sql-command-controller.js} +2 -2
- package/dist/sql-connection-controller.d.ts +15 -15
- package/dist/sql-connection-controller.js +20 -88
- package/dist/{sql-select-controller.d.ts → sql-selection-controller.d.ts} +2 -2
- package/dist/{sql-select-controller.js → sql-selection-controller.js} +4 -4
- package/package.json +19 -23
- package/src/index.ts +8 -0
- package/src/{sql-exec-controller.ts → sql-command-controller.ts} +6 -6
- package/src/sql-connection-controller.ts +27 -121
- package/src/{sql-select-controller.ts → sql-selection-controller.ts} +5 -5
- package/dist/sql-postgres-connection-controller.d.ts +0 -19
- package/dist/sql-postgres-connection-controller.js +0 -9
- package/dist/sql-sqlite-connection-controller.d.ts +0 -13
- package/dist/sql-sqlite-connection-controller.js +0 -6
- package/dist/sqlite-driver-bun.d.ts +0 -2
- package/dist/sqlite-driver-bun.js +0 -31
- package/dist/sqlite-driver-node.d.ts +0 -2
- package/dist/sqlite-driver-node.js +0 -31
- package/src/sql-postgres-connection-controller.ts +0 -28
- package/src/sql-sqlite-connection-controller.ts +0 -18
- package/src/sqlite-driver-bun.ts +0 -34
- package/src/sqlite-driver-node.ts +0 -35
package/README.md
CHANGED
|
@@ -55,8 +55,8 @@ metadata:
|
|
|
55
55
|
A complete feedback collection REST API — no code, pure YAML.
|
|
56
56
|
Persists entries to SQLite and serves them over HTTP.
|
|
57
57
|
imports:
|
|
58
|
-
Http: std/http-server@0.
|
|
59
|
-
Sql: std/sql@0.9.
|
|
58
|
+
Http: std/http-server@0.12.0
|
|
59
|
+
Sql: std/sql@0.9.2
|
|
60
60
|
targets:
|
|
61
61
|
- !ref Migrations
|
|
62
62
|
- !ref Server
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { SqlConnectionResource, createSqlConnection, type SqlDriver, type PlaceholderStyle, } from "./sql-connection-controller.js";
|
|
2
|
+
export { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
3
|
+
export type { SqliteDb, SqliteStatement } from "./sqlite-driver-interface.js";
|
package/dist/index.js
ADDED
|
@@ -2,7 +2,7 @@ import type { ResourceContext, ResourceInstance } from "@telorun/sdk";
|
|
|
2
2
|
import type { SqlConnectionResource } from "./sql-connection-controller.js";
|
|
3
3
|
import type { SqlResult } from "./sql-query-controller.js";
|
|
4
4
|
import type { SqlTransactionResource } from "./sql-transaction-controller.js";
|
|
5
|
-
interface
|
|
5
|
+
interface SqlCommandManifest {
|
|
6
6
|
metadata: {
|
|
7
7
|
name: string;
|
|
8
8
|
module: string;
|
|
@@ -14,12 +14,12 @@ interface SqlExecManifest {
|
|
|
14
14
|
bindings?: unknown[];
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
|
-
declare class
|
|
17
|
+
declare class SqlCommandResource implements ResourceInstance {
|
|
18
18
|
private readonly manifest;
|
|
19
19
|
private readonly ctx;
|
|
20
|
-
constructor(manifest:
|
|
20
|
+
constructor(manifest: SqlCommandManifest, ctx: ResourceContext);
|
|
21
21
|
invoke(input: unknown): Promise<SqlResult>;
|
|
22
22
|
}
|
|
23
23
|
export declare function register(): void;
|
|
24
|
-
export declare function create(resource:
|
|
24
|
+
export declare function create(resource: SqlCommandManifest, ctx: ResourceContext): Promise<SqlCommandResource>;
|
|
25
25
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
2
2
|
import { runSql } from "./sql-run.js";
|
|
3
|
-
class
|
|
3
|
+
class SqlCommandResource {
|
|
4
4
|
manifest;
|
|
5
5
|
ctx;
|
|
6
6
|
constructor(manifest, ctx) {
|
|
@@ -20,5 +20,5 @@ class SqlExecResource {
|
|
|
20
20
|
}
|
|
21
21
|
export function register() { }
|
|
22
22
|
export async function create(resource, ctx) {
|
|
23
|
-
return new
|
|
23
|
+
return new SqlCommandResource(resource, ctx);
|
|
24
24
|
}
|
|
@@ -2,27 +2,22 @@ import type { ResourceInstance } from "@telorun/sdk";
|
|
|
2
2
|
import { Kysely, type QueryResult } from "kysely";
|
|
3
3
|
import type { SqlTransactionResource } from "./sql-transaction-controller.js";
|
|
4
4
|
import type { SqliteDb } from "./sqlite-driver-interface.js";
|
|
5
|
-
interface PoolConfig {
|
|
6
|
-
min?: number;
|
|
7
|
-
max?: number;
|
|
8
|
-
idleTimeoutMs?: number;
|
|
9
|
-
connectionTimeoutMs?: number;
|
|
10
|
-
}
|
|
11
5
|
export type SqlDriver = "postgres" | "sqlite";
|
|
12
6
|
/** Native bind-placeholder syntax per driver: SQLite binds anonymous `?`,
|
|
13
7
|
* PostgreSQL binds numbered `$1`, `$2`, … */
|
|
14
8
|
export type PlaceholderStyle = "qmark" | "numbered";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Driver-agnostic SQL connection. The kysely instance (and, for SQLite, the
|
|
11
|
+
* underlying database handle used by `executeScript`) is built by the driver
|
|
12
|
+
* backend (`sql-postgres`, `sql-sqlite`) and handed in via
|
|
13
|
+
* {@link createSqlConnection}. Everything here — execution, transactions,
|
|
14
|
+
* placeholder style, row-count normalization — is transport-neutral.
|
|
15
|
+
*/
|
|
21
16
|
export declare class SqlConnectionResource implements ResourceInstance {
|
|
22
17
|
readonly driver: SqlDriver;
|
|
23
18
|
private readonly db;
|
|
24
19
|
private readonly sqlite?;
|
|
25
|
-
constructor(
|
|
20
|
+
constructor(driver: SqlDriver, db: Kysely<any>, sqlite?: SqliteDb);
|
|
26
21
|
init(): Promise<void>;
|
|
27
22
|
teardown(): Promise<void>;
|
|
28
23
|
transaction<T>(cb: () => Promise<T>): Promise<T>;
|
|
@@ -39,5 +34,10 @@ export declare class SqlConnectionResource implements ResourceInstance {
|
|
|
39
34
|
snapshot(): Record<string, unknown>;
|
|
40
35
|
private resolveExecutor;
|
|
41
36
|
}
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Build a connection from a driver-constructed kysely instance. Driver backends
|
|
39
|
+
* (`sql-postgres`, `sql-sqlite`) own dialect construction and call this; the
|
|
40
|
+
* `sqlite` handle is required only for SQLite (its `executeScript` runs through
|
|
41
|
+
* the native handle).
|
|
42
|
+
*/
|
|
43
|
+
export declare function createSqlConnection(driver: SqlDriver, db: Kysely<any>, sqlite?: SqliteDb): SqlConnectionResource;
|
|
@@ -1,44 +1,21 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
|
-
import { CompiledQuery
|
|
3
|
-
import { Pool } from "pg";
|
|
2
|
+
import { CompiledQuery } from "kysely";
|
|
4
3
|
import { currentTxId, deleteTx, getTx, setTx, txStorage } from "./transaction-store.js";
|
|
4
|
+
/**
|
|
5
|
+
* Driver-agnostic SQL connection. The kysely instance (and, for SQLite, the
|
|
6
|
+
* underlying database handle used by `executeScript`) is built by the driver
|
|
7
|
+
* backend (`sql-postgres`, `sql-sqlite`) and handed in via
|
|
8
|
+
* {@link createSqlConnection}. Everything here — execution, transactions,
|
|
9
|
+
* placeholder style, row-count normalization — is transport-neutral.
|
|
10
|
+
*/
|
|
5
11
|
export class SqlConnectionResource {
|
|
6
12
|
driver;
|
|
7
13
|
db;
|
|
8
14
|
sqlite;
|
|
9
|
-
constructor(
|
|
10
|
-
this.driver =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
throw new Error("Sql: postgres connection requires a connectionString");
|
|
14
|
-
}
|
|
15
|
-
const url = new URL(config.connectionString);
|
|
16
|
-
const ssl = sslFromSslmode(url.searchParams.get("sslmode"));
|
|
17
|
-
url.searchParams.delete("sslmode");
|
|
18
|
-
this.db = new Kysely({
|
|
19
|
-
dialect: new PostgresDialect({
|
|
20
|
-
pool: new Pool({
|
|
21
|
-
connectionString: url.toString(),
|
|
22
|
-
ssl,
|
|
23
|
-
min: config.pool?.min ?? 1,
|
|
24
|
-
max: config.pool?.max ?? 10,
|
|
25
|
-
idleTimeoutMillis: config.pool?.idleTimeoutMs,
|
|
26
|
-
connectionTimeoutMillis: config.pool?.connectionTimeoutMs,
|
|
27
|
-
}),
|
|
28
|
-
}),
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
if (!sqlite) {
|
|
33
|
-
throw new Error("Sql: sqlite database was not initialized");
|
|
34
|
-
}
|
|
35
|
-
this.sqlite = sqlite;
|
|
36
|
-
this.db = new Kysely({
|
|
37
|
-
dialect: new TransactionalSqliteDialect({
|
|
38
|
-
database: this.sqlite,
|
|
39
|
-
}),
|
|
40
|
-
});
|
|
41
|
-
}
|
|
15
|
+
constructor(driver, db, sqlite) {
|
|
16
|
+
this.driver = driver;
|
|
17
|
+
this.db = db;
|
|
18
|
+
this.sqlite = sqlite;
|
|
42
19
|
}
|
|
43
20
|
async init() {
|
|
44
21
|
await this.db.connection().execute(async () => {
|
|
@@ -113,57 +90,12 @@ export class SqlConnectionResource {
|
|
|
113
90
|
return this.db;
|
|
114
91
|
}
|
|
115
92
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
class TransactionalSqliteDialect extends SqliteDialect {
|
|
126
|
-
createAdapter() {
|
|
127
|
-
return new TransactionalSqliteAdapter();
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
function sslFromSslmode(mode) {
|
|
131
|
-
switch (mode) {
|
|
132
|
-
case null:
|
|
133
|
-
case "disable":
|
|
134
|
-
return false;
|
|
135
|
-
case "require":
|
|
136
|
-
return { rejectUnauthorized: false };
|
|
137
|
-
case "verify-ca":
|
|
138
|
-
// libpq `verify-ca` validates the CA chain but not the hostname; Node's
|
|
139
|
-
// default `checkServerIdentity` enforces the hostname, so disable it.
|
|
140
|
-
return { rejectUnauthorized: true, checkServerIdentity: () => undefined };
|
|
141
|
-
case "verify-full":
|
|
142
|
-
return { rejectUnauthorized: true };
|
|
143
|
-
default:
|
|
144
|
-
throw new Error(`Sql.Connection: unsupported sslmode '${mode}'. ` +
|
|
145
|
-
`Use 'disable', 'require', 'verify-ca', or 'verify-full'.`);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
export async function openSqliteDatabase(file = ":memory:") {
|
|
149
|
-
// Auto-create the parent directory for file-backed databases. SQLite
|
|
150
|
-
// drivers fail-fast when the directory doesn't exist; mirroring `mkdir
|
|
151
|
-
// -p` here lets manifests use paths like `./tmp/foo.sqlite` without a
|
|
152
|
-
// separate filesystem-prep step. `:memory:` skips filesystem entirely.
|
|
153
|
-
if (file !== ":memory:") {
|
|
154
|
-
const { mkdir } = await import("node:fs/promises");
|
|
155
|
-
const { dirname } = await import("node:path");
|
|
156
|
-
const dir = dirname(file);
|
|
157
|
-
if (dir && dir !== "." && dir !== "/") {
|
|
158
|
-
await mkdir(dir, { recursive: true });
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
// Route through the package's own `./sqlite-driver` subpath export so the
|
|
162
|
-
// resolver selects the driver per runtime (Bun → bun:sqlite, Node →
|
|
163
|
-
// better-sqlite3). A manual `typeof Bun` check with relative imports gets
|
|
164
|
-
// flattened by the controller bundler into an unconditional top-level
|
|
165
|
-
// `import "bun:sqlite"`, which Node's ESM loader rejects before the guard
|
|
166
|
-
// runs; an external `@telorun/*` specifier stays a deferred dynamic import.
|
|
167
|
-
const { openDatabase } = await import("@telorun/sql/sqlite-driver");
|
|
168
|
-
return openDatabase(file);
|
|
93
|
+
/**
|
|
94
|
+
* Build a connection from a driver-constructed kysely instance. Driver backends
|
|
95
|
+
* (`sql-postgres`, `sql-sqlite`) own dialect construction and call this; the
|
|
96
|
+
* `sqlite` handle is required only for SQLite (its `executeScript` runs through
|
|
97
|
+
* the native handle).
|
|
98
|
+
*/
|
|
99
|
+
export function createSqlConnection(driver, db, sqlite) {
|
|
100
|
+
return new SqlConnectionResource(driver, db, sqlite);
|
|
169
101
|
}
|
|
@@ -58,12 +58,12 @@ interface SelectManifest {
|
|
|
58
58
|
offset?: unknown;
|
|
59
59
|
inputType?: string | Record<string, any>;
|
|
60
60
|
}
|
|
61
|
-
declare class
|
|
61
|
+
declare class SqlSelectionResource implements ResourceInstance {
|
|
62
62
|
private readonly manifest;
|
|
63
63
|
private readonly ctx;
|
|
64
64
|
constructor(manifest: SelectManifest, ctx: ResourceContext);
|
|
65
65
|
invoke(input: unknown): Promise<SqlResult>;
|
|
66
66
|
}
|
|
67
67
|
export declare function register(): void;
|
|
68
|
-
export declare function create(resource: SelectManifest, ctx: ResourceContext): Promise<
|
|
68
|
+
export declare function create(resource: SelectManifest, ctx: ResourceContext): Promise<SqlSelectionResource>;
|
|
69
69
|
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
2
2
|
// ── Controller ────────────────────────────────────────────────────────────────
|
|
3
|
-
class
|
|
3
|
+
class SqlSelectionResource {
|
|
4
4
|
manifest;
|
|
5
5
|
ctx;
|
|
6
6
|
constructor(manifest, ctx) {
|
|
@@ -21,7 +21,7 @@ class SqlSelectResource {
|
|
|
21
21
|
const offset = m.offset != null ? ctx.expandValue(m.offset, expandCtx) : undefined;
|
|
22
22
|
const connection = resolveSqlConnection(m.connection, ctx) ?? m.transaction?.getConnection();
|
|
23
23
|
if (!connection) {
|
|
24
|
-
throw new Error("Sql.
|
|
24
|
+
throw new Error("Sql.Selection: either 'connection' or 'transaction' must be set");
|
|
25
25
|
}
|
|
26
26
|
const { sql, params } = buildSelect(m, where, having, limit, offset, connection.driver);
|
|
27
27
|
const result = await connection.execute(sql, params, m.transaction);
|
|
@@ -157,7 +157,7 @@ function opToSql(op) {
|
|
|
157
157
|
};
|
|
158
158
|
const sql = map[op];
|
|
159
159
|
if (!sql)
|
|
160
|
-
throw new Error(`Sql.
|
|
160
|
+
throw new Error(`Sql.Selection: unknown operator '${op}'`);
|
|
161
161
|
return sql;
|
|
162
162
|
}
|
|
163
163
|
// ── Exports ───────────────────────────────────────────────────────────────────
|
|
@@ -190,5 +190,5 @@ function extractDefaults(inputType, ctx) {
|
|
|
190
190
|
}
|
|
191
191
|
export function register() { }
|
|
192
192
|
export async function create(resource, ctx) {
|
|
193
|
-
return new
|
|
193
|
+
return new SqlSelectionResource(resource, ctx);
|
|
194
194
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@telorun/sql",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Telo SQL module - SQL database resource kinds for Telo manifests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"telo",
|
|
@@ -21,35 +21,36 @@
|
|
|
21
21
|
"url": "https://github.com/telorun/telo/issues"
|
|
22
22
|
},
|
|
23
23
|
"type": "module",
|
|
24
|
+
"main": "./dist/index.js",
|
|
25
|
+
"module": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
24
27
|
"exports": {
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"bun": "./src/index.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
28
32
|
},
|
|
29
|
-
"./
|
|
30
|
-
"
|
|
31
|
-
"
|
|
33
|
+
"./connection": {
|
|
34
|
+
"types": "./dist/sql-connection-controller.d.ts",
|
|
35
|
+
"bun": "./src/sql-connection-controller.ts",
|
|
36
|
+
"import": "./dist/sql-connection-controller.js"
|
|
32
37
|
},
|
|
33
38
|
"./sql-query": {
|
|
34
39
|
"bun": "./src/sql-query-controller.ts",
|
|
35
40
|
"import": "./dist/sql-query-controller.js"
|
|
36
41
|
},
|
|
37
|
-
"./sql-
|
|
38
|
-
"bun": "./src/sql-
|
|
39
|
-
"import": "./dist/sql-
|
|
42
|
+
"./sql-selection": {
|
|
43
|
+
"bun": "./src/sql-selection-controller.ts",
|
|
44
|
+
"import": "./dist/sql-selection-controller.js"
|
|
40
45
|
},
|
|
41
|
-
"./sql-
|
|
42
|
-
"bun": "./src/sql-
|
|
43
|
-
"import": "./dist/sql-
|
|
46
|
+
"./sql-command": {
|
|
47
|
+
"bun": "./src/sql-command-controller.ts",
|
|
48
|
+
"import": "./dist/sql-command-controller.js"
|
|
44
49
|
},
|
|
45
50
|
"./sql-transaction": {
|
|
46
51
|
"bun": "./src/sql-transaction-controller.ts",
|
|
47
52
|
"import": "./dist/sql-transaction-controller.js"
|
|
48
53
|
},
|
|
49
|
-
"./sqlite-driver": {
|
|
50
|
-
"bun": "./src/sqlite-driver-bun.ts",
|
|
51
|
-
"import": "./dist/sqlite-driver-node.js"
|
|
52
|
-
},
|
|
53
54
|
"./sql-migration": {
|
|
54
55
|
"bun": "./src/sql-migration-controller.ts",
|
|
55
56
|
"import": "./dist/sql-migration-controller.js"
|
|
@@ -64,17 +65,12 @@
|
|
|
64
65
|
"src/**"
|
|
65
66
|
],
|
|
66
67
|
"dependencies": {
|
|
67
|
-
"better-sqlite3": "^12.8.0",
|
|
68
|
-
"pg": "^8.20.0",
|
|
69
68
|
"kysely": "^0.28.15"
|
|
70
69
|
},
|
|
71
70
|
"devDependencies": {
|
|
72
|
-
"@types/better-sqlite3": "^7.0.0",
|
|
73
|
-
"@types/bun": "^1.3.10",
|
|
74
71
|
"@types/node": "^20.0.0",
|
|
75
|
-
"@types/pg": "^8.0.0",
|
|
76
72
|
"typescript": "^5.0.0",
|
|
77
|
-
"@telorun/sdk": "0.
|
|
73
|
+
"@telorun/sdk": "0.34.0"
|
|
78
74
|
},
|
|
79
75
|
"peerDependencies": {
|
|
80
76
|
"@telorun/sdk": "*"
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export {
|
|
2
|
+
SqlConnectionResource,
|
|
3
|
+
createSqlConnection,
|
|
4
|
+
type SqlDriver,
|
|
5
|
+
type PlaceholderStyle,
|
|
6
|
+
} from "./sql-connection-controller.js";
|
|
7
|
+
export { resolveSqlConnection } from "./sql-connection-ref.js";
|
|
8
|
+
export type { SqliteDb, SqliteStatement } from "./sqlite-driver-interface.js";
|
|
@@ -5,7 +5,7 @@ import type { SqlResult } from "./sql-query-controller.js";
|
|
|
5
5
|
import { runSql } from "./sql-run.js";
|
|
6
6
|
import type { SqlTransactionResource } from "./sql-transaction-controller.js";
|
|
7
7
|
|
|
8
|
-
interface
|
|
8
|
+
interface SqlCommandManifest {
|
|
9
9
|
metadata: { name: string; module: string };
|
|
10
10
|
connection?: SqlConnectionResource;
|
|
11
11
|
transaction?: SqlTransactionResource;
|
|
@@ -15,9 +15,9 @@ interface SqlExecManifest {
|
|
|
15
15
|
};
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
class
|
|
18
|
+
class SqlCommandResource implements ResourceInstance {
|
|
19
19
|
constructor(
|
|
20
|
-
private readonly manifest:
|
|
20
|
+
private readonly manifest: SqlCommandManifest,
|
|
21
21
|
private readonly ctx: ResourceContext,
|
|
22
22
|
) {}
|
|
23
23
|
|
|
@@ -38,8 +38,8 @@ class SqlExecResource implements ResourceInstance {
|
|
|
38
38
|
export function register(): void {}
|
|
39
39
|
|
|
40
40
|
export async function create(
|
|
41
|
-
resource:
|
|
41
|
+
resource: SqlCommandManifest,
|
|
42
42
|
ctx: ResourceContext,
|
|
43
|
-
): Promise<
|
|
44
|
-
return new
|
|
43
|
+
): Promise<SqlCommandResource> {
|
|
44
|
+
return new SqlCommandResource(resource, ctx);
|
|
45
45
|
}
|
|
@@ -1,77 +1,34 @@
|
|
|
1
1
|
import type { ResourceInstance } from "@telorun/sdk";
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
|
-
import {
|
|
4
|
-
CompiledQuery,
|
|
5
|
-
Kysely,
|
|
6
|
-
PostgresDialect,
|
|
7
|
-
SqliteAdapter,
|
|
8
|
-
SqliteDialect,
|
|
9
|
-
type QueryResult,
|
|
10
|
-
type Transaction,
|
|
11
|
-
} from "kysely";
|
|
12
|
-
import { Pool } from "pg";
|
|
3
|
+
import { CompiledQuery, Kysely, type QueryResult, type Transaction } from "kysely";
|
|
13
4
|
import type { SqlTransactionResource } from "./sql-transaction-controller.js";
|
|
14
5
|
import type { SqliteDb } from "./sqlite-driver-interface.js";
|
|
15
6
|
import { currentTxId, deleteTx, getTx, setTx, txStorage } from "./transaction-store.js";
|
|
16
7
|
|
|
17
|
-
interface PoolConfig {
|
|
18
|
-
min?: number;
|
|
19
|
-
max?: number;
|
|
20
|
-
idleTimeoutMs?: number;
|
|
21
|
-
connectionTimeoutMs?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
8
|
export type SqlDriver = "postgres" | "sqlite";
|
|
25
9
|
|
|
26
10
|
/** Native bind-placeholder syntax per driver: SQLite binds anonymous `?`,
|
|
27
11
|
* PostgreSQL binds numbered `$1`, `$2`, … */
|
|
28
12
|
export type PlaceholderStyle = "qmark" | "numbered";
|
|
29
13
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Driver-agnostic SQL connection. The kysely instance (and, for SQLite, the
|
|
16
|
+
* underlying database handle used by `executeScript`) is built by the driver
|
|
17
|
+
* backend (`sql-postgres`, `sql-sqlite`) and handed in via
|
|
18
|
+
* {@link createSqlConnection}. Everything here — execution, transactions,
|
|
19
|
+
* placeholder style, row-count normalization — is transport-neutral.
|
|
20
|
+
*/
|
|
37
21
|
export class SqlConnectionResource implements ResourceInstance {
|
|
38
|
-
readonly driver: SqlDriver;
|
|
39
22
|
private readonly db: Kysely<any>;
|
|
40
23
|
private readonly sqlite?: SqliteDb;
|
|
41
24
|
|
|
42
|
-
constructor(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const url = new URL(config.connectionString);
|
|
50
|
-
const ssl = sslFromSslmode(url.searchParams.get("sslmode"));
|
|
51
|
-
url.searchParams.delete("sslmode");
|
|
52
|
-
this.db = new Kysely({
|
|
53
|
-
dialect: new PostgresDialect({
|
|
54
|
-
pool: new Pool({
|
|
55
|
-
connectionString: url.toString(),
|
|
56
|
-
ssl,
|
|
57
|
-
min: config.pool?.min ?? 1,
|
|
58
|
-
max: config.pool?.max ?? 10,
|
|
59
|
-
idleTimeoutMillis: config.pool?.idleTimeoutMs,
|
|
60
|
-
connectionTimeoutMillis: config.pool?.connectionTimeoutMs,
|
|
61
|
-
}),
|
|
62
|
-
}),
|
|
63
|
-
});
|
|
64
|
-
} else {
|
|
65
|
-
if (!sqlite) {
|
|
66
|
-
throw new Error("Sql: sqlite database was not initialized");
|
|
67
|
-
}
|
|
68
|
-
this.sqlite = sqlite;
|
|
69
|
-
this.db = new Kysely({
|
|
70
|
-
dialect: new TransactionalSqliteDialect({
|
|
71
|
-
database: this.sqlite,
|
|
72
|
-
}),
|
|
73
|
-
});
|
|
74
|
-
}
|
|
25
|
+
constructor(
|
|
26
|
+
readonly driver: SqlDriver,
|
|
27
|
+
db: Kysely<any>,
|
|
28
|
+
sqlite?: SqliteDb,
|
|
29
|
+
) {
|
|
30
|
+
this.db = db;
|
|
31
|
+
this.sqlite = sqlite;
|
|
75
32
|
}
|
|
76
33
|
|
|
77
34
|
async init() {
|
|
@@ -171,67 +128,16 @@ export class SqlConnectionResource implements ResourceInstance {
|
|
|
171
128
|
}
|
|
172
129
|
}
|
|
173
130
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return new TransactionalSqliteAdapter();
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
type SslOption =
|
|
191
|
-
| false
|
|
192
|
-
| { rejectUnauthorized: boolean; checkServerIdentity?: () => undefined };
|
|
193
|
-
|
|
194
|
-
function sslFromSslmode(mode: string | null): SslOption {
|
|
195
|
-
switch (mode) {
|
|
196
|
-
case null:
|
|
197
|
-
case "disable":
|
|
198
|
-
return false;
|
|
199
|
-
case "require":
|
|
200
|
-
return { rejectUnauthorized: false };
|
|
201
|
-
case "verify-ca":
|
|
202
|
-
// libpq `verify-ca` validates the CA chain but not the hostname; Node's
|
|
203
|
-
// default `checkServerIdentity` enforces the hostname, so disable it.
|
|
204
|
-
return { rejectUnauthorized: true, checkServerIdentity: () => undefined };
|
|
205
|
-
case "verify-full":
|
|
206
|
-
return { rejectUnauthorized: true };
|
|
207
|
-
default:
|
|
208
|
-
throw new Error(
|
|
209
|
-
`Sql.Connection: unsupported sslmode '${mode}'. ` +
|
|
210
|
-
`Use 'disable', 'require', 'verify-ca', or 'verify-full'.`,
|
|
211
|
-
);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export async function openSqliteDatabase(file = ":memory:"): Promise<SqliteDb> {
|
|
216
|
-
// Auto-create the parent directory for file-backed databases. SQLite
|
|
217
|
-
// drivers fail-fast when the directory doesn't exist; mirroring `mkdir
|
|
218
|
-
// -p` here lets manifests use paths like `./tmp/foo.sqlite` without a
|
|
219
|
-
// separate filesystem-prep step. `:memory:` skips filesystem entirely.
|
|
220
|
-
if (file !== ":memory:") {
|
|
221
|
-
const { mkdir } = await import("node:fs/promises");
|
|
222
|
-
const { dirname } = await import("node:path");
|
|
223
|
-
const dir = dirname(file);
|
|
224
|
-
if (dir && dir !== "." && dir !== "/") {
|
|
225
|
-
await mkdir(dir, { recursive: true });
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Route through the package's own `./sqlite-driver` subpath export so the
|
|
230
|
-
// resolver selects the driver per runtime (Bun → bun:sqlite, Node →
|
|
231
|
-
// better-sqlite3). A manual `typeof Bun` check with relative imports gets
|
|
232
|
-
// flattened by the controller bundler into an unconditional top-level
|
|
233
|
-
// `import "bun:sqlite"`, which Node's ESM loader rejects before the guard
|
|
234
|
-
// runs; an external `@telorun/*` specifier stays a deferred dynamic import.
|
|
235
|
-
const { openDatabase } = await import("@telorun/sql/sqlite-driver");
|
|
236
|
-
return openDatabase(file);
|
|
131
|
+
/**
|
|
132
|
+
* Build a connection from a driver-constructed kysely instance. Driver backends
|
|
133
|
+
* (`sql-postgres`, `sql-sqlite`) own dialect construction and call this; the
|
|
134
|
+
* `sqlite` handle is required only for SQLite (its `executeScript` runs through
|
|
135
|
+
* the native handle).
|
|
136
|
+
*/
|
|
137
|
+
export function createSqlConnection(
|
|
138
|
+
driver: SqlDriver,
|
|
139
|
+
db: Kysely<any>,
|
|
140
|
+
sqlite?: SqliteDb,
|
|
141
|
+
): SqlConnectionResource {
|
|
142
|
+
return new SqlConnectionResource(driver, db, sqlite);
|
|
237
143
|
}
|
|
@@ -76,7 +76,7 @@ interface SelectManifest {
|
|
|
76
76
|
|
|
77
77
|
// ── Controller ────────────────────────────────────────────────────────────────
|
|
78
78
|
|
|
79
|
-
class
|
|
79
|
+
class SqlSelectionResource implements ResourceInstance {
|
|
80
80
|
constructor(
|
|
81
81
|
private readonly manifest: SelectManifest,
|
|
82
82
|
private readonly ctx: ResourceContext,
|
|
@@ -98,7 +98,7 @@ class SqlSelectResource implements ResourceInstance {
|
|
|
98
98
|
|
|
99
99
|
const connection = resolveSqlConnection(m.connection, ctx) ?? m.transaction?.getConnection();
|
|
100
100
|
if (!connection) {
|
|
101
|
-
throw new Error("Sql.
|
|
101
|
+
throw new Error("Sql.Selection: either 'connection' or 'transaction' must be set");
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
const { sql, params } = buildSelect(m, where, having, limit, offset, connection.driver);
|
|
@@ -266,7 +266,7 @@ function opToSql(op: Op): string {
|
|
|
266
266
|
ilike: "ILIKE",
|
|
267
267
|
};
|
|
268
268
|
const sql = map[op];
|
|
269
|
-
if (!sql) throw new Error(`Sql.
|
|
269
|
+
if (!sql) throw new Error(`Sql.Selection: unknown operator '${op}'`);
|
|
270
270
|
return sql;
|
|
271
271
|
}
|
|
272
272
|
|
|
@@ -306,6 +306,6 @@ export function register(): void {}
|
|
|
306
306
|
export async function create(
|
|
307
307
|
resource: SelectManifest,
|
|
308
308
|
ctx: ResourceContext,
|
|
309
|
-
): Promise<
|
|
310
|
-
return new
|
|
309
|
+
): Promise<SqlSelectionResource> {
|
|
310
|
+
return new SqlSelectionResource(resource, ctx);
|
|
311
311
|
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { ResourceContext } from "@telorun/sdk";
|
|
2
|
-
import { SqlConnectionResource } from "./sql-connection-controller.js";
|
|
3
|
-
interface PoolConfig {
|
|
4
|
-
min?: number;
|
|
5
|
-
max?: number;
|
|
6
|
-
idleTimeoutMs?: number;
|
|
7
|
-
connectionTimeoutMs?: number;
|
|
8
|
-
}
|
|
9
|
-
interface PostgresConnectionManifest {
|
|
10
|
-
metadata: {
|
|
11
|
-
name: string;
|
|
12
|
-
module: string;
|
|
13
|
-
};
|
|
14
|
-
connectionString: string;
|
|
15
|
-
pool?: PoolConfig;
|
|
16
|
-
}
|
|
17
|
-
export declare function register(): void;
|
|
18
|
-
export declare function create(resource: PostgresConnectionManifest, ctx: ResourceContext): Promise<SqlConnectionResource>;
|
|
19
|
-
export {};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { SqlConnectionResource } from "./sql-connection-controller.js";
|
|
2
|
-
export function register() { }
|
|
3
|
-
export async function create(resource, ctx) {
|
|
4
|
-
return new SqlConnectionResource({
|
|
5
|
-
driver: "postgres",
|
|
6
|
-
connectionString: resource.connectionString,
|
|
7
|
-
pool: resource.pool,
|
|
8
|
-
});
|
|
9
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { ResourceContext } from "@telorun/sdk";
|
|
2
|
-
import { SqlConnectionResource } from "./sql-connection-controller.js";
|
|
3
|
-
interface SqliteConnectionManifest {
|
|
4
|
-
metadata: {
|
|
5
|
-
name: string;
|
|
6
|
-
module: string;
|
|
7
|
-
};
|
|
8
|
-
/** File path, or omitted / `:memory:` for an in-memory database. */
|
|
9
|
-
file?: string;
|
|
10
|
-
}
|
|
11
|
-
export declare function register(): void;
|
|
12
|
-
export declare function create(resource: SqliteConnectionManifest, ctx: ResourceContext): Promise<SqlConnectionResource>;
|
|
13
|
-
export {};
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { openSqliteDatabase, SqlConnectionResource } from "./sql-connection-controller.js";
|
|
2
|
-
export function register() { }
|
|
3
|
-
export async function create(resource, ctx) {
|
|
4
|
-
const sqlite = await openSqliteDatabase(resource.file ?? ":memory:");
|
|
5
|
-
return new SqlConnectionResource({ driver: "sqlite" }, sqlite);
|
|
6
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Database } from "bun:sqlite";
|
|
2
|
-
export function openDatabase(file) {
|
|
3
|
-
const db = new Database(file);
|
|
4
|
-
return {
|
|
5
|
-
prepare(sql) {
|
|
6
|
-
const stmt = db.prepare(sql);
|
|
7
|
-
return {
|
|
8
|
-
reader: true,
|
|
9
|
-
all(params) {
|
|
10
|
-
return stmt.all(...params);
|
|
11
|
-
},
|
|
12
|
-
run(params) {
|
|
13
|
-
const result = stmt.run(...params);
|
|
14
|
-
return {
|
|
15
|
-
changes: result.changes,
|
|
16
|
-
lastInsertRowid: result.lastInsertRowid,
|
|
17
|
-
};
|
|
18
|
-
},
|
|
19
|
-
iterate(params) {
|
|
20
|
-
return stmt.iterate(...params);
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
},
|
|
24
|
-
exec(sql) {
|
|
25
|
-
db.exec(sql);
|
|
26
|
-
},
|
|
27
|
-
close() {
|
|
28
|
-
db.close();
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3";
|
|
2
|
-
export function openDatabase(file) {
|
|
3
|
-
const db = new Database(file);
|
|
4
|
-
return {
|
|
5
|
-
prepare(sql) {
|
|
6
|
-
const stmt = db.prepare(sql);
|
|
7
|
-
return {
|
|
8
|
-
reader: stmt.reader,
|
|
9
|
-
all(params) {
|
|
10
|
-
return stmt.all(...params);
|
|
11
|
-
},
|
|
12
|
-
run(params) {
|
|
13
|
-
const result = stmt.run(...params);
|
|
14
|
-
return {
|
|
15
|
-
changes: result.changes,
|
|
16
|
-
lastInsertRowid: result.lastInsertRowid,
|
|
17
|
-
};
|
|
18
|
-
},
|
|
19
|
-
iterate(params) {
|
|
20
|
-
return stmt.iterate(...params);
|
|
21
|
-
},
|
|
22
|
-
};
|
|
23
|
-
},
|
|
24
|
-
exec(sql) {
|
|
25
|
-
db.exec(sql);
|
|
26
|
-
},
|
|
27
|
-
close() {
|
|
28
|
-
db.close();
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
import type { ResourceContext } from "@telorun/sdk";
|
|
2
|
-
import { SqlConnectionResource } from "./sql-connection-controller.js";
|
|
3
|
-
|
|
4
|
-
interface PoolConfig {
|
|
5
|
-
min?: number;
|
|
6
|
-
max?: number;
|
|
7
|
-
idleTimeoutMs?: number;
|
|
8
|
-
connectionTimeoutMs?: number;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
interface PostgresConnectionManifest {
|
|
12
|
-
metadata: { name: string; module: string };
|
|
13
|
-
connectionString: string;
|
|
14
|
-
pool?: PoolConfig;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function register(): void {}
|
|
18
|
-
|
|
19
|
-
export async function create(
|
|
20
|
-
resource: PostgresConnectionManifest,
|
|
21
|
-
ctx: ResourceContext,
|
|
22
|
-
): Promise<SqlConnectionResource> {
|
|
23
|
-
return new SqlConnectionResource({
|
|
24
|
-
driver: "postgres",
|
|
25
|
-
connectionString: resource.connectionString,
|
|
26
|
-
pool: resource.pool,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { ResourceContext } from "@telorun/sdk";
|
|
2
|
-
import { openSqliteDatabase, SqlConnectionResource } from "./sql-connection-controller.js";
|
|
3
|
-
|
|
4
|
-
interface SqliteConnectionManifest {
|
|
5
|
-
metadata: { name: string; module: string };
|
|
6
|
-
/** File path, or omitted / `:memory:` for an in-memory database. */
|
|
7
|
-
file?: string;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function register(): void {}
|
|
11
|
-
|
|
12
|
-
export async function create(
|
|
13
|
-
resource: SqliteConnectionManifest,
|
|
14
|
-
ctx: ResourceContext,
|
|
15
|
-
): Promise<SqlConnectionResource> {
|
|
16
|
-
const sqlite = await openSqliteDatabase(resource.file ?? ":memory:");
|
|
17
|
-
return new SqlConnectionResource({ driver: "sqlite" }, sqlite);
|
|
18
|
-
}
|
package/src/sqlite-driver-bun.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import { Database } from "bun:sqlite";
|
|
2
|
-
import type { SqliteDb } from "./sqlite-driver-interface.js";
|
|
3
|
-
|
|
4
|
-
export function openDatabase(file: string): SqliteDb {
|
|
5
|
-
const db = new Database(file);
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
prepare(sql: string) {
|
|
9
|
-
const stmt = db.prepare(sql);
|
|
10
|
-
return {
|
|
11
|
-
reader: true,
|
|
12
|
-
all(params: ReadonlyArray<unknown>) {
|
|
13
|
-
return stmt.all(...(params as any[]));
|
|
14
|
-
},
|
|
15
|
-
run(params: ReadonlyArray<unknown>) {
|
|
16
|
-
const result = stmt.run(...(params as any[]));
|
|
17
|
-
return {
|
|
18
|
-
changes: result.changes,
|
|
19
|
-
lastInsertRowid: result.lastInsertRowid,
|
|
20
|
-
};
|
|
21
|
-
},
|
|
22
|
-
iterate(params: ReadonlyArray<unknown>) {
|
|
23
|
-
return stmt.iterate(...(params as any[])) as IterableIterator<unknown>;
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
},
|
|
27
|
-
exec(sql: string) {
|
|
28
|
-
db.exec(sql);
|
|
29
|
-
},
|
|
30
|
-
close() {
|
|
31
|
-
db.close();
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import Database from "better-sqlite3";
|
|
2
|
-
import type { SqliteDb } from "./sqlite-driver-interface.js";
|
|
3
|
-
|
|
4
|
-
export function openDatabase(file: string): SqliteDb {
|
|
5
|
-
const db = new Database(file);
|
|
6
|
-
|
|
7
|
-
return {
|
|
8
|
-
prepare(sql: string) {
|
|
9
|
-
const stmt = db.prepare(sql);
|
|
10
|
-
|
|
11
|
-
return {
|
|
12
|
-
reader: stmt.reader,
|
|
13
|
-
all(params: ReadonlyArray<unknown>) {
|
|
14
|
-
return stmt.all(...(params as unknown[]));
|
|
15
|
-
},
|
|
16
|
-
run(params: ReadonlyArray<unknown>) {
|
|
17
|
-
const result = stmt.run(...(params as unknown[]));
|
|
18
|
-
return {
|
|
19
|
-
changes: result.changes,
|
|
20
|
-
lastInsertRowid: result.lastInsertRowid,
|
|
21
|
-
};
|
|
22
|
-
},
|
|
23
|
-
iterate(params: ReadonlyArray<unknown>) {
|
|
24
|
-
return stmt.iterate(...(params as unknown[])) as IterableIterator<unknown>;
|
|
25
|
-
},
|
|
26
|
-
};
|
|
27
|
-
},
|
|
28
|
-
exec(sql: string) {
|
|
29
|
-
db.exec(sql);
|
|
30
|
-
},
|
|
31
|
-
close() {
|
|
32
|
-
db.close();
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
}
|