@telorun/sql 0.7.2 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +2 -141
  2. package/dist/index.d.ts +3 -0
  3. package/dist/index.js +2 -0
  4. package/dist/{sql-exec-controller.d.ts → sql-command-controller.d.ts} +4 -4
  5. package/dist/{sql-exec-controller.js → sql-command-controller.js} +4 -3
  6. package/dist/sql-connection-controller.d.ts +15 -15
  7. package/dist/sql-connection-controller.js +20 -88
  8. package/dist/sql-connection-ref.d.ts +10 -7
  9. package/dist/sql-connection-ref.js +13 -22
  10. package/dist/sql-migrations-controller.js +1 -1
  11. package/dist/sql-query-controller.js +5 -3
  12. package/dist/{sql-select-controller.d.ts → sql-selection-controller.d.ts} +2 -2
  13. package/dist/{sql-select-controller.js → sql-selection-controller.js} +6 -5
  14. package/dist/sql-transaction-controller.js +1 -2
  15. package/package.json +19 -23
  16. package/src/index.ts +8 -0
  17. package/src/{sql-exec-controller.ts → sql-command-controller.ts} +9 -7
  18. package/src/sql-connection-controller.ts +27 -121
  19. package/src/sql-connection-ref.ts +14 -33
  20. package/src/sql-migrations-controller.ts +5 -1
  21. package/src/sql-query-controller.ts +10 -2
  22. package/src/{sql-select-controller.ts → sql-selection-controller.ts} +8 -6
  23. package/src/sql-transaction-controller.ts +5 -2
  24. package/dist/sql-postgres-connection-controller.d.ts +0 -19
  25. package/dist/sql-postgres-connection-controller.js +0 -9
  26. package/dist/sql-sqlite-connection-controller.d.ts +0 -13
  27. package/dist/sql-sqlite-connection-controller.js +0 -6
  28. package/dist/sqlite-driver-bun.d.ts +0 -2
  29. package/dist/sqlite-driver-bun.js +0 -31
  30. package/dist/sqlite-driver-node.d.ts +0 -2
  31. package/dist/sqlite-driver-node.js +0 -31
  32. package/src/sql-postgres-connection-controller.ts +0 -28
  33. package/src/sql-sqlite-connection-controller.ts +0 -18
  34. package/src/sqlite-driver-bun.ts +0 -34
  35. package/src/sqlite-driver-node.ts +0 -35
package/README.md CHANGED
@@ -23,7 +23,7 @@ Built to be language-agnostic and infinitely extensible.
23
23
 
24
24
  ```bash
25
25
  # Reconcile your manifest into a running backend
26
- $ telo ./examples/hello-api.yaml
26
+ $ telo ./examples/hello-api
27
27
 
28
28
  {"level":30,"time":1771610393008,"pid":1310178,"hostname":"dev","msg":"Server listening at http://127.0.0.1:8844"}
29
29
  ```
@@ -44,146 +44,7 @@ $ telo ./examples/hello-api.yaml
44
44
 
45
45
  ## Example manifest
46
46
 
