@rotorsoft/act-sqlite 0.7.0 → 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.
package/README.md CHANGED
@@ -4,153 +4,142 @@
4
4
  [![NPM Downloads](https://img.shields.io/npm/dm/@rotorsoft/act-sqlite.svg)](https://www.npmjs.com/package/@rotorsoft/act-sqlite)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- SQLite event store adapter for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act). Provides persistent, file-based event storage with ACID guarantees via [`@libsql/client`](https://github.com/tursodatabase/libsql-client-ts). Ideal for single-server deployments, edge functions, and embedded applications.
7
+ _SQLite event store for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act) via [`@libsql/client`](https://github.com/tursodatabase/libsql-client-ts). File-based, edge-ready, ACID — for single-node deployments. Lane-aware claim/ack via `streams.lane` + `streams_lane_ix` since v0.9.0 ([ACT-1103](https://github.com/Rotorsoft/act-root/issues/733))._
8
8
 
9
- > **Stability:** Public API governed by the [Act Stability Charter](../../STABILITY.md). Charter takes effect at 1.0 (gated on [milestone 1.0](https://github.com/Rotorsoft/act-root/milestone/1)).
9
+ ## Why this package
10
+
11
+ Not every Act app needs Postgres. Single-server apps, embedded deployments, edge functions, and unit tests all want the same thing: a real event store with ACID guarantees, but no operational overhead. `SqliteStore` is that — `@libsql/client` under the hood (zero native bindings, browser-incompatible parts already stripped), full conformance with Act's `Store` port, the same one-line bootstrap swap.
12
+
13
+ SQLite serializes all writes at the database level. For a single-server deployment this gives you the same isolation guarantees as Postgres's `FOR UPDATE SKIP LOCKED` without any coordination layer. When you outgrow that — multi-server distributed processing, sub-poll cross-process wakeup — swap in `@rotorsoft/act-pg`. Application code doesn't change.
10
14
 
11
15
  ## Installation
12
16
 
13
- ```sh
14
- npm install @rotorsoft/act @rotorsoft/act-sqlite
15
- # or
17
+ ```bash
16
18
  pnpm add @rotorsoft/act @rotorsoft/act-sqlite
17
19
  ```
18
20
 
19
- **Requirements:** Node.js >= 22.18.0
20
-
21
- ## Usage
21
+ ## Quick start
22
22
 
23
- ```typescript
23
+ ```ts
24
24
  import { act, state, store } from "@rotorsoft/act";
25
25
  import { SqliteStore } from "@rotorsoft/act-sqlite";
26
26
  import { z } from "zod";
27
27
 
28
- // Inject the SQLite store before building your app
28
+ // File-based persistence
29
29
  store(new SqliteStore({ url: "file:myapp.db" }));
30
30
 
31
- // Initialize tables (creates events table, streams table, and indexes)
31
+ // One-time schema setup (idempotent safe to leave in your bootstrap).
32
32
  await store().seed();
33
33
 
34
- // Build and use your app as normal
35
34
  const Counter = state({ Counter: z.object({ count: z.number() }) })
36
35
  .init(() => ({ count: 0 }))
37
36
  .emits({ Incremented: z.object({ amount: z.number() }) })
38
37
  .patch({ Incremented: ({ data }, s) => ({ count: s.count + data.amount }) })
39
38
  .on({ increment: z.object({ by: z.number() }) })
40
- .emit((action) => ["Incremented", { amount: action.by }])
39
+ .emit((a) => ["Incremented", { amount: a.by }])
41
40
  .build();
42
41
 
43
42
  const app = act().withState(Counter).build();
44
- await app.do("increment", { stream: "counter1", actor: { id: "1", name: "User" } }, { by: 1 });
43
+ await app.do("increment", { stream: "c1", actor: { id: "1", name: "u" } }, { by: 1 });
45
44
  ```
46
45
 
46
+ ## API
47
+
48
+ - **`SqliteStore`** — class implementing Act's `Store` port. Construct once, pass to `store()`.
49
+ - **`SqliteConfig`** — constructor options (`url`, `authToken`).
50
+
51
+ Full type reference: [typedoc](https://github.com/Rotorsoft/act-root/blob/master/docs/docs/api/act-sqlite/src/README.md).
52
+
47
53
  ## Configuration
48
54
 
49
55
  | Option | Default | Description |
50
- |--------|---------|-------------|
51
- | `url` | `file::memory:` | SQLite connection URL. Use `file:path.db` for persistent storage. |
56
+ |---|---|---|
57
+ | `url` | `file::memory:` | libSQL connection URL. Use `file:path.db` for persistent file, `libsql://…` for Turso. |
52
58
  | `authToken` | — | Auth token for libSQL server connections (Turso). |
53
59
 
54
- ### File-Based Storage
60
+ ### File-based persistence
55
61
 
56
- ```typescript
62
+ ```ts
57
63
  store(new SqliteStore({ url: "file:data/events.db" }));
58
64
  ```
59
65
 
60
- ### In-Memory (Testing)
66
+ ### In-memory (tests / quick experiments)
61
67
 
62
- ```typescript
68
+ ```ts
63
69
  store(new SqliteStore()); // defaults to file::memory:
64
70
  ```
65
71
 
66
- ### Turso (Edge)
72
+ ### Turso (edge)
67
73
 
68
- ```typescript
74
+ ```ts
69
75
  store(new SqliteStore({
70
76
  url: process.env.TURSO_URL!,
71
77
  authToken: process.env.TURSO_AUTH_TOKEN,
72
78
  }));
73
79
  ```
74
80
 
75
- ## Features
81
+ ## Common patterns
82
+
83
+ ### Schema setup
76
84
 
77
- - **ACID Transactions** — All write operations use SQLite write transactions for atomicity
78
- - **Optimistic Concurrency** — Version-based conflict detection prevents lost updates
79
- - **WAL Mode** — Write-Ahead Logging enables concurrent readers during writes
80
- - **Serialized Writes** — SQLite's single-writer model guarantees mutual exclusion (equivalent to `FOR UPDATE SKIP LOCKED` for single-server use)
81
- - **Auto Schema Setup** — `seed()` creates all required tables and indexes
82
- - **Zero Dependencies** — Only requires `@libsql/client` (no native bindings)
83
- - **Edge-Ready** — Works with Turso for distributed SQLite at the edge
85
+ ```ts
86
+ await store().seed();
87
+ ```
84
88
 
85
- ## Database Schema
89
+ Idempotent. Creates the events table, the streams (subscription) table, and the indexes that support claim ordering. PRAGMA `journal_mode=WAL` is set at the same time so readers don't block writers. Safe to leave in your bootstrap.
86
90
 
87
- Calling `seed()` creates two tables:
91
+ ### Concurrency model
88
92
 
89
- **Events table** (`events`)stores all committed events:
90
- - `id` (INTEGER PRIMARY KEY) — global event sequence (autoincrement)
91
- - `name` — event type name
92
- - `data` (TEXT/JSON) — event payload
93
- - `stream` — stream identifier
94
- - `version` — per-stream sequence number
95
- - `created` — ISO 8601 timestamp
96
- - `meta` (TEXT/JSON) — correlation, causation, and actor metadata
93
+ SQLite serializes write transactions at the database level. No application-layer locking, no `FOR UPDATE SKIP LOCKED` needed writes queue automatically and `ack`/`block` validate `leased_by` to prevent stale workers from interfering. For a single-server deployment, this gives the same isolation guarantees as Postgres.
97
94
 
98
- **Streams table** (`streams`) — tracks stream processing state:
99
- - `stream` — stream identifier (PRIMARY KEY)
100
- - `source` — source stream pattern for reactions
101
- - `at` — last processed event position (watermark)
102
- - `leased_by` / `leased_until` — processing claim info
103
- - `blocked` / `error` — error tracking for failed streams
95
+ ### Database schema reference
104
96
 
105
- ## Concurrency Model
97
+ Created by `seed()`:
106
98
 
107
- SQLite serializes all write transactions at the database level. This means:
99
+ - **Events** (`events`): `id` (INTEGER PRIMARY KEY AUTOINCREMENT), `name`, `data` (TEXT/JSON), `stream`, `version`, `created` (ISO 8601), `meta` (TEXT/JSON). Unique index on `(stream, version)`.
100
+ - **Streams** (`streams`): `stream` (PK), `source`, `at`, `retry`, `blocked`, `error`, `leased_by`, `leased_until`, `priority`. Composite index on `(blocked, priority DESC, at)`.
108
101
 
109
- - **No lock contention** write transactions queue automatically
110
- - **Equivalent guarantees** — for single-server deployments, this provides the same isolation as PostgreSQL's `FOR UPDATE SKIP LOCKED`
111
- - **Lease ownership** — `ack()` and `block()` validate `leased_by` to prevent stale workers from interfering
102
+ ## When to use this vs `act-pg`
112
103
 
113
- For multi-server deployments requiring distributed stream processing, use [@rotorsoft/act-pg](https://www.npmjs.com/package/@rotorsoft/act-pg) instead.
104
+ | You want… | Use |
105
+ |---|---|
106
+ | Single server / embedded / edge | `act-sqlite` |
107
+ | Zero infrastructure setup (file path is the config) | `act-sqlite` |
108
+ | Edge runtime with Turso replication | `act-sqlite` (with Turso URL) |
109
+ | Multi-server, distributed processing | `act-pg` |
110
+ | Sub-poll cross-process reaction latency | `act-pg` (with `notify: true`) |
111
+ | Heavy write contention across many writers | `act-pg` |
114
112
 
115
- ## What's *not* implemented
113
+ Both adapters pass the same `runStoreTck` suite. Application code doesn't change between them; only the bootstrap line differs.
116
114
 
117
- - **`Store.notify`** is intentionally absent. The notify hook is a cross-process wake-up signal that lets a horizontally-scaled Act deployment skip the polling lag on remote commits. SQLite is single-node by design — there's no remote writer to be notified of — so the {@link Act} orchestrator falls back to the existing debounce/poll path, which is correct for this topology. If you outgrow that, switch to `@rotorsoft/act-pg`.
115
+ ## What's intentionally not implemented
118
116
 
119
- ## SQLite vs PostgreSQL
117
+ **`Store.notify`** is absent. The notify hook is a cross-process wake-up signal that lets a horizontally-scaled deployment skip polling lag on remote commits. SQLite is single-node by design — there's no remote writer to be notified of — so the Act orchestrator falls back to the existing debounce/poll path, which is correct for this topology. If you outgrow it, switch to `@rotorsoft/act-pg`.
120
118
 
121
- | Feature | act-sqlite | act-pg |
122
- |---------|-----------|--------|
123
- | Deployment | Single server, edge | Multi-server, distributed |
124
- | Setup | Zero config (file path) | Connection pool config |
125
- | Concurrency | Serialized writes | `FOR UPDATE SKIP LOCKED` |
126
- | JSON storage | TEXT + `json_extract()` | Native JSONB |
127
- | Streaming | Callback pattern | Callback pattern |
128
- | Performance | Fast for moderate loads | Scales horizontally |
119
+ ## Compatibility
129
120
 
130
- ## Testing
121
+ - **Node**: >=22.18.0
122
+ - **Peer**: `@rotorsoft/act` >=0.39.0, `zod` ^4.4.3
123
+ - **Bundled deps**: `@libsql/client` ^0.17.3 (no native bindings)
124
+ - **Module formats**: ESM + CJS
125
+ - **Runtimes**: Node, Bun, Deno (libSQL pure-TS implementation); also runs in Turso-compatible edge environments
131
126
 
132
- Validated against the executable Store contract in [`@rotorsoft/act-tck`](https://www.npmjs.com/package/@rotorsoft/act-tck):
127
+ ## Stability
133
128
 
134
- ```ts
135
- import { runStoreTck } from "@rotorsoft/act-tck";
136
- import { SqliteStore } from "@rotorsoft/act-sqlite";
129
+ Public API governed by the [Act Stability Charter](../../STABILITY.md). Charter takes effect at 1.0 (gated on [milestone 1.0](https://github.com/Rotorsoft/act-root/milestone/1)).
137
130
 
138
- runStoreTck({
139
- name: "SqliteStore",
140
- factory: () => new SqliteStore({ url: "file:tck-store.db" }),
141
- });
142
- ```
131
+ ## Related packages
143
132
 
144
- See [Writing a custom Store adapter](https://github.com/Rotorsoft/act-root/blob/master/docs/docs/guides/writing-a-store.md) for the third-party authoring guide.
133
+ - **[@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act)** the framework whose `Store` port this implements.
134
+ - **[@rotorsoft/act-pg](https://www.npmjs.com/package/@rotorsoft/act-pg)** — sibling store adapter for multi-server / distributed deployments.
135
+ - **[@rotorsoft/act-tck](https://www.npmjs.com/package/@rotorsoft/act-tck)** — conformance suite. `SqliteStore` passes `runStoreTck`.
145
136
 
146
- ## Related
137
+ ## Documentation
147
138
 
148
- - [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act) — Core framework
149
- - [@rotorsoft/act-pg](https://www.npmjs.com/package/@rotorsoft/act-pg) — PostgreSQL adapter
150
- - [@rotorsoft/act-tck](https://www.npmjs.com/package/@rotorsoft/act-tck) — Test Compatibility Kit
151
- - [Documentation](https://rotorsoft.github.io/act-root/)
152
- - [Examples](https://github.com/rotorsoft/act-root/tree/master/packages)
139
+ - **[Production checklist](https://rotorsoft.github.io/act-root/docs/guides/production-checklist)**operator-facing guide; the SQLite path is called out where it differs from the PG path.
140
+ - **[Concurrency model](https://rotorsoft.github.io/act-root/docs/architecture/concurrency-model)**lease lifecycle, single-writer guarantees, optimistic concurrency.
141
+ - **[Writing a custom Store adapter](https://rotorsoft.github.io/act-root/docs/guides/writing-a-store)**for authors building against other databases; `SqliteStore` is one of the reference implementations.
153
142
 
154
143
  ## License
155
144
 
156
- [MIT](https://github.com/rotorsoft/act-root/blob/master/LICENSE)
145
+ MIT