@rotorsoft/act-sqlite 0.8.0 → 1.0.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/README.md CHANGED
@@ -4,153 +4,144 @@
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
76
82
 
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
83
+ ### Schema setup
84
84
 
85
- ## Database Schema
85
+ ```ts
86
+ await store().seed();
87
+ ```
86
88
 
87
- Calling `seed()` creates two tables:
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.
88
90
 
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
91
+ ### Concurrency model
97
92
 
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
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.
104
94
 
105
- ## Concurrency Model
95
+ ### Database schema reference
106
96
 
107
- SQLite serializes all write transactions at the database level. This means:
97
+ Created by `seed()`:
108
98
 
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
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)`.
112
101
 
113
- For multi-server deployments requiring distributed stream processing, use [@rotorsoft/act-pg](https://www.npmjs.com/package/@rotorsoft/act-pg) instead.
102
+ ## When to use this vs `act-pg`
114
103
 
115
- ## What's *not* implemented
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` |
116
112
 
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`.
113
+ Both adapters pass the same `runStoreTck` suite. Application code doesn't change between them; only the bootstrap line differs.
118
114
 
119
- ## SQLite vs PostgreSQL
115
+ ## What's intentionally not implemented
120
116
 
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 |
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`.
129
118
 
130
- ## Testing
119
+ ## Compatibility
131
120
 
132
- Validated against the executable Store contract in [`@rotorsoft/act-tck`](https://www.npmjs.com/package/@rotorsoft/act-tck):
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
133
126
 
134
- ```ts
135
- import { runStoreTck } from "@rotorsoft/act-tck";
136
- import { SqliteStore } from "@rotorsoft/act-sqlite";
127
+ ## Stability
137
128
 
138
- runStoreTck({
139
- name: "SqliteStore",
140
- factory: () => new SqliteStore({ url: "file:tck-store.db" }),
141
- });
142
- ```
129
+ Public API governed by the [Act Stability Charter](../../STABILITY.md). `SqliteStore` implements the `Store` contract from `@rotorsoft/act` and is validated against `@rotorsoft/act-tck` on `@libsql/client` pinned + latest in CI. Charter is **in effect as of 1.0.0**; the milestone tracker is [milestone 1.0](https://github.com/Rotorsoft/act-root/milestone/1).
130
+
131
+ > **Versioning note.** Version `1.0.0` is reserved on the npm registry from a prior publish and cannot be republished. The first 1.x release of this package on npm is **`1.0.1`**; its public surface is identical to the intended 1.0.0 cut.
132
+
133
+ ## Related packages
143
134
 
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.
135
+ - **[@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act)** the framework whose `Store` port this implements.
136
+ - **[@rotorsoft/act-pg](https://www.npmjs.com/package/@rotorsoft/act-pg)** — sibling store adapter for multi-server / distributed deployments.
137
+ - **[@rotorsoft/act-tck](https://www.npmjs.com/package/@rotorsoft/act-tck)** — conformance suite. `SqliteStore` passes `runStoreTck`.
145
138
 
146
- ## Related
139
+ ## Documentation
147
140
 
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)
141
+ - **[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.
142
+ - **[Concurrency model](https://rotorsoft.github.io/act-root/docs/architecture/concurrency-model)**lease lifecycle, single-writer guarantees, optimistic concurrency.
143
+ - **[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
144
 
154
145
  ## License
155
146
 
156
- [MIT](https://github.com/rotorsoft/act-root/blob/master/LICENSE)
147
+ MIT