47
- Here is an example Telo application that defines a simple HTTP API:
48
-
49
- ```yaml
50
- kind: Telo.Application
51
- metadata:
52
- name: feedback
53
- version: 1.0.0
54
- description: |
55
- A complete feedback collection REST API — no code, pure YAML.
56
- Persists entries to SQLite and serves them over HTTP.
57
- imports:
58
- Http: std/http-server@0.11.0
59
- Sql: std/sql@0.9.0
60
- targets:
61
- - !ref Migrations
62
- - !ref Server
63
- ---
64
- # SQLite database — swap driver/host/database for PostgreSQL with zero YAML changes
65
- kind: Sql.Connection
66
- metadata:
67
- name: Db
68
- driver: sqlite
69
- file: ./tmp/feedback.db
70
- ---
71
- # Migrations: applied automatically before the server starts
72
- kind: Sql.Migrations
73
- metadata:
74
- name: Migrations
75
- connection: !ref Db
76
- ---
77
- kind: Sql.Migration
78
- metadata:
79
- name: Migration_20260413_182154_CreateFeedback
80
- version: 20260413_182154_CreateFeedback
81
- sql: |
82
- CREATE TABLE IF NOT EXISTS feedback (
83
- id INTEGER PRIMARY KEY AUTOINCREMENT,
84
- text TEXT NOT NULL,
85
- source TEXT,
86
- score INTEGER NOT NULL DEFAULT 0,
87
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
88
- )
89
- ---
90
- kind: Http.Server
91
- metadata:
92
- name: Server
93
- baseUrl: http://localhost:8844
94
- port: 8844
95
- logger: true
96
- openapi:
97
- info:
98
- title: Feedback API
99
- version: 1.0.0
100
- mounts:
101
- - path: /v1
102
- mount: !ref FeedbackRoutes
103
- ---
104
- kind: Http.Api
105
- metadata:
106
- name: FeedbackRoutes
107
- routes:
108
- # POST /v1/feedback — insert a new entry, score derived from body length heuristic
109
- - request:
110
- path: /feedback
111
- method: POST
112
- schema:
113
- body:
114
- type: object
115
- properties:
116
- text:
117
- type: string
118
- minLength: 1
119
- source:
120
- type: string
121
- required: [ text ]
122
- handler:
123
- kind: Sql.Exec
124
- connection: !ref Db
125
- inputs:
126
- sql: "INSERT INTO feedback (text, source, score) VALUES (?, ?, ?)"
127
- bindings:
128
- - "${{ request.body.text }}"
129
- - "${{ request.body.source }}"
130
- - "${{ size(request.body.text) }}"
131
- response:
132
- - status: 201
133
- headers:
134
- Content-Type: application/json
135
- body:
136
- ok: true
137
- message: Feedback received
138
-
139
- # GET /v1/feedback — list all entries, newest first
140
- - request:
141
- path: /feedback
142
- method: GET
143
- handler:
144
- kind: Sql.Select
145
- connection: !ref Db
146
- from: feedback
147
- columns: [ id, text, source, score, created_at ]
148
- orderBy:
149
- - { column: created_at, direction: desc }
150
- response:
151
- - status: 200
152
- headers:
153
- Content-Type: application/json
154
- body: "${{ result.rows }}"
155
-
156
- # GET /v1/feedback/{id} — fetch a single entry
157
- - request:
158
- path: /feedback/{id}
159
- method: GET
160
- schema:
161
- params:
162
- type: object
163
- properties:
164
- id:
165
- type: integer
166
- required: [ id ]
167
- handler:
168
- kind: Sql.Select
169
- connection: !ref Db
170
- from: feedback
171
- columns: [ id, text, source, score, created_at ]
172
- where:
173
- - { column: id, op: "=", value: "${{ request.params.id }}" }
174
- response:
175
- - status: 200
176
- when: "size(result.rows) > 0"
177
- headers:
178
- Content-Type: application/json
179
- body: "${{ result.rows[0] }}"
180
- - status: 404
181
- headers:
182
- Content-Type: application/json
183
- body:
184
- ok: false
185
- message: Not found
186
- ```
47
+ See [examples/](./examples/) for a list of working applications.
187
48
 
188
49
  ## Status
189
50
 
