@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 +70 -81
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/sqlite-store.d.ts +39 -2
- package/dist/@types/sqlite-store.d.ts.map +1 -1
- package/dist/index.cjs +246 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +246 -15
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -4,153 +4,142 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/@rotorsoft/act-sqlite)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
6
|
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
```
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
## Usage
|
|
21
|
+
## Quick start
|
|
22
22
|
|
|
23
|
-
```
|
|
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
|
-
//
|
|
28
|
+
// File-based persistence
|
|
29
29
|
store(new SqliteStore({ url: "file:myapp.db" }));
|
|
30
30
|
|
|
31
|
-
//
|
|
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
|
-
|
|
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: "
|
|
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:` |
|
|
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-
|
|
60
|
+
### File-based persistence
|
|
55
61
|
|
|
56
|
-
```
|
|
62
|
+
```ts
|
|
57
63
|
store(new SqliteStore({ url: "file:data/events.db" }));
|
|
58
64
|
```
|
|
59
65
|
|
|
60
|
-
### In-
|
|
66
|
+
### In-memory (tests / quick experiments)
|
|
61
67
|
|
|
62
|
-
```
|
|
68
|
+
```ts
|
|
63
69
|
store(new SqliteStore()); // defaults to file::memory:
|
|
64
70
|
```
|
|
65
71
|
|
|
66
|
-
### Turso (
|
|
72
|
+
### Turso (edge)
|
|
67
73
|
|
|
68
|
-
```
|
|
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
|
-
##
|
|
81
|
+
## Common patterns
|
|
82
|
+
|
|
83
|
+
### Schema setup
|
|
76
84
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
|
|
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
|
-
|
|
91
|
+
### Concurrency model
|
|
88
92
|
|
|
89
|
-
|
|
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
|
-
|
|
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
|
-
|
|
97
|
+
Created by `seed()`:
|
|
106
98
|
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
113
|
+
Both adapters pass the same `runStoreTck` suite. Application code doesn't change between them; only the bootstrap line differs.
|
|
116
114
|
|
|
117
|
-
|
|
115
|
+
## What's intentionally not implemented
|
|
118
116
|
|
|
119
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
127
|
+
## Stability
|
|
133
128
|
|
|
134
|
-
|
|
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
|
-
|
|
139
|
-
name: "SqliteStore",
|
|
140
|
-
factory: () => new SqliteStore({ url: "file:tck-store.db" }),
|
|
141
|
-
});
|
|
142
|
-
```
|
|
131
|
+
## Related packages
|
|
143
132
|
|
|
144
|
-
|
|
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
|
-
##
|
|
137
|
+
## Documentation
|
|
147
138
|
|
|
148
|
-
- [
|
|
149
|
-
- [
|
|
150
|
-
- [
|
|
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
|
-
|
|
145
|
+
MIT
|