@fadhilp/stateql 0.1.2 → 0.2.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
@@ -64,17 +64,23 @@ Example first page:
64
64
  Running the same normalized query with the same parameters reuses `q_1` while
65
65
  its cache is valid. Use `--cache bypass` when a fresh read is required.
66
66
 
67
- PostgreSQL credentials should come from an environment variable:
67
+ PostgreSQL and MySQL credentials should come from environment variables:
68
68
 
69
69
  ```bash
70
70
  export APP_DATABASE_URL='postgres://user:password@host/app'
71
71
  stql connect --env APP_DATABASE_URL --name app --read-only
72
+
73
+ export MYSQL_DATABASE_URL='mysql://user:password@host/app'
74
+ stql connect --env MYSQL_DATABASE_URL --name mysql-app --read-only
72
75
  ```
73
76
 
77
+ MySQL uses positional `?` parameters. MariaDB compatibility is not currently
78
+ claimed.
79
+
74
80
  ## Commands
75
81
 
76
82
  ```text
77
- stql connect <sqlite-path|postgres-url> [--name NAME] [--env ENV] [--read-write]
83
+ stql connect <sqlite-path|postgres-url|mysql-url> [--name NAME] [--env ENV] [--read-write]
78
84
  stql connect --profile NAME
79
85
  stql status
80
86
  stql profile add|list|show|remove
@@ -97,6 +103,13 @@ stql batch [commands.json|commands.jsonl|-] [--continue-on-error]
97
103
  stql pipe [--continue-on-error]
98
104
  ```
99
105
 
106
+ Database commands accept `--timeout-ms N`; default is 30,000 ms. `Ctrl+C`
107
+ cancels active work. SQLite runs in a killable child process so long synchronous
108
+ statements cannot block StateQL's event loop. PostgreSQL uses server-side
109
+ `statement_timeout` plus client deadlines. MySQL deadlines destroy the active
110
+ connection. A timed-out write may return `OUTCOME_UNKNOWN` when commit status
111
+ cannot be proven.
112
+
100
113
  ## Output modes
101
114
 
102
115
  CLI output defaults to compact, one-line `agent` JSON. Successes flatten useful
@@ -176,25 +189,31 @@ Run the array with `stql batch commands.json`. Batch fields use snake case;
176
189
  supported command names match CLI paths, such as `filter`,
177
190
  `transaction.begin`, `session.summary`, `alias.set`, `plan`, and `apply`.
178
191
  Batch filters use `where` for the predicate and may assign the derived result
179
- with `as`.
192
+ with `as`. Database commands may set `timeout_ms`; otherwise they use the
193
+ 30-second default.
180
194
 
181
195
  State metadata lives under `STQL_HOME`, or the platform data directory when
182
196
  unset. Set `STQL_SESSION` to select a named session.
183
197
 
184
198
  Read cache entries expire after five minutes; materialized handles expire after
