@rindle/optimistic 0.4.3 → 0.5.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 +30 -61
- package/dist/backend.d.ts +881 -26
- package/dist/backend.d.ts.map +1 -1
- package/dist/backend.js +2033 -205
- package/dist/backend.js.map +1 -1
- package/dist/client.d.ts +156 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +848 -8
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/local-persist.d.ts +102 -0
- package/dist/local-persist.d.ts.map +1 -0
- package/dist/local-persist.js +957 -0
- package/dist/local-persist.js.map +1 -0
- package/dist/system-streams.d.ts +62 -0
- package/dist/system-streams.d.ts.map +1 -0
- package/dist/system-streams.js +107 -0
- package/dist/system-streams.js.map +1 -0
- package/package.json +6 -5
- package/src/backend.ts +2476 -198
- package/src/client.ts +1174 -13
- package/src/index.ts +51 -0
- package/src/local-persist.ts +1129 -0
- package/src/system-streams.ts +140 -0
package/README.md
CHANGED
|
@@ -1,70 +1,39 @@
|
|
|
1
1
|
# @rindle/optimistic
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The browser store for a synced Rindle app: optimistic writes over the local-first wasm
|
|
4
|
+
engine. `createRindleClient` boots the wasm engine, opens the ws subscription to the
|
|
5
|
+
daemon, resolves query leases through your API server, and runs the mutation queue.
|
|
4
6
|
|
|
5
|
-
The local IVM engine **is** the rebase function:
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
the client glue around that engine cycle:
|
|
10
|
-
|
|
11
|
-
- **Named client mutators** (`ClientRegistry`): `(tx, args) => void` against a
|
|
12
|
-
`MutationTx` (`get`/`add`/`remove`/`edit`; reads see the current base + the
|
|
13
|
-
transaction's own writes). Deterministic and replayable — a mutator is re-invoked on
|
|
14
|
-
every rebase, so a read-dependent `incrementScore(+5)` computed on a stale `10`
|
|
15
|
-
correctly becomes `105` after the server lands `100`.
|
|
16
|
-
- **cv-buffering** (§8.5): per-query data frames are `cv`-tagged and buffer; a
|
|
17
|
-
connection-level progress frame `{ cvMin, lmid, rejected }` releases everything
|
|
18
|
-
`cv ≤ cvMin` as one coherent step — no torn multi-query reads, ever. Buffer
|
|
19
|
-
overflow drops + re-hydrates (the one new client obligation).
|
|
20
|
-
- **Pending stack** of `(mid, name, args)` — never effects; `lmid` confirms (drop),
|
|
21
|
-
`rejected` snaps back (the engine cycle removes the phantom effects).
|
|
22
|
-
- **`ResultType`** per query (§6/§7): the **server channel's** state only — `unknown`
|
|
23
|
-
while not hydrated (the server hasn't answered yet), `complete` once it has. A pending
|
|
24
|
-
mutation no longer moves it; `error` is reserved and currently unproduced.
|
|
25
|
-
- **Pending axis** (§7.2): "is an unconfirmed prediction touching this query?" is its own
|
|
26
|
-
reactive signal — `pending(qid)` / `onPending` / `pendingTables()` — so a "saving…"
|
|
27
|
-
indicator is separate from loading.
|
|
28
|
-
- **Local-only tables**: `table("draft", { local: true })` declares client-authoritative
|
|
29
|
-
state that never syncs, rebases, or appears in the normalized server schema. Use
|
|
30
|
-
`store.writeLocal(...)` for selections, drafts, and view prefs; replayable predicted
|
|
31
|
-
mutators are intentionally barred from reading/writing local-only tables.
|
|
32
|
-
- **Folded mutations** (FOLDED-MUTATIONS-DESIGN): `mutate.foo.folded({ key, debounceMs,
|
|
33
|
-
maxWaitMs }, args)` collapses a run of high-frequency same-key writes (a slider drag) into
|
|
34
|
-
**one** debounced, last-value-wins server write while the local view still updates on every
|
|
35
|
-
call. Folded mutators must be **absorbing** (last-writer-wins); a read-dependent one is
|
|
36
|
-
refused. `flushFolds()` (also auto-fired on `pagehide`/`beforeunload`) drains in-flight folds.
|
|
7
|
+
The local IVM engine **is** the rebase function: it keeps an authoritative fork per base
|
|
8
|
+
table and reconciles every server batch by **rewinding** to the authoritative state and
|
|
9
|
+
**re-invoking** the still-pending mutators against it — no inverse mutations, no rollback
|
|
10
|
+
code. A rejected write snaps back on its own.
|
|
37
11
|
|
|
38
12
|
```ts
|
|
39
|
-
import {
|
|
40
|
-
import {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const q = newQueryBuilder(schema);
|
|
50
|
-
const queries = {
|
|
51
|
-
allIssues: defineQuery("allIssues", () => q.issue.orderBy("id", "asc")),
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
const { store, backend, mutate } = createOptimisticStore(schema, source, registry, {
|
|
55
|
-
clientID: "client-1",
|
|
13
|
+
import { createRindleClient } from "@rindle/optimistic";
|
|
14
|
+
import { mutators, schema } from "../shared/app-def.ts"; // isomorphic mutators + generated schema
|
|
15
|
+
|
|
16
|
+
export const app = await createRindleClient({
|
|
17
|
+
schema,
|
|
18
|
+
mutators, // one generator body per mutator, run on both tiers
|
|
19
|
+
user: () => currentUser(), // the acting principal a mutator sees as ctx.user
|
|
20
|
+
api: { url: "" }, // your API server (named queries + mutations)
|
|
21
|
+
daemon: { wsUrl: "ws://127.0.0.1:7601" }, // the daemon's PUBLIC subscription port
|
|
56
22
|
});
|
|
57
23
|
|
|
58
|
-
|
|
59
|
-
mutate.createIssue({ id: 7, title: "instant" }); // applies NOW; reconciles when confirmed
|
|
60
|
-
backend.resultType(/* queryId */ 1); // "unknown" (loading) → "complete"
|
|
61
|
-
backend.pending(/* queryId */ 1); // is an optimistic write pending on this query?
|
|
24
|
+
app.mutate.createIssue({ id, title: "ship it", createdAt: Date.now() }); // applies NOW, rebases on confirm
|
|
62
25
|
```
|
|
63
26
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
27
|
+
Also in the box: **folded mutations** (collapse a slider drag into one last-value-wins
|
|
28
|
+
server write), **local-only tables** (`{ local: true }` — client state that never syncs
|
|
29
|
+
or rebases, written with `store.writeLocal`), the per-query **`ResultType`** axis
|
|
30
|
+
(loading vs. server-answered) and the separate **pending** axis (is an unconfirmed write
|
|
31
|
+
touching this query?).
|
|
32
|
+
|
|
33
|
+
## Docs
|
|
34
|
+
|
|
35
|
+
Full docs — wiring, optimistic writes & rebase, folded writes, local-only tables, and the
|
|
36
|
+
loading/pending signals: **[rindle.sh/docs/client](https://rindle.sh/docs/client)** ·
|
|
37
|
+
markdown mirror: [`client.md`](https://rindle.sh/docs/client.md) · the mutator contract:
|
|
38
|
+
[rindle.sh/docs/mutators](https://rindle.sh/docs/mutators) · for agents:
|
|
39
|
+
[llms.txt](https://rindle.sh/llms.txt)
|