@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 +72 -81
- package/dist/.tsbuildinfo +1 -1
- package/dist/@types/sqlite-store.d.ts +2 -1
- package/dist/@types/sqlite-store.d.ts.map +1 -1
- package/dist/index.cjs +48 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +48 -15
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -4,153 +4,144 @@
|
|
|
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
|
|
76
82
|
|
|
77
|
-
|
|
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
|
-
|
|
85
|
+
```ts
|
|
86
|
+
await store().seed();
|
|
87
|
+
```
|
|
86
88
|
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
95
|
+
### Database schema reference
|
|
106
96
|
|
|
107
|
-
|
|
97
|
+
Created by `seed()`:
|
|
108
98
|
|
|
109
|
-
- **
|
|
110
|
-
- **
|
|
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
|
-
|
|
102
|
+
## When to use this vs `act-pg`
|
|
114
103
|
|
|
115
|
-
|
|
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
|
-
|
|
113
|
+
Both adapters pass the same `runStoreTck` suite. Application code doesn't change between them; only the bootstrap line differs.
|
|
118
114
|
|
|
119
|
-
##
|
|
115
|
+
## What's intentionally not implemented
|
|
120
116
|
|
|
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 |
|
|
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
|
-
##
|
|
119
|
+
## Compatibility
|
|
131
120
|
|
|
132
|
-
|
|
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
|
-
|
|
135
|
-
import { runStoreTck } from "@rotorsoft/act-tck";
|
|
136
|
-
import { SqliteStore } from "@rotorsoft/act-sqlite";
|
|
127
|
+
## Stability
|
|
137
128
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
139
|
+
## Documentation
|
|
147
140
|
|
|
148
|
-
- [
|
|
149
|
-
- [
|
|
150
|
-
- [
|
|
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
|
-
|
|
147
|
+
MIT
|