@fadhilp/stateql 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 StateQL contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ # StateQL
2
+
3
+ StateQL is a stateful database CLI for AI agents and automation. It provides a
4
+ safe interface for querying, changing, and inspecting databases while keeping
5
+ results reusable and operations traceable across commands.
6
+
7
+ Requires Node.js 22.5 or newer.
8
+
9
+ ## Quick start
10
+
11
+ Connect to an existing SQLite database, then run a filtered, parameterized
12
+ query. Parameters keep values separate from SQL; `ORDER BY` makes paging
13
+ stable, while `LIMIT` bounds work at the database.
14
+
15
+ ```bash
16
+ npm install --global stateql
17
+ export STQL_SESSION=audit
18
+ stql profile add local ./app.sqlite
19
+ stql connect local
20
+
21
+ stql query \
22
+ "SELECT id, name, email FROM users WHERE status = ? AND created_at >= ? ORDER BY id LIMIT 50" \
23
+ --param active \
24
+ --param 2026-01-01
25
+ ```
26
+
27
+ Example output:
28
+
29
+ ```json
30
+ {"ok":true,"handle":"q_1","rows":[{"id":7,"name":"Ada","email":"ada@example.com"},{"id":12,"name":"Grace","email":"grace@example.com"},{"id":18,"name":"Linus","email":"linus@kernel.org"}],"truncated":false,"cached":false,"total":3,"next_offset":null}
31
+ ```
32
+
33
+ `q_1` is a durable result handle. Filter its stored snapshot without querying
34
+ the original database; the result becomes another durable handle:
35
+
36
+ ```bash
37
+ stql filter q_1 "email LIKE ?" --param "%@example.com"
38
+ ```
39
+
40
+ ```json
41
+ {"ok":true,"handle":"q_2","rows":[{"id":7,"name":"Ada","email":"ada@example.com"},{"id":12,"name":"Grace","email":"grace@example.com"}],"truncated":false,"cached":false,"total":2,"next_offset":null}
42
+ ```
43
+
44
+ Give the derived result a readable alias, page through it, inspect its count,
45
+ or export it—all without rerunning SQL:
46
+
47
+ ```bash
48
+ stql alias set example-users q_2
49
+ stql rows example-users --offset 0 --limit 1
50
+ stql rows example-users --offset 1 --limit 1
51
+ stql count example-users
52
+ stql export example-users --output example-users.csv --format csv
53
+ ```
54
+
55
+ Example first page:
56
+
57
+ ```json
58
+ {"ok":true,"handle":"q_2","rows":[{"id":7,"name":"Ada","email":"ada@example.com"}],"total":2,"truncated":true,"next_offset":1}
59
+ ```
60
+
61
+ Running the same normalized query with the same parameters reuses `q_1` while
62
+ its cache is valid. Use `--cache bypass` when a fresh read is required.
63
+
64
+ PostgreSQL credentials should come from an environment variable:
65
+
66
+ ```bash
67
+ export APP_DATABASE_URL='postgres://user:password@host/app'
68
+ stql connect --env APP_DATABASE_URL --name app --read-only
69
+ ```
70
+
71
+ ## Commands
72
+
73
+ ```text
74
+ stql connect <sqlite-path|postgres-url> [--name NAME] [--env ENV] [--read-write]
75
+ stql connect --profile NAME
76
+ stql status
77
+ stql profile add|list|show|remove
78
+ stql session start|list|show|summary|close
79
+ stql query <sql> [--params JSON | --param VALUE...] [--cache auto|bypass|require]
80
+ stql filter <result-handle> <predicate> [--params JSON | --param VALUE...]
81
+ stql exec <sql> [--params JSON | --param VALUE...] [--idempotency-key KEY] [--replay]
82
+ [--allow-unbounded] [--allow-destructive]
83
+ stql show|count|columns <result-handle>
84
+ stql rows <result-handle> [--offset N] [--limit N]
85
+ stql export <result-handle> --output FILE [--format json|jsonl|csv]
86
+ stql inspect schema|table|columns|indexes|constraints [table]
87
+ stql transaction begin|status|commit|rollback [--isolation LEVEL]
88
+ stql plan <sql> [--allow-unbounded] [--allow-destructive]
89
+ stql apply <plan-handle>
90
+ stql history [--limit N]
91
+ stql receipt <operation-handle>
92
+ stql capabilities
93
+ stql batch [commands.json|commands.jsonl|-] [--continue-on-error]
94
+ stql pipe [--continue-on-error]
95
+ ```
96
+
97
+ ## Output modes
98
+
99
+ CLI output defaults to compact, one-line `agent` JSON. Successes flatten useful
100
+ data and expose the primary durable ID as `handle`; errors retain their complete
101
+ error object. Empty warnings and tracing metadata are omitted.
102
+
103
+ ```json
104
+ {"ok":false,"error":{"code":"UNBOUNDED_MUTATION","message":"Mutation has no WHERE clause.","retryable":false,"executed":false,"override_flag":"--allow-unbounded"}}
105
+ ```
106
+
107
+ Use `--output json` for the original pretty, verbose envelope, or
108
+ `--output jsonl` for that envelope on one line. `--output text` prints a short
109
+ human status; `--output silent` prints only a successful handle. Set
110
+ `STQL_OUTPUT` to choose a mode globally. For `export`, `--output` names the
111
+ file, so use `STQL_OUTPUT` to choose its response mode. Library responses keep
112
+ the full envelope regardless of CLI mode.
113
+
114
+ For shell-safe positional parameters, repeat `--param`. JSON scalars become
115
+ their native types; other values remain strings.
116
+
117
+ ```powershell
118
+ stql exec "INSERT INTO users (name, status) VALUES (?, ?)" `
119
+ --param Ada --param trial
120
+ ```
121
+
122
+ Use `--params-file FILE` for arrays or named parameters that are awkward to
123
+ quote. `--params-file -` reads JSON from standard input.
124
+
125
+ ## Local profiles
126
+
127
+ Profiles persist under `STQL_HOME` with other StateQL metadata.
128
+
129
+ ```bash
130
+ stql profile add local ./app.sqlite --read-write
131
+ stql profile add production --env PROD_DATABASE_URL --read-only
132
+ stql profile list
133
+ stql connect local
134
+ stql connect --profile production
135
+ ```
136
+
137
+ A bare connection target matching a profile name resolves to that profile;
138
+ otherwise it remains a path or database URL. Profiles store targets, read-only
139
+ policy, and environment-variable names. Credential values are never stored.
140
+
141
+ ## Batch and pipes
142
+
143
+ `batch` reads a JSON array from a `.json` file or JSONL from a `.jsonl` file.
144
+ `pipe` reads JSONL from standard input. Commands run sequentially and stop on
145
+ the first error unless `--continue-on-error` is set. Output defaults to one
146
+ compact `agent` JSON object per line.
147
+
148
+ ```bash
149
+ printf '%s\n' \
150
+ '{"command":"query","sql":"SELECT id, email FROM users ORDER BY id","as":"users"}' \
151
+ '{"command":"filter","handle":"users","where":"email LIKE ?","params":["%@example.com"],"as":"example_users"}' \
152
+ '{"command":"rows","handle":"example_users","limit":10}' |
153
+ stql pipe
154
+ ```
155
+
156
+ ```json
157
+ [
158
+ {
159
+ "command": "exec",
160
+ "sql": "UPDATE jobs SET claimed = 1 WHERE id = ?",
161
+ "params": [42],
162
+ "idempotency_key": "claim-job-42"
163
+ },
164
+ {
165
+ "command": "query",
166
+ "sql": "SELECT * FROM jobs WHERE id = ?",
167
+ "params": [42]
168
+ }
169
+ ]
170
+ ```
171
+
172
+ Run the array with `stql batch commands.json`. Batch fields use snake case;
173
+ supported command names match CLI paths, such as `filter`,
174
+ `transaction.begin`, `session.summary`, `alias.set`, `plan`, and `apply`.
175
+ Batch filters use `where` for the predicate and may assign the derived result
176
+ with `as`.
177
+
178
+ State metadata lives under `STQL_HOME`, or the platform data directory when
179
+ unset. Set `STQL_SESSION` to select a named session.
180
+
181
+ Read cache entries expire after five minutes; materialized handles expire after
182
+ 24 hours. Queries exceeding 10,000 rows fail before materialization; add a
183
+ narrower `WHERE` clause or `LIMIT`. Command history keeps the latest 10,000
184
+ entries per session. SQLite cache reuse also checks
185
+ the database file signature; PostgreSQL reuse is labeled `ttl_based`, never
186
+ authoritative. Transactions are staged in local state so they survive CLI
187
+ invocations, then executed atomically on commit. Connections cannot be changed
188
+ or disconnected while a transaction is active. SQLite supports `serializable`;
189
+ PostgreSQL also supports `repeatable read`, `read committed`, and
190
+ `read uncommitted`. PostgreSQL reads run inside database-enforced read-only
191
+ transactions.
192
+
193
+ StateQL stores no PostgreSQL password. Credential-bearing URLs must be supplied
194
+ through `--env`. SQLite result rows are materialized locally for durable access.
195
+ `filter` evaluates one scalar SQLite predicate against those stored rows, keeps
196
+ source order, state metadata, and expiry, and never accesses the original
197
+ database. Use parameters for values. Subqueries, query-shaping clauses, and
198
+ non-allowlisted functions are rejected; common deterministic functions such as
199
+ `lower`, `upper`, `length`, and `coalesce` are supported.
200
+
201
+ Destructive and unbounded operations require their respective flags
202
+ independently. Plans persist only flags explicitly supplied when the plan is
203
+ created; `apply` never adds authorization. If a database write starts but its
204
+ final outcome cannot be recorded safely, StateQL returns `OUTCOME_UNKNOWN` and
205
+ blocks automatic replay. Inspect database state before using `--replay`.
206
+ Interrupted commits remain fail-closed; stale `committing` records become
207
+ `outcome_unknown` after five minutes.
208
+
209
+ ## Library
210
+
211
+ ```ts
212
+ import { StateQL } from "stateql";
213
+
214
+ const stateql = new StateQL({ home: "./.stql" });
215
+ const response = await stateql.query("SELECT * FROM users");
216
+ if (response.ok) {
217
+ const handle = (response.data as { result_id: string }).result_id;
218
+ await stateql.filter(handle, "email LIKE ?", {
219
+ params: ["%@example.com"],
220
+ });
221
+ }
222
+ ```
@@ -0,0 +1,23 @@
1
+ import type { Column, Row, SqlParameters, StateConfidence } from "./types.js";
2
+ import type { ConnectionRecord, OperationRecord } from "./store.js";
3
+ export interface ReadResult {
4
+ rows: Row[];
5
+ columns: Column[];
6
+ }
7
+ export interface WriteResult {
8
+ affectedRows: number;
9
+ }
10
+ export declare class BatchWriteError extends Error {
11
+ readonly outcomeUnknown: boolean;
12
+ constructor(message: string, outcomeUnknown: boolean);
13
+ }
14
+ export interface Adapter {
15
+ readonly confidence: StateConfidence;
16
+ read(sql: string, params: SqlParameters): Promise<ReadResult>;
17
+ write(sql: string, params: SqlParameters): Promise<WriteResult>;
18
+ writeBatch(operations: OperationRecord[], isolation: string): Promise<WriteResult[]>;
19
+ signature(): Promise<string>;
20
+ inspect(kind: string, table?: string): Promise<unknown>;
21
+ close(): Promise<void>;
22
+ }
23
+ export declare function createAdapter(connection: ConnectionRecord): Promise<Adapter>;
@@ -0,0 +1,346 @@
1
+ import { existsSync, statSync } from "node:fs";
2
+ import { DatabaseSync } from "node:sqlite";
3
+ import { Client, types as pgTypes } from "pg";
4
+ import { hash, parseJson, toJsonSafe } from "./util.js";
5
+ export class BatchWriteError extends Error {
6
+ outcomeUnknown;
7
+ constructor(message, outcomeUnknown) {
8
+ super(message);
9
+ this.outcomeUnknown = outcomeUnknown;
10
+ this.name = "BatchWriteError";
11
+ }
12
+ }
13
+ export async function createAdapter(connection) {
14
+ const source = connection.secret_env
15
+ ? process.env[connection.secret_env]
16
+ : connection.source;
17
+ if (!source) {
18
+ throw new Error(`Environment variable ${connection.secret_env ?? "(missing)"} is not set.`);
19
+ }
20
+ if (connection.driver === "sqlite") {
21
+ return new SQLiteAdapter(source, Boolean(connection.read_only));
22
+ }
23
+ return new PostgresAdapter(source, Boolean(connection.read_only));
24
+ }
25
+ class SQLiteAdapter {
26
+ source;
27
+ readOnly;
28
+ confidence = "database_reported";
29
+ db;
30
+ constructor(source, readOnly) {
31
+ this.source = source;
32
+ this.readOnly = readOnly;
33
+ this.db = new DatabaseSync(source, {
34
+ readOnly,
35
+ enableForeignKeyConstraints: true,
36
+ });
37
+ this.db.exec("PRAGMA busy_timeout = 5000");
38
+ }
39
+ async read(sql, params) {
40
+ const statement = this.db.prepare(sql);
41
+ const rows = bindAll(statement, params);
42
+ return {
43
+ rows: toJsonSafe(rows),
44
+ columns: statement.columns().map((column) => ({
45
+ name: column.name,
46
+ type: column.type?.toLowerCase() ?? inferType(rows, column.name),
47
+ })),
48
+ };
49
+ }
50
+ async write(sql, params) {
51
+ if (this.readOnly)
52
+ throw new Error("Connection is read-only.");
53
+ const result = bindRun(this.db.prepare(sql), params);
54
+ return { affectedRows: Number(result.changes) };
55
+ }
56
+ async writeBatch(operations, isolation) {
57
+ if (this.readOnly)
58
+ throw new Error("Connection is read-only.");
59
+ if (isolation !== "serializable") {
60
+ throw new Error(`SQLite does not support isolation level "${isolation}".`);
61
+ }
62
+ const results = [];
63
+ try {
64
+ this.db.exec("BEGIN");
65
+ }
66
+ catch (error) {
67
+ throw new BatchWriteError(errorText(error), false);
68
+ }
69
+ try {
70
+ for (const operation of operations) {
71
+ results.push(await this.write(operation.sql, parseJson(operation.parameters, [])));
72
+ }
73
+ }
74
+ catch (error) {
75
+ try {
76
+ this.db.exec("ROLLBACK");
77
+ }
78
+ catch {
79
+ throw new BatchWriteError(errorText(error), true);
80
+ }
81
+ throw new BatchWriteError(errorText(error), false);
82
+ }
83
+ try {
84
+ this.db.exec("COMMIT");
85
+ return results;
86
+ }
87
+ catch (error) {
88
+ try {
89
+ this.db.exec("ROLLBACK");
90
+ throw new BatchWriteError(errorText(error), false);
91
+ }
92
+ catch (rollbackError) {
93
+ if (rollbackError instanceof BatchWriteError)
94
+ throw rollbackError;
95
+ throw new BatchWriteError(errorText(error), true);
96
+ }
97
+ }
98
+ }
99
+ async signature() {
100
+ if (this.source === ":memory:")
101
+ return "memory";
102
+ const stats = statSync(this.source, { bigint: true });
103
+ const walPath = `${this.source}-wal`;
104
+ const wal = existsSync(walPath)
105
+ ? statSync(walPath, { bigint: true })
106
+ : undefined;
107
+ return hash({
108
+ size: stats.size.toString(),
109
+ modified: stats.mtimeNs.toString(),
110
+ walSize: wal?.size.toString() ?? "0",
111
+ walModified: wal?.mtimeNs.toString() ?? "0",
112
+ });
113
+ }
114
+ async inspect(kind, table) {
115
+ if (kind === "schema") {
116
+ const tables = this.db
117
+ .prepare(`SELECT name, type
118
+ FROM sqlite_master
119
+ WHERE type IN ('table', 'view') AND name NOT LIKE 'sqlite_%'
120
+ ORDER BY name`)
121
+ .all();
122
+ return { schema: "main", tables };
123
+ }
124
+ if (!table)
125
+ throw new Error(`Table is required for inspect ${kind}.`);
126
+ const quoted = quoteSqliteLiteral(table);
127
+ const columns = this.db
128
+ .prepare(`PRAGMA table_info(${quoted})`)
129
+ .all();
130
+ if (columns.length === 0)
131
+ throw new Error(`Table "${table}" was not found.`);
132
+ const indexes = this.db
133
+ .prepare(`PRAGMA index_list(${quoted})`)
134
+ .all();
135
+ const foreignKeys = this.db
136
+ .prepare(`PRAGMA foreign_key_list(${quoted})`)
137
+ .all();
138
+ if (kind === "columns")
139
+ return { table, columns };
140
+ if (kind === "indexes")
141
+ return { table, indexes };
142
+ if (kind === "constraints") {
143
+ return {
144
+ table,
145
+ primary_key: columns
146
+ .filter((column) => Number(column.pk) > 0)
147
+ .map((column) => column.name),
148
+ foreign_keys: foreignKeys,
149
+ };
150
+ }
151
+ if (kind !== "table")
152
+ throw new Error(`Unknown inspection kind "${kind}".`);
153
+ return {
154
+ table,
155
+ schema: "main",
156
+ columns: columns.map((column) => ({
157
+ name: column.name,
158
+ type: String(column.type).toLowerCase(),
159
+ nullable: column.notnull === 0 && Number(column.pk) === 0,
160
+ primary_key: Number(column.pk) > 0,
161
+ })),
162
+ indexes: indexes.length,
163
+ foreign_keys: foreignKeys.length,
164
+ };
165
+ }
166
+ async close() {
167
+ this.db.close();
168
+ }
169
+ }
170
+ class PostgresAdapter {
171
+ readOnly;
172
+ confidence = "ttl_based";
173
+ client;
174
+ connected = false;
175
+ constructor(source, readOnly) {
176
+ this.readOnly = readOnly;
177
+ this.client = new Client({ connectionString: source });
178
+ }
179
+ async read(sql, params) {
180
+ await this.connect();
181
+ await this.client.query("BEGIN READ ONLY");
182
+ try {
183
+ const result = await this.client.query(sql, postgresParams(params));
184
+ await this.client.query("COMMIT");
185
+ return {
186
+ rows: toJsonSafe(result.rows),
187
+ columns: result.fields.map((field) => ({
188
+ name: field.name,
189
+ type: pgTypes.getTypeParser(field.dataTypeID).name ||
190
+ `oid_${field.dataTypeID}`,
191
+ })),
192
+ };
193
+ }
194
+ catch (error) {
195
+ await this.client.query("ROLLBACK");
196
+ throw error;
197
+ }
198
+ }
199
+ async write(sql, params) {
200
+ if (this.readOnly)
201
+ throw new Error("Connection is read-only.");
202
+ await this.connect();
203
+ const result = await this.client.query(sql, postgresParams(params));
204
+ return { affectedRows: result.rowCount ?? 0 };
205
+ }
206
+ async writeBatch(operations, isolation) {
207
+ if (this.readOnly)
208
+ throw new Error("Connection is read-only.");
209
+ await this.connect();
210
+ const level = isolation.toUpperCase();
211
+ if (!POSTGRES_ISOLATION_LEVELS.has(level)) {
212
+ throw new Error(`Unsupported PostgreSQL isolation level "${isolation}".`);
213
+ }
214
+ try {
215
+ await this.client.query(`BEGIN ISOLATION LEVEL ${level}`);
216
+ }
217
+ catch (error) {
218
+ throw new BatchWriteError(errorText(error), false);
219
+ }
220
+ const results = [];
221
+ try {
222
+ for (const operation of operations) {
223
+ results.push(await this.write(operation.sql, parseJson(operation.parameters, [])));
224
+ }
225
+ }
226
+ catch (error) {
227
+ try {
228
+ await this.client.query("ROLLBACK");
229
+ }
230
+ catch {
231
+ throw new BatchWriteError(errorText(error), true);
232
+ }
233
+ throw new BatchWriteError(errorText(error), false);
234
+ }
235
+ try {
236
+ await this.client.query("COMMIT");
237
+ return results;
238
+ }
239
+ catch (error) {
240
+ try {
241
+ await this.client.query("ROLLBACK");
242
+ }
243
+ catch {
244
+ // COMMIT response was lost; rollback cannot establish outcome.
245
+ }
246
+ throw new BatchWriteError(errorText(error), true);
247
+ }
248
+ }
249
+ async signature() {
250
+ return "ttl";
251
+ }
252
+ async inspect(kind, table) {
253
+ await this.connect();
254
+ if (kind === "schema") {
255
+ const result = await this.client.query(`SELECT table_schema AS schema, table_name AS name, table_type AS type
256
+ FROM information_schema.tables
257
+ WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
258
+ ORDER BY table_schema, table_name`);
259
+ return { tables: result.rows };
260
+ }
261
+ if (!table)
262
+ throw new Error(`Table is required for inspect ${kind}.`);
263
+ const [schema, name] = table.includes(".")
264
+ ? table.split(".", 2)
265
+ : ["public", table];
266
+ const columns = await this.client.query(`SELECT column_name AS name, data_type AS type,
267
+ is_nullable = 'YES' AS nullable
268
+ FROM information_schema.columns
269
+ WHERE table_schema = $1 AND table_name = $2
270
+ ORDER BY ordinal_position`, [schema, name]);
271
+ if (columns.rows.length === 0) {
272
+ throw new Error(`Table "${table}" was not found.`);
273
+ }
274
+ if (kind === "columns")
275
+ return { table: name, schema, columns: columns.rows };
276
+ const indexes = await this.client.query(`SELECT indexname AS name, indexdef AS definition
277
+ FROM pg_indexes WHERE schemaname = $1 AND tablename = $2
278
+ ORDER BY indexname`, [schema, name]);
279
+ const constraints = await this.client.query(`SELECT constraint_name AS name, constraint_type AS type
280
+ FROM information_schema.table_constraints
281
+ WHERE table_schema = $1 AND table_name = $2
282
+ ORDER BY constraint_name`, [schema, name]);
283
+ if (kind === "indexes")
284
+ return { table: name, schema, indexes: indexes.rows };
285
+ if (kind === "constraints") {
286
+ return { table: name, schema, constraints: constraints.rows };
287
+ }
288
+ if (kind !== "table")
289
+ throw new Error(`Unknown inspection kind "${kind}".`);
290
+ return {
291
+ table: name,
292
+ schema,
293
+ columns: columns.rows,
294
+ indexes: indexes.rows.length,
295
+ constraints: constraints.rows.length,
296
+ };
297
+ }
298
+ async close() {
299
+ if (this.connected)
300
+ await this.client.end();
301
+ }
302
+ async connect() {
303
+ if (this.connected)
304
+ return;
305
+ await this.client.connect();
306
+ this.connected = true;
307
+ if (this.readOnly) {
308
+ await this.client.query("SET default_transaction_read_only = on");
309
+ }
310
+ }
311
+ }
312
+ const POSTGRES_ISOLATION_LEVELS = new Set([
313
+ "SERIALIZABLE",
314
+ "REPEATABLE READ",
315
+ "READ COMMITTED",
316
+ "READ UNCOMMITTED",
317
+ ]);
318
+ function bindAll(statement, params) {
319
+ if (Array.isArray(params))
320
+ return statement.all(...params);
321
+ return statement.all(params);
322
+ }
323
+ function bindRun(statement, params) {
324
+ if (Array.isArray(params))
325
+ return statement.run(...params);
326
+ return statement.run(params);
327
+ }
328
+ function postgresParams(params) {
329
+ if (Array.isArray(params))
330
+ return params;
331
+ throw new Error("PostgreSQL parameters must be a JSON array.");
332
+ }
333
+ function errorText(error) {
334
+ return error instanceof Error ? error.message : String(error);
335
+ }
336
+ function inferType(rows, name) {
337
+ const value = rows.find((row) => row[name] !== null)?.[name];
338
+ if (value === undefined)
339
+ return "unknown";
340
+ if (Buffer.isBuffer(value))
341
+ return "binary";
342
+ return typeof value;
343
+ }
344
+ function quoteSqliteLiteral(value) {
345
+ return `'${value.replaceAll("'", "''")}'`;
346
+ }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};