185
- 24 hours. Queries exceeding 10,000 rows fail before materialization; add a
186
- narrower `WHERE` clause or `LIMIT`. Command history keeps the latest 10,000
187
- entries per session. SQLite cache reuse also checks
188
- the database file signature; PostgreSQL reuse is labeled `ttl_based`, never
189
- authoritative. Transactions are staged in local state so they survive CLI
190
- invocations, then executed atomically on commit. Connections cannot be changed
191
- or disconnected while a transaction is active. SQLite supports `serializable`;
192
- PostgreSQL also supports `repeatable read`, `read committed`, and
193
- `read uncommitted`. PostgreSQL reads run inside database-enforced read-only
194
- transactions.
195
-
196
- StateQL stores no PostgreSQL password. Credential-bearing URLs must be supplied
197
- through `--env`. SQLite result rows are materialized locally for durable access.
199
+ 24 hours. Expired results and plans are deleted when StateQL next opens. Queries
200
+ exceeding 10,000 rows or 16 MiB of serialized row data fail before persistence;
201
+ add a narrower `WHERE` clause, `LIMIT`, or smaller column selection. These caps
202
+ bound persisted materialization, while the independent deadline bounds execution
203
+ time. Command history keeps the latest 10,000 entries per session. SQLite cache reuse also checks
204
+ the database file signature; PostgreSQL and MySQL reuse is labeled `ttl_based`,
205
+ never authoritative. Transactions are staged in local state so they survive CLI
206
+ invocations, then executed atomically on commit. Database reads, plans,
207
+ connection changes, and disconnects are rejected while a transaction is active;
208
+ commit or roll back first. SQLite supports `serializable`;
209
+ PostgreSQL and MySQL also support `repeatable read`, `read committed`, and
210
+ `read uncommitted`. Server reads run inside database-enforced read-only
211
+ transactions. MySQL staged transactions reject DDL because MySQL implicitly
212
+ commits those statements.
213
+
214
+ StateQL stores no PostgreSQL or MySQL password. Credential-bearing URLs must be
215
+ supplied through `--env`. SQLite result rows are materialized locally for
216
+ durable access.
198
217
  `filter` evaluates one scalar SQLite predicate against those stored rows, keeps
199
218
  source order, state metadata, and expiry, and never accesses the original
200
219
  database. Use parameters for values. Subqueries, query-shaping clauses, and
@@ -212,10 +231,18 @@ Interrupted commits remain fail-closed; stale `committing` records become
212
231
  ## Library
213
232
 
214
233
  ```ts
215
- import { StateQL } from "stateql";
216
-
217
- const stateql = new StateQL({ home: "./.stql" });
218
- const response = await stateql.query("SELECT * FROM users");
234
+ import { StateQL } from "@fadhilp/stateql";
235
+
236
+ const stateql = new StateQL({
237
+ home: "./.stql",
238
+ timeoutMs: 30_000,
239
+ maxResultBytes: 16 * 1024 * 1024,
240
+ });
241
+ const controller = new AbortController();
242
+ const response = await stateql.query("SELECT * FROM users", {
243
+ signal: controller.signal,
244
+ timeoutMs: 5_000,
245
+ });
219
246
  if (response.ok) {
220
247
  const handle = (response.data as { result_id: string }).result_id;
221
248
  await stateql.filter(handle, "email LIKE ?", {
@@ -7,10 +7,23 @@ export interface ReadResult {
7
7
  export interface WriteResult {
8
8
  affectedRows: number;
9
9
  }
10
+ export interface AdapterContext {
11
+ deadline: number;
12
+ signal?: AbortSignal;
13
+ }
14
+ export declare class AdapterExecutionError extends Error {
15
+ readonly reason: "timeout" | "aborted";
16
+ readonly outcomeUnknown: boolean;
17
+ constructor(message: string, reason: "timeout" | "aborted", outcomeUnknown: boolean);
18
+ }
10
19
  export declare class BatchWriteError extends Error {
11
20
  readonly outcomeUnknown: boolean;
12
21
  constructor(message: string, outcomeUnknown: boolean);
13
22
  }
23
+ export declare class AdapterWriteError extends Error {
24
+ readonly outcomeUnknown: boolean;
25
+ constructor(message: string, outcomeUnknown: boolean);
26
+ }
14
27
  export interface Adapter {
15
28
  readonly confidence: StateConfidence;
16
29
  read(sql: string, params: SqlParameters): Promise<ReadResult>;
@@ -20,4 +33,5 @@ export interface Adapter {
20
33
  inspect(kind: string, table?: string): Promise<unknown>;
21
34
  close(): Promise<void>;
22
35
  }
23
- export declare function createAdapter(connection: ConnectionRecord): Promise<Adapter>;
36
+ export declare function createAdapterContext(timeoutMs: number, signal?: AbortSignal): AdapterContext;
37
+ export declare function createAdapter(connection: ConnectionRecord, context: AdapterContext): Promise<Adapter>;