@@ -0,0 +1,3 @@
1
+ export { SqlConnectionResource, createSqlConnection, type SqlDriver, type PlaceholderStyle, } from "./sql-connection-controller.js";
2
+ export { isSqlConnection, resolveSqlConnection } from "./sql-connection-ref.js";
3
+ export type { SqliteDb, SqliteStatement } from "./sqlite-driver-interface.js";
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { SqlConnectionResource, createSqlConnection, } from "./sql-connection-controller.js";
2
+ export { isSqlConnection, resolveSqlConnection } from "./sql-connection-ref.js";
@@ -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 SqlExecManifest {
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 SqlExecResource implements ResourceInstance {
17
+ declare class SqlCommandResource implements ResourceInstance {
18
18
  private readonly manifest;
19
19
  private readonly ctx;
20
- constructor(manifest: SqlExecManifest, ctx: ResourceContext);
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: SqlExecManifest, ctx: ResourceContext): Promise<SqlExecResource>;
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 SqlExecResource {
3
+ class SqlCommandResource {
4
4
  manifest;
5
5
  ctx;
6
6
  constructor(manifest, ctx) {
@@ -10,7 +10,8 @@ class SqlExecResource {
10
10
  async invoke(input) {
11
11
  const m = this.manifest;
12
12
  const ctx = this.ctx;
13
- const connection = resolveSqlConnection(m.connection, ctx) ?? m.transaction?.getConnection();
13
+ const connection = resolveSqlConnection(m.connection, ctx, () => `Sql.Command "${m.metadata.name}": 'connection'`) ??
14
+ m.transaction?.getConnection();
14
15
  if (!connection) {
15
16
  throw new Error("Sql: either 'connection' or 'transaction' must be set");
16
17
  }
@@ -20,5 +21,5 @@ class SqlExecResource {
20
21
  }
21
22
  export function register() { }
22
23
  export async function create(resource, ctx) {
23
- return new SqlExecResource(resource, ctx);
24
+ return new SqlCommandResource(resource, ctx);
24
25
  }
@@ -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
- export interface SqlConnectionConfig {
16
- driver: SqlDriver;
17
- /** Required for `postgres`; ignored for `sqlite` (which opens via `sqlite`). */
18
- connectionString?: string;
19
- pool?: PoolConfig;
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(config: SqlConnectionConfig, sqlite?: SqliteDb);
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
- export declare function openSqliteDatabase(file?: string): Promise<SqliteDb>;
43
- export {};
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, Kysely, PostgresDialect, SqliteAdapter, SqliteDialect, } from "kysely";
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(config, sqlite) {
10
- this.driver = config.driver;
11
- if (this.driver === "postgres") {
12
- if (!config.connectionString) {
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
- // Kysely's stock SQLite adapter reports `supportsTransactionalDdl = false`, so
117
- // its Migrator runs migrations without a transaction. SQLite does support
118
- // transactional DDL, so we flip the flag letting the Migrator wrap the whole
119
- // migration batch in a single transaction, matching PostgreSQL.
120
- class TransactionalSqliteAdapter extends SqliteAdapter {
121
- get supportsTransactionalDdl() {
122
- return true;
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
  }
@@ -1,8 +1,11 @@
1
- import type { ResourceContext } from "@telorun/sdk";
1
+ import type { KindRef, ResourceContext } from "@telorun/sdk";
2
2
  import type { SqlConnectionResource } from "./sql-connection-controller.js";
3
- interface ConnectionRef {
4
- name: string;
5
- alias?: string;
6
- }
7
- export declare function resolveSqlConnection(value: SqlConnectionResource | ConnectionRef | undefined, ctx: ResourceContext): SqlConnectionResource | undefined;
8
- export {};
3
+ /** True when a value already exposes the connection contract (Phase-5 injected). */
4
+ export declare function isSqlConnection(value: unknown): value is SqlConnectionResource;
5
+ /**
6
+ * Resolve a `connection` `!ref` field to a live connection. The slot is optional
7
+ * an unset one yields `undefined` so the caller can fall back to a `transaction`
8
+ * — but a slot that IS set must resolve. `describe` names the owning resource and
9
+ * slot, so the failure points at a concrete manifest location.
10
+ */
11
+ export declare function resolveSqlConnection(value: SqlConnectionResource | KindRef<SqlConnectionResource> | undefined, ctx: ResourceContext, describe: () => string): SqlConnectionResource | undefined;
@@ -1,24 +1,15 @@
1
- export function resolveSqlConnection(value, ctx) {
2
- if (!value) {
1
+ /** True when a value already exposes the connection contract (Phase-5 injected). */
2
+ export function isSqlConnection(value) {
3
+ return typeof value?.execute === "function";
4
+ }
5
+ /**
6
+ * Resolve a `connection` `!ref` field to a live connection. The slot is optional
7
+ * — an unset one yields `undefined` so the caller can fall back to a `transaction`
8
+ * — but a slot that IS set must resolve. `describe` names the owning resource and
9
+ * slot, so the failure points at a concrete manifest location.
10
+ */
11
+ export function resolveSqlConnection(value, ctx, describe) {
12
+ if (!value)
3
13
  return undefined;
4
- }
5
- if (typeof value.execute === "function") {
6
- return value;
7
- }
8
- const ref = value;
9
- if (typeof ref.name !== "string") {
10
- throw new Error("Sql: invalid connection reference");
11
- }
12
- // Cross-module reference (`!ref Alias.Connection`): a connection resolved
13
- // inside a nested library is not Phase-5-injected, so the controller receives
14
- // the raw `{name, alias}` ref and must route through the import's exported
15
- // scope rather than a bare local lookup.
16
- if (ref.alias && ref.alias !== "Self") {
17
- const instance = ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name);
18
- if (typeof instance?.execute !== "function") {
19
- throw new Error(`Sql: connection reference '${ref.alias}.${ref.name}' did not resolve to an exported connection instance.`);
20
- }
21
- return instance;
22
- }
23
- return ctx.moduleContext.getInstance(ref.name);
14
+ return ctx.resolveRef(value, isSqlConnection, describe, "std/sql#Connection");
24
15
  }
@@ -32,7 +32,7 @@ class SqlMigrationsResource {
32
32
  this.ctx = ctx;
33
33
  }
34
34
  async run() {
35
- const conn = resolveSqlConnection(this.manifest.connection, this.ctx) ?? failMissingConnection();
35
+ const conn = resolveSqlConnection(this.manifest.connection, this.ctx, () => `Sql.Migrations "${this.manifest.metadata.name}": 'connection'`) ?? failMissingConnection();
36
36
  const migrations = {};
37
37
  // Legacy: standalone `Sql.Migration` resources in the same module scope.
38
38
  for (const [, { resource }] of this.ctx.moduleContext.resourceInstances) {
@@ -10,13 +10,15 @@ class SqlQueryResource {
10
10
  async invoke(input) {
11
11
  const m = this.manifest;
12
12
  const ctx = this.ctx;
13
- const connection = resolveConnection(m.connection, m.transaction, ctx);
13
+ const connection = resolveConnection(m.connection, m.transaction, ctx, () => `Sql.Query "${m.metadata.name}": 'connection'`);
14
14
  const result = await runSql(connection, m.transaction, input, ctx);
15
15
  return { rows: result.rows, rowCount: result.rows.length };
16
16
  }
17
17
  }
18
- function resolveConnection(connection, transaction, ctx) {
19
- return (resolveSqlConnection(connection, ctx) ?? transaction?.getConnection() ?? failMissingConnection());
18
+ function resolveConnection(connection, transaction, ctx, describe) {
19
+ return (resolveSqlConnection(connection, ctx, describe) ??
20
+ transaction?.getConnection() ??
21
+ failMissingConnection());
20
22
  }
21
23
  function failMissingConnection() {
22
24
  throw new Error("Sql: either 'connection' or 'transaction' must be set");
@@ -58,12 +58,12 @@ interface SelectManifest {
58
58
  offset?: unknown;
59
59
  inputType?: string | Record<string, any>;
60
60
  }
61
- declare class SqlSelectResource implements ResourceInstance {
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<SqlSelectResource>;
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 SqlSelectResource {
3
+ class SqlSelectionResource {
4
4
  manifest;
5
5
  ctx;
6
6
  constructor(manifest, ctx) {
@@ -19,9 +19,10 @@ class SqlSelectResource {
19
19
  const having = ctx.expandValue(m.having ?? [], expandCtx);
20
20
  const limit = m.limit != null ? ctx.expandValue(m.limit, expandCtx) : undefined;
21
21
  const offset = m.offset != null ? ctx.expandValue(m.offset, expandCtx) : undefined;
22
- const connection = resolveSqlConnection(m.connection, ctx) ?? m.transaction?.getConnection();
22
+ const connection = resolveSqlConnection(m.connection, ctx, () => `Sql.Selection "${m.metadata.name}": 'connection'`) ??
23
+ m.transaction?.getConnection();
23
24
  if (!connection) {
24
- throw new Error("Sql.Select: either 'connection' or 'transaction' must be set");
25
+ throw new Error("Sql.Selection: either 'connection' or 'transaction' must be set");
25
26
  }
26
27
  const { sql, params } = buildSelect(m, where, having, limit, offset, connection.driver);
27
28
  const result = await connection.execute(sql, params, m.transaction);
@@ -157,7 +158,7 @@ function opToSql(op) {
157
158
  };
158
159
  const sql = map[op];
159
160
  if (!sql)
160
- throw new Error(`Sql.Select: unknown operator '${op}'`);
161
+ throw new Error(`Sql.Selection: unknown operator '${op}'`);
161
162
  return sql;
162
163
  }
163
164
  // ── Exports ───────────────────────────────────────────────────────────────────
@@ -190,5 +191,5 @@ function extractDefaults(inputType, ctx) {
190
191
  }
191
192
  export function register() { }
192
193
  export async function create(resource, ctx) {
193
- return new SqlSelectResource(resource, ctx);
194
+ return new SqlSelectionResource(resource, ctx);
194
195
  }
@@ -8,8 +8,7 @@ export class SqlTransactionResource {
8
8
  this.ctx = ctx;
9
9
  }
10
10
  getConnection() {
11
- return (resolveSqlConnection(this.manifest.connection, this.ctx) ??
12
- failMissingConnection(this.manifest.metadata.name));
11
+ return (resolveSqlConnection(this.manifest.connection, this.ctx, () => `Sql.Transaction "${this.manifest.metadata.name}": 'connection'`) ?? failMissingConnection(this.manifest.metadata.name));
13
12
  }
14
13
  assertActive() {
15
14
  if (!currentTxId()) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/sql",
3
- "version": "0.7.2",
3
+ "version": "0.9.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
- "./sql-postgres-connection": {
26
- "bun": "./src/sql-postgres-connection-controller.ts",
27
- "import": "./dist/sql-postgres-connection-controller.js"
28
+ ".": {
29
+ "types": "./dist/index.d.ts",
30
+ "bun": "./src/index.ts",
31
+ "import": "./dist/index.js"
28
32
  },
29
- "./sql-sqlite-connection": {
30
- "bun": "./src/sql-sqlite-connection-controller.ts",
31
- "import": "./dist/sql-sqlite-connection-controller.js"
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-select": {
38
- "bun": "./src/sql-select-controller.ts",
39
- "import": "./dist/sql-select-controller.js"
42
+ "./sql-selection": {
43
+ "bun": "./src/sql-selection-controller.ts",
44
+ "import": "./dist/sql-selection-controller.js"
40
45
  },
41
- "./sql-exec": {
42
- "bun": "./src/sql-exec-controller.ts",
43
- "import": "./dist/sql-exec-controller.js"
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.26.0"
73
+ "@telorun/sdk": "0.54.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 { isSqlConnection, 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 SqlExecManifest {
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 SqlExecResource implements ResourceInstance {
18
+ class SqlCommandResource implements ResourceInstance {
19
19
  constructor(
20
- private readonly manifest: SqlExecManifest,
20
+ private readonly manifest: SqlCommandManifest,
21
21
  private readonly ctx: ResourceContext,
22
22
  ) {}
23
23
 
@@ -25,7 +25,9 @@ class SqlExecResource implements ResourceInstance {
25
25
  const m = this.manifest;
26
26
  const ctx = this.ctx;
27
27
 
28
- const connection = resolveSqlConnection(m.connection, ctx) ?? m.transaction?.getConnection();
28
+ const connection =
29
+ resolveSqlConnection(m.connection, ctx, () => `Sql.Command "${m.metadata.name}": 'connection'`) ??
30
+ m.transaction?.getConnection();
29
31
  if (!connection) {
30
32
  throw new Error("Sql: either 'connection' or 'transaction' must be set");
31
33
  }
@@ -38,8 +40,8 @@ class SqlExecResource implements ResourceInstance {
38
40
  export function register(): void {}
39
41
 
40
42
  export async function create(
41
- resource: SqlExecManifest,
43
+ resource: SqlCommandManifest,
42
44
  ctx: ResourceContext,
43
- ): Promise<SqlExecResource> {
44
- return new SqlExecResource(resource, ctx);
45
+ ): Promise<SqlCommandResource> {
46
+ return new SqlCommandResource(resource, ctx);
45
47
  }
@@ -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
- export interface SqlConnectionConfig {
31
- driver: SqlDriver;
32
- /** Required for `postgres`; ignored for `sqlite` (which opens via `sqlite`). */
33
- connectionString?: string;
34
- pool?: PoolConfig;
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(config: SqlConnectionConfig, sqlite?: SqliteDb) {
43
- this.driver = config.driver;
44
-
45
- if (this.driver === "postgres") {
46
- if (!config.connectionString) {
47
- throw new Error("Sql: postgres connection requires a connectionString");
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
- // Kysely's stock SQLite adapter reports `supportsTransactionalDdl = false`, so
175
- // its Migrator runs migrations without a transaction. SQLite does support
176
- // transactional DDL, so we flip the flag letting the Migrator wrap the whole
177
- // migration batch in a single transaction, matching PostgreSQL.
178
- class TransactionalSqliteAdapter extends SqliteAdapter {
179
- override get supportsTransactionalDdl(): boolean {
180
- return true;
181
- }
182
- }
183
-
184
- class TransactionalSqliteDialect extends SqliteDialect {
185
- override createAdapter(): SqliteAdapter {
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
  }
@@ -1,41 +1,22 @@
1
- import type { ResourceContext } from "@telorun/sdk";
1
+ import type { KindRef, ResourceContext } from "@telorun/sdk";
2
2
  import type { SqlConnectionResource } from "./sql-connection-controller.js";
3
3
 
4
- interface ConnectionRef {
5
- name: string;
6
- alias?: string;
4
+ /** True when a value already exposes the connection contract (Phase-5 injected). */
5
+ export function isSqlConnection(value: unknown): value is SqlConnectionResource {
6
+ return typeof (value as SqlConnectionResource | undefined)?.execute === "function";
7
7
  }
8
8
 
9
+ /**
10
+ * Resolve a `connection` `!ref` field to a live connection. The slot is optional
11
+ * — an unset one yields `undefined` so the caller can fall back to a `transaction`
12
+ * — but a slot that IS set must resolve. `describe` names the owning resource and
13
+ * slot, so the failure points at a concrete manifest location.
14
+ */
9
15
  export function resolveSqlConnection(
10
- value: SqlConnectionResource | ConnectionRef | undefined,
16
+ value: SqlConnectionResource | KindRef<SqlConnectionResource> | undefined,
11
17
  ctx: ResourceContext,
18
+ describe: () => string,
12
19
  ): SqlConnectionResource | undefined {
13
- if (!value) {
14
- return undefined;
15
- }
16
-
17
- if (typeof (value as SqlConnectionResource).execute === "function") {
18
- return value as SqlConnectionResource;
19
- }
20
-
21
- const ref = value as ConnectionRef;
22
- if (typeof ref.name !== "string") {
23
- throw new Error("Sql: invalid connection reference");
24
- }
25
-
26
- // Cross-module reference (`!ref Alias.Connection`): a connection resolved
27
- // inside a nested library is not Phase-5-injected, so the controller receives
28
- // the raw `{name, alias}` ref and must route through the import's exported
29
- // scope rather than a bare local lookup.
30
- if (ref.alias && ref.alias !== "Self") {
31
- const instance = ctx.moduleContext.resolveImportedInstance(ref.alias, ref.name);
32
- if (typeof (instance as SqlConnectionResource | undefined)?.execute !== "function") {
33
- throw new Error(
34
- `Sql: connection reference '${ref.alias}.${ref.name}' did not resolve to an exported connection instance.`,
35
- );
36
- }
37
- return instance as unknown as SqlConnectionResource;
38
- }
39
-
40
- return ctx.moduleContext.getInstance(ref.name) as SqlConnectionResource;
20
+ if (!value) return undefined;
21
+ return ctx.resolveRef(value, isSqlConnection, describe, "std/sql#Connection");
41
22
  }
@@ -56,7 +56,11 @@ class SqlMigrationsResource implements ResourceInstance {
56
56
 
57
57
  async run(): Promise<void> {
58
58
  const conn =
59
- resolveSqlConnection(this.manifest.connection, this.ctx) ?? failMissingConnection();
59
+ resolveSqlConnection(
60
+ this.manifest.connection,
61
+ this.ctx,
62
+ () => `Sql.Migrations "${this.manifest.metadata.name}": 'connection'`,
63
+ ) ?? failMissingConnection();
60
64
 
61
65
  const migrations: Record<string, string[]> = {};
62
66
  // Legacy: standalone `Sql.Migration` resources in the same module scope.
@@ -28,7 +28,12 @@ class SqlQueryResource implements ResourceInstance {
28
28
  async invoke(input: unknown): Promise<SqlResult> {
29
29
  const m = this.manifest;
30
30
  const ctx = this.ctx;
31
- const connection = resolveConnection(m.connection, m.transaction, ctx);
31
+ const connection = resolveConnection(
32
+ m.connection,
33
+ m.transaction,
34
+ ctx,
35
+ () => `Sql.Query "${m.metadata.name}": 'connection'`,
36
+ );
32
37
  const result = await runSql(connection, m.transaction, input, ctx);
33
38
  return { rows: result.rows, rowCount: result.rows.length };
34
39
  }
@@ -38,9 +43,12 @@ function resolveConnection(
38
43
  connection: SqlConnectionResource | undefined,
39
44
  transaction: SqlTransactionResource | undefined,
40
45
  ctx: ResourceContext,
46
+ describe: () => string,
41
47
  ): SqlConnectionResource {
42
48
  return (
43
- resolveSqlConnection(connection, ctx) ?? transaction?.getConnection() ?? failMissingConnection()
49
+ resolveSqlConnection(connection, ctx, describe) ??
50
+ transaction?.getConnection() ??
51
+ failMissingConnection()
44
52
  );
45
53
  }
46
54
 
@@ -76,7 +76,7 @@ interface SelectManifest {
76
76
 
77
77
  // ── Controller ────────────────────────────────────────────────────────────────
78
78
 
79
- class SqlSelectResource implements ResourceInstance {
79
+ class SqlSelectionResource implements ResourceInstance {
80
80
  constructor(
81
81
  private readonly manifest: SelectManifest,
82
82
  private readonly ctx: ResourceContext,
@@ -96,9 +96,11 @@ class SqlSelectResource implements ResourceInstance {
96
96
  const limit = m.limit != null ? ctx.expandValue(m.limit, expandCtx) : undefined;
97
97
  const offset = m.offset != null ? ctx.expandValue(m.offset, expandCtx) : undefined;
98
98
 
99
- const connection = resolveSqlConnection(m.connection, ctx) ?? m.transaction?.getConnection();
99
+ const connection =
100
+ resolveSqlConnection(m.connection, ctx, () => `Sql.Selection "${m.metadata.name}": 'connection'`) ??
101
+ m.transaction?.getConnection();
100
102
  if (!connection) {
101
- throw new Error("Sql.Select: either 'connection' or 'transaction' must be set");
103
+ throw new Error("Sql.Selection: either 'connection' or 'transaction' must be set");
102
104
  }
103
105
 
104
106
  const { sql, params } = buildSelect(m, where, having, limit, offset, connection.driver);
@@ -266,7 +268,7 @@ function opToSql(op: Op): string {
266
268
  ilike: "ILIKE",
267
269
  };
268
270
  const sql = map[op];
269
- if (!sql) throw new Error(`Sql.Select: unknown operator '${op}'`);
271
+ if (!sql) throw new Error(`Sql.Selection: unknown operator '${op}'`);
270
272
  return sql;
271
273
  }
272
274
 
@@ -306,6 +308,6 @@ export function register(): void {}
306
308
  export async function create(
307
309
  resource: SelectManifest,
308
310
  ctx: ResourceContext,
309
- ): Promise<SqlSelectResource> {
310
- return new SqlSelectResource(resource, ctx);
311
+ ): Promise<SqlSelectionResource> {
312
+ return new SqlSelectionResource(resource, ctx);
311
313
  }
@@ -18,8 +18,11 @@ export class SqlTransactionResource implements ResourceInstance {
18
18
 
19
19
  getConnection(): SqlConnectionResource {
20
20
  return (
21
- resolveSqlConnection(this.manifest.connection, this.ctx) ??
22
- failMissingConnection(this.manifest.metadata.name)
21
+ resolveSqlConnection(
22
+ this.manifest.connection,
23
+ this.ctx,
24
+ () => `Sql.Transaction "${this.manifest.metadata.name}": 'connection'`,
25
+ ) ?? failMissingConnection(this.manifest.metadata.name)
23
26
  );
24
27
  }
25
28
 
@@ -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,2 +0,0 @@
1
- import type { SqliteDb } from "./sqlite-driver-interface.js";
2
- export declare function openDatabase(file: string): SqliteDb;
@@ -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,2 +0,0 @@
1
- import type { SqliteDb } from "./sqlite-driver-interface.js";
2
- export declare function openDatabase(file: string): SqliteDb;
@@ -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
- }
@@ -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
- }