@telorun/sql-sqlite 0.2.1 → 0.3.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 CHANGED
@@ -44,146 +44,7 @@ $ telo ./examples/hello-api
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.12.0
59
- Sql: std/sql@0.9.2
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
 
@@ -1,5 +1,7 @@
1
1
  import type { ResourceContext } from "@telorun/sdk";
2
- import { type SqlConnectionResource } from "@telorun/sql";
2
+ import { SqlConnectionBase } from "@telorun/sql";
3
+ import { Kysely } from "kysely";
4
+ import type { SqliteDb } from "./sqlite-driver-interface.js";
3
5
  interface SqliteConnectionManifest {
4
6
  metadata: {
5
7
  name: string;
@@ -8,6 +10,13 @@ interface SqliteConnectionManifest {
8
10
  /** File path, or omitted / `:memory:` for an in-memory database. */
9
11
  file?: string;
10
12
  }
13
+ declare class SqliteConnection extends SqlConnectionBase {
14
+ private readonly sqlite;
15
+ constructor(db: Kysely<any>, sqlite: SqliteDb);
16
+ /** The driver's native multi-statement entry point — kysely binds one
17
+ * statement per call. */
18
+ executeScript(sql: string): Promise<void>;
19
+ }
11
20
  export declare function register(): void;
12
- export declare function create(resource: SqliteConnectionManifest, _ctx: ResourceContext): Promise<SqlConnectionResource>;
21
+ export declare function create(resource: SqliteConnectionManifest, _ctx: ResourceContext): Promise<SqliteConnection>;
13
22
  export {};
@@ -1,5 +1,26 @@
1
- import { createSqlConnection } from "@telorun/sql";
1
+ import { quoteAnsiIdentifier, SqlConnectionBase } from "@telorun/sql";
2
2
  import { Kysely, SqliteAdapter, SqliteDialect } from "kysely";
3
+ const sqliteDialect = {
4
+ placeholderStyle: "qmark",
5
+ quoteIdentifier: quoteAnsiIdentifier,
6
+ // SQLite has no array type, so set membership expands to one placeholder per
7
+ // element.
8
+ renderIn(column, values, addParam) {
9
+ return `${column} IN (${values.map((value) => addParam(value)).join(", ")})`;
10
+ },
11
+ };
12
+ class SqliteConnection extends SqlConnectionBase {
13
+ sqlite;
14
+ constructor(db, sqlite) {
15
+ super(db, sqliteDialect);
16
+ this.sqlite = sqlite;
17
+ }
18
+ /** The driver's native multi-statement entry point — kysely binds one
19
+ * statement per call. */
20
+ async executeScript(sql) {
21
+ this.sqlite.exec(sql);
22
+ }
23
+ }
3
24
  // Kysely's stock SQLite adapter reports `supportsTransactionalDdl = false`, so
4
25
  // its Migrator runs migrations without a transaction. SQLite does support
5
26
  // transactional DDL, so we flip the flag — letting the Migrator wrap the whole
@@ -42,5 +63,5 @@ export async function create(resource, _ctx) {
42
63
  const db = new Kysely({
43
64
  dialect: new TransactionalSqliteDialect({ database: sqlite }),
44
65
  });
45
- return createSqlConnection("sqlite", db, sqlite);
66
+ return new SqliteConnection(db, sqlite);
46
67
  }
@@ -1,2 +1,2 @@
1
- import type { SqliteDb } from "@telorun/sql";
1
+ import type { SqliteDb } from "./sqlite-driver-interface.js";
2
2
  export declare function openDatabase(file: string): SqliteDb;
@@ -0,0 +1,14 @@
1
+ export interface SqliteStatement {
2
+ readonly reader: boolean;
3
+ all(params: ReadonlyArray<unknown>): unknown[];
4
+ run(params: ReadonlyArray<unknown>): {
5
+ changes: number | bigint;
6
+ lastInsertRowid: number | bigint;
7
+ };
8
+ iterate(params: ReadonlyArray<unknown>): IterableIterator<unknown>;
9
+ }
10
+ export interface SqliteDb {
11
+ prepare(sql: string): SqliteStatement;
12
+ exec(sql: string): void;
13
+ close(): void;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,2 +1,2 @@
1
- import type { SqliteDb } from "@telorun/sql";
1
+ import type { SqliteDb } from "./sqlite-driver-interface.js";
2
2
  export declare function openDatabase(file: string): SqliteDb;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telorun/sql-sqlite",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Telo SqlSqlite.Connection — SQLite backend for the Sql.Connection abstract (better-sqlite3 / bun:sqlite, transactional DDL).",
5
5
  "keywords": [
6
6
  "telo",
@@ -36,14 +36,14 @@
36
36
  "dependencies": {
37
37
  "better-sqlite3": "^12.8.0",
38
38
  "kysely": "^0.28.15",
39
- "@telorun/sql": "0.8.0"
39
+ "@telorun/sql": "0.10.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/better-sqlite3": "^7.0.0",
43
43
  "@types/bun": "^1.3.10",
44
44
  "@types/node": "^20.0.0",
45
45
  "typescript": "^5.0.0",
46
- "@telorun/sdk": "0.34.0"
46
+ "@telorun/sdk": "0.58.0"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@telorun/sdk": "*"
@@ -1,6 +1,7 @@
1
1
  import type { ResourceContext } from "@telorun/sdk";
2
- import { createSqlConnection, type SqlConnectionResource, type SqliteDb } from "@telorun/sql";
2
+ import { quoteAnsiIdentifier, SqlConnectionBase, type SqlDialect } from "@telorun/sql";
3
3
  import { Kysely, SqliteAdapter, SqliteDialect } from "kysely";
4
+ import type { SqliteDb } from "./sqlite-driver-interface.js";
4
5
 
5
6
  interface SqliteConnectionManifest {
6
7
  metadata: { name: string; module: string };
@@ -8,6 +9,31 @@ interface SqliteConnectionManifest {
8
9
  file?: string;
9
10
  }
10
11
 
12
+ const sqliteDialect: SqlDialect = {
13
+ placeholderStyle: "qmark",
14
+ quoteIdentifier: quoteAnsiIdentifier,
15
+ // SQLite has no array type, so set membership expands to one placeholder per
16
+ // element.
17
+ renderIn(column, values, addParam) {
18
+ return `${column} IN (${values.map((value) => addParam(value)).join(", ")})`;
19
+ },
20
+ };
21
+
22
+ class SqliteConnection extends SqlConnectionBase {
23
+ constructor(
24
+ db: Kysely<any>,
25
+ private readonly sqlite: SqliteDb,
26
+ ) {
27
+ super(db, sqliteDialect);
28
+ }
29
+
30
+ /** The driver's native multi-statement entry point — kysely binds one
31
+ * statement per call. */
32
+ override async executeScript(sql: string): Promise<void> {
33
+ this.sqlite.exec(sql);
34
+ }
35
+ }
36
+
11
37
  // Kysely's stock SQLite adapter reports `supportsTransactionalDdl = false`, so
12
38
  // its Migrator runs migrations without a transaction. SQLite does support
13
39
  // transactional DDL, so we flip the flag — letting the Migrator wrap the whole
@@ -53,10 +79,10 @@ export function register(): void {}
53
79
  export async function create(
54
80
  resource: SqliteConnectionManifest,
55
81
  _ctx: ResourceContext,
56
- ): Promise<SqlConnectionResource> {
82
+ ): Promise<SqliteConnection> {
57
83
  const sqlite = await openSqliteDatabase(resource.file ?? ":memory:");
58
84
  const db = new Kysely<any>({
59
85
  dialect: new TransactionalSqliteDialect({ database: sqlite }),
60
86
  });
61
- return createSqlConnection("sqlite", db, sqlite);
87
+ return new SqliteConnection(db, sqlite);
62
88
  }
@@ -1,5 +1,5 @@
1
1
  import { Database } from "bun:sqlite";
2
- import type { SqliteDb } from "@telorun/sql";
2
+ import type { SqliteDb } from "./sqlite-driver-interface.js";
3
3
 
4
4
  export function openDatabase(file: string): SqliteDb {
5
5
  const db = new Database(file);
@@ -0,0 +1,15 @@
1
+ export interface SqliteStatement {
2
+ readonly reader: boolean;
3
+ all(params: ReadonlyArray<unknown>): unknown[];
4
+ run(params: ReadonlyArray<unknown>): {
5
+ changes: number | bigint;
6
+ lastInsertRowid: number | bigint;
7
+ };
8
+ iterate(params: ReadonlyArray<unknown>): IterableIterator<unknown>;
9
+ }
10
+
11
+ export interface SqliteDb {
12
+ prepare(sql: string): SqliteStatement;
13
+ exec(sql: string): void;
14
+ close(): void;
15
+ }
@@ -1,5 +1,5 @@
1
1
  import Database from "better-sqlite3";
2
- import type { SqliteDb } from "@telorun/sql";
2
+ import type { SqliteDb } from "./sqlite-driver-interface.js";
3
3
 
4
4
  export function openDatabase(file: string): SqliteDb {
5
5
  const db = new Database(file);