@rindle/client 0.4.3 → 0.4.4
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 +31 -161
- package/dist/schema.d.ts +30 -6
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +35 -5
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +10 -3
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +34 -14
- package/dist/store.js.map +1 -1
- package/dist/types.d.ts +8 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/view.d.ts +56 -7
- package/dist/view.d.ts.map +1 -1
- package/dist/view.js +319 -18
- package/dist/view.js.map +1 -1
- package/package.json +1 -1
- package/src/schema.ts +50 -9
- package/src/store.ts +36 -13
- package/src/types.ts +13 -3
- package/src/view.ts +316 -17
package/README.md
CHANGED
|
@@ -5,174 +5,44 @@ The shared core for the flat-change client: a **typed schema + query builder**,
|
|
|
5
5
|
the **`Store`**, and the **`Backend`** seam. It talks to no engine directly — pair it with a
|
|
6
6
|
backend such as [`@rindle/wasm`](../wasm) (local, in-process) or a remote (network) backend.
|
|
7
7
|
|
|
8
|
-
## Schema + queries
|
|
9
|
-
|
|
10
8
|
```ts
|
|
11
|
-
import {
|
|
12
|
-
table,
|
|
13
|
-
string,
|
|
14
|
-
number,
|
|
15
|
-
boolean,
|
|
16
|
-
json,
|
|
17
|
-
createSchema,
|
|
18
|
-
defineRelationships,
|
|
19
|
-
rel,
|
|
20
|
-
extendSchema,
|
|
21
|
-
eq,
|
|
22
|
-
gt,
|
|
23
|
-
ilike,
|
|
24
|
-
or,
|
|
25
|
-
exists,
|
|
26
|
-
} from "@rindle/client";
|
|
9
|
+
import { table, string, number, createSchema, gt } from "@rindle/client";
|
|
27
10
|
|
|
28
11
|
const issue = table("issue")
|
|
29
|
-
.columns({ id: number(), title: string(), priority: number()
|
|
30
|
-
.primaryKey("id");
|
|
31
|
-
|
|
32
|
-
const comment = table("comment")
|
|
33
|
-
.columns({ id: number(), issueId: number(), body: string() })
|
|
34
|
-
.primaryKey("id");
|
|
35
|
-
|
|
36
|
-
const schema = createSchema({ tables: [issue, comment] }); // pass to `new Store(schema, backend)`
|
|
37
|
-
|
|
38
|
-
const rels = defineRelationships({
|
|
39
|
-
issueComments: rel(issue, comment, { id: "issueId" }),
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
- **Operators** are named value-functions: `eq / ne / gt / ge / lt / le / like / ilike / inList / …`.
|
|
44
|
-
- **`where`** is a typed proxy — `where.closed(false)`, `where.priority(gt(3))` — with camelCase
|
|
45
|
-
sugar `whereClosed(false)`. Multiple `where`s are AND-ed.
|
|
46
|
-
- **`or` / `and` / `exists`** are plain top-level functions (no closure): conditions carry their
|
|
47
|
-
table type, e.g. `where(or(issue.priority(gt(8)), exists(comment, { parent: ["id"], child: ["issueId"] })))`.
|
|
48
|
-
- **Nesting** is `sub(alias, relationship, build?)`, or
|
|
49
|
-
`sub(alias, childTable, correlation, build?)` when you want to spell the join inline.
|
|
50
|
-
- **Paging / single-row**: `orderBy(col, dir)`, `limit(n)`, `start(cursor, { exclusive })` (cursor
|
|
51
|
-
paging from a partial row over the sort columns), and `one()`.
|
|
52
|
-
|
|
53
|
-
The result type flows through: `store.query.issue.sub("comments", …).materialize()` yields an
|
|
54
|
-
`ArrayView` whose `.data` is typed `{ …issue, comments: Comment[] }[]`.
|
|
55
|
-
|
|
56
|
-
A top-level **`.one()`** flips `materialize()` to a `SingularArrayView` whose `.data` is the single
|
|
57
|
-
row or `null` (the engine caps the query to `limit 1`) — the unwrap is type-level too:
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
const view = store.query.issue.where.id(id).one().materialize();
|
|
61
|
-
view.subscribe((row) => render(row)); // row: Issue | null, not Issue[]
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Named queries + fragments
|
|
65
|
-
|
|
66
|
-
Remote subscriptions should be named with `defineQuery`. The returned value is callable: it
|
|
67
|
-
validates untrusted args, builds the query, and stamps it with the `(name, args)` wire identity that
|
|
68
|
-
remote backends subscribe to.
|
|
69
|
-
|
|
70
|
-
Fragments are reusable selections over a table. They compose into named queries with `include`
|
|
71
|
-
(same row) and `sub` (related rows), so a React screen can open one root query while child
|
|
72
|
-
components keep owning their own data declarations.
|
|
73
|
-
|
|
74
|
-
```ts
|
|
75
|
-
import { defineFragment, defineQuery, newQueryBuilder } from "@rindle/client";
|
|
76
|
-
import type { FragmentData, FragmentRef } from "@rindle/client";
|
|
77
|
-
|
|
78
|
-
const q = newQueryBuilder(schema);
|
|
79
|
-
|
|
80
|
-
export const IssueCardFragment = defineFragment(issue, (i) =>
|
|
81
|
-
i
|
|
82
|
-
.select("id", "title")
|
|
83
|
-
.countAs("commentCount", rels.issueComments),
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
export type IssueCardData = FragmentData<typeof IssueCardFragment>;
|
|
87
|
-
export type IssueCardRef = FragmentRef<typeof IssueCardFragment>;
|
|
88
|
-
|
|
89
|
-
export const issuesPageQuery = defineQuery(
|
|
90
|
-
"issuesPage",
|
|
91
|
-
(raw): { limit: number } => {
|
|
92
|
-
const limit = Number((raw as { limit?: unknown }).limit);
|
|
93
|
-
if (!Number.isInteger(limit) || limit < 1 || limit > 100) throw new Error("bad limit");
|
|
94
|
-
return { limit };
|
|
95
|
-
},
|
|
96
|
-
({ limit }) => q.issue.orderBy("id", "asc").limit(limit).include(IssueCardFragment),
|
|
97
|
-
);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
`FragmentData<F>` is the local data shape read by the component that owns `F`. `FragmentRef<F>` is
|
|
101
|
-
the opaque token React passes between components; use `fragmentKey(ref)` from `@rindle/client` or
|
|
102
|
-
`@rindle/react` for stable list keys. Passing a fragment directly to `sub(alias, rel, ChildFragment)`
|
|
103
|
-
creates child refs in the React-facing local data. Passing an inline builder such as
|
|
104
|
-
`sub(alias, rel, (q) => q.include(ChildFragment).orderBy(...))` keeps that child payload inline for
|
|
105
|
-
the parent to read.
|
|
106
|
-
|
|
107
|
-
## Local-only tables
|
|
108
|
-
|
|
109
|
-
Some UI state should live in the same reactive store as synced data but must never cross the
|
|
110
|
-
wire: selections, draft text, view preferences, scratch rows. Mark those tables as
|
|
111
|
-
**local-only** with `table(name, { local: true })` and include them in the same schema:
|
|
112
|
-
|
|
113
|
-
```ts
|
|
114
|
-
const issue = table("issue").columns({ id: number(), title: string() }).primaryKey("id");
|
|
115
|
-
const selection = table("selection", { local: true })
|
|
116
|
-
.columns({ id: number(), issueId: number() })
|
|
117
|
-
.primaryKey("id");
|
|
118
|
-
|
|
119
|
-
const schema = createSchema({ tables: [issue, selection] });
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Local-only tables are client-authoritative: they are omitted from the normalized schema
|
|
123
|
-
fingerprint, never advertised to the server, never synced, and never rewound during an
|
|
124
|
-
optimistic rebase. `store.query` can join/query them locally, but `newQueryBuilder(schema)`
|
|
125
|
-
(the server/named-query scope) rejects them so a named remote query cannot depend on private
|
|
126
|
-
client state.
|
|
127
|
-
|
|
128
|
-
Write local state with `store.writeLocal(...)`, not with a replayable mutator:
|
|
129
|
-
|
|
130
|
-
```ts
|
|
131
|
-
await store.writeLocal((tx) => tx.add("selection", { id: 1, issueId: 42 }));
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
`writeLocal` accepts only local-only tables and rejects synced tables. Conversely, predicted
|
|
135
|
-
mutators cannot touch local-only tables: the server replays a mutator from `(name, args)` alone,
|
|
136
|
-
so private client state must stay on the direct local-write path.
|
|
137
|
-
|
|
138
|
-
SQL-first apps usually keep synced tables in a generated file. Do not edit that file to add
|
|
139
|
-
local tables; extend it in hand-written code:
|
|
140
|
-
|
|
141
|
-
```ts
|
|
142
|
-
// shared/schema.local.ts
|
|
143
|
-
import { extendSchema, number, string, table } from "@rindle/client";
|
|
144
|
-
import { schema as generatedSchema } from "./schema.gen.ts";
|
|
145
|
-
|
|
146
|
-
export const draft = table("draft", { local: true })
|
|
147
|
-
.columns({ id: string(), issueId: number(), body: string() })
|
|
12
|
+
.columns({ id: number(), title: string(), priority: number() })
|
|
148
13
|
.primaryKey("id");
|
|
14
|
+
const schema = createSchema({ tables: [issue] }); // pass to `new Store(schema, backend)`
|
|
149
15
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
Hand `clientSchema` to the browser store. Keep using the generated schema for API-server
|
|
154
|
-
resolution and daemon-facing code; `extendSchema` intentionally accepts only local-only tables,
|
|
155
|
-
so real synced tables still have to come from `rindle schema gen`.
|
|
156
|
-
|
|
157
|
-
## The Backend seam
|
|
158
|
-
|
|
159
|
-
A `Backend` emits a per-query `ChangeEvent` stream (`hello` → `snapshot` → `batch`) and accepts
|
|
160
|
-
mutations. The `Store` builds one `ArrayView` per query and routes events to it. The **same**
|
|
161
|
-
`Store` / `ArrayView` / builder work behind a local (WASM) or remote (network) backend — only the
|
|
162
|
-
backend differs.
|
|
163
|
-
|
|
164
|
-
```ts
|
|
165
|
-
const store = new Store(schema, backend);
|
|
166
|
-
const view = store.query.issue.where.closed(false).materialize();
|
|
167
|
-
view.subscribe(render);
|
|
168
|
-
await store.write((tx) => tx.add("issue", { id: 1, title: "x", priority: 3, meta: { tags: [] } }));
|
|
16
|
+
const view = store.query.issue.where.priority(gt(3)).orderBy("id", "asc").materialize();
|
|
17
|
+
view.subscribe(render); // typed, reference-stable, always == a fresh query
|
|
169
18
|
```
|
|
170
19
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
20
|
+
Everything an app builds on lives here:
|
|
21
|
+
|
|
22
|
+
- the **query builder** (`where` proxies, `or`/`and`/`exists`, `sub`, `orderBy`/`limit`/`start`,
|
|
23
|
+
`.one()`, `countAs`/`count()`/`groupBy`/`having`, `select`);
|
|
24
|
+
- **named queries & fragments** (`defineQuery`, `defineFragment`, `defineRelationships`/`rel`) —
|
|
25
|
+
the co-located, waterfall-free composition surface;
|
|
26
|
+
- the **isomorphic-mutator vocabulary** (`defineMutators` → `shared(args, gen)`, `IsoTx`,
|
|
27
|
+
`MutationGen`, `MutatorCtx`) — one generator body per write, run on both tiers;
|
|
28
|
+
- **local-only tables** (`table(name, { local: true })` + `extendSchema` + `store.writeLocal`) —
|
|
29
|
+
client-authoritative state that never syncs or rebases.
|
|
30
|
+
|
|
31
|
+
## Docs
|
|
32
|
+
|
|
33
|
+
- **[Supported query shapes](https://rindle.sh/docs/supported-queries-ts)** — the honest matrix of
|
|
34
|
+
what the builder can lower, and the build-time rejections.
|
|
35
|
+
- **[Compose the UI with fragments](https://rindle.sh/docs/fragments)** — `defineQuery` /
|
|
36
|
+
`defineFragment` / `defineRelationships` in full.
|
|
37
|
+
- **[Isomorphic mutators](https://rindle.sh/docs/mutators)** — the write contract: the op
|
|
38
|
+
vocabulary, reads, `ctx.user`, the determinism rules.
|
|
39
|
+
- **[The browser client](https://rindle.sh/docs/client)** — this core behind the optimistic synced
|
|
40
|
+
store, including local-only tables.
|
|
41
|
+
- **[Schema & migrations](https://rindle.sh/docs/schema)** — why the schema is generated from SQL,
|
|
42
|
+
and the one allowed hand-edit.
|
|
43
|
+
|
|
44
|
+
Markdown mirrors live at `https://rindle.sh/docs/<slug>.md`; for agents:
|
|
45
|
+
[llms.txt](https://rindle.sh/llms.txt).
|
|
176
46
|
|
|
177
47
|
## Build
|
|
178
48
|
|
package/dist/schema.d.ts
CHANGED
|
@@ -41,15 +41,24 @@ export interface TableMeta<N extends string = string, C extends AnyCols = AnyCol
|
|
|
41
41
|
/** A **local-only** table (`201-LOCAL-ONLY-TABLES-DESIGN.md`): client-authoritative,
|
|
42
42
|
* never synced/tracked/rebased. The single marker the whole design keys off (§4) — it is
|
|
43
43
|
* immutable for the table's lifetime (N2) and never crosses the wire (C2). Absent ⇒ an
|
|
44
|
-
* ordinary synced table.
|
|
45
|
-
|
|
44
|
+
* ordinary synced table.
|
|
45
|
+
*
|
|
46
|
+
* `true` ⇒ eligible for the local-persistence plane (durable + cross-tab when the client
|
|
47
|
+
* enables `persistLocal`, `207-LOCAL-TABLE-PERSISTENCE-DESIGN.md`). `"session"` ⇒ local but
|
|
48
|
+
* EPHEMERAL: outside the plane entirely — never persisted, never replicated across tabs,
|
|
49
|
+
* per-client-instance state that empties on reload (201's original behavior) even when
|
|
50
|
+
* `persistLocal` is on. Every OTHER locality rule (untracked source, M1/M2 guards, E3/Q1
|
|
51
|
+
* wire exclusion) treats both variants identically. */
|
|
52
|
+
readonly local?: boolean | "session";
|
|
46
53
|
}
|
|
47
54
|
/** Options for {@link table}. */
|
|
48
55
|
export interface TableOptions {
|
|
49
56
|
/** Declare a {@link TableMeta.local local-only} table (selection state, draft text, view
|
|
50
57
|
* prefs, scratch rows): client-authoritative, never synced or rebased. See
|
|
51
|
-
* `201-LOCAL-ONLY-TABLES-DESIGN.md`.
|
|
52
|
-
|
|
58
|
+
* `201-LOCAL-ONLY-TABLES-DESIGN.md`. Pass `"session"` for a local table that must stay
|
|
59
|
+
* EPHEMERAL and per-tab even when the client enables `persistLocal` — e.g. selection state
|
|
60
|
+
* that should not follow the user across tabs or reloads (207 §5.4). */
|
|
61
|
+
local?: boolean | "session";
|
|
53
62
|
}
|
|
54
63
|
export type TableDef<N extends string, C extends AnyCols, PK extends string = string> = {
|
|
55
64
|
readonly [SCHEMA]: TableMeta<N, C, PK>;
|
|
@@ -201,10 +210,25 @@ export declare function refineSchema<S extends ColsMap, P extends Record<string,
|
|
|
201
210
|
tables: T;
|
|
202
211
|
}): Schema<RefinedColsMap<S, T>, P>;
|
|
203
212
|
/** Whether `table` is a {@link TableMeta.local local-only} table in `schema` (an unknown table
|
|
204
|
-
* reads as non-local). The single locality predicate the backends key off.
|
|
213
|
+
* reads as non-local). The single locality predicate the backends key off. BOTH variants —
|
|
214
|
+
* `true` and `"session"` — are local here; the persisted/ephemeral split matters only to the
|
|
215
|
+
* persistence plane ({@link persistedLocalTableNames}). */
|
|
205
216
|
export declare function isLocalTable<S extends ColsMap>(schema: Schema<S>, table: string): boolean;
|
|
206
|
-
/** The set of local-only table names in `schema` (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4)
|
|
217
|
+
/** The set of local-only table names in `schema` (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4) — BOTH
|
|
218
|
+
* variants (`true` and `"session"`); every locality rule except persistence keys off this set. */
|
|
207
219
|
export declare function localTableNames<S extends ColsMap>(schema: Schema<S>): Set<string>;
|
|
220
|
+
/** The subset of {@link localTableNames} eligible for the persistence plane — `local: true` only.
|
|
221
|
+
* A `local: "session"` table stays outside it: never persisted, never replicated across tabs
|
|
222
|
+
* (`207-LOCAL-TABLE-PERSISTENCE-DESIGN.md` §5.4). */
|
|
223
|
+
export declare function persistedLocalTableNames<S extends ColsMap>(schema: Schema<S>): Set<string>;
|
|
224
|
+
/** A stable fingerprint of the schema's PERSISTED local tables only — the persistence gate's
|
|
225
|
+
* `schemaHash` (`207-LOCAL-TABLE-PERSISTENCE-DESIGN.md` §3.3 / P7). Per `local: true` table:
|
|
226
|
+
* `(name, ordered column names + types + optionality, pk columns)`. Column order is kept (rows are
|
|
227
|
+
* positional); tables are sorted by name so registration order can't skew it; synced-table AND
|
|
228
|
+
* `local: "session"` changes never move it (reshaping an ephemeral table must not wipe durable
|
|
229
|
+
* data). The value is the canonical descriptor itself, not a digest — local-table sets are tiny,
|
|
230
|
+
* and exactness (no collision can ever skip a P7 clear) beats compactness here. */
|
|
231
|
+
export declare function localSchemaHash<S extends ColsMap>(schema: Schema<S>): string;
|
|
208
232
|
/** Brand on a {@link Relationship} value (a `unique symbol`, distinct from a {@link TableDef}). */
|
|
209
233
|
declare const RELATIONSHIP_BRAND: unique symbol;
|
|
210
234
|
/**
|
package/dist/schema.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAErD;;;;;;;0EAO0E;AAC1E,MAAM,WAAW,GAAG,CAAC,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;CAClB;AACD,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;AACzD,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AACnD,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,OAAO,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAC;AAEtE;;;;;;iBAMiB;AACjB,MAAM,WAAW,UAAU,CAAC,CAAC,CAAE,SAAQ,GAAG,CAAC,CAAC,CAAC;IAC3C,QAAQ,IAAI,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CAClC;AAOD,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,MAAM,GAAG,MAAM,OAAK,UAAU,CAAC,CAAC,CAAyB,CAAC;AAC3F,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,MAAM,GAAG,MAAM,OAAK,UAAU,CAAC,CAAC,CAAyB,CAAC;AAC3F,eAAO,MAAM,OAAO,QAAO,UAAU,CAAC,OAAO,CAAgC,CAAC;AAC9E,eAAO,MAAM,IAAI,GAAI,CAAC,GAAG,OAAO,OAAK,UAAU,CAAC,CAAC,CAAuB,CAAC;AAEzE,0FAA0F;AAC1F,eAAO,MAAM,MAAM,EAAE,OAAO,MAAgC,CAAC;AAE7D,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,SAAS,OAAO,GAAG,OAAO,EAAE,EAAE,SAAS,MAAM,GAAG,MAAM;IAC3G,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAMpB,QAAQ,CAAC,UAAU,EAAE,SAAS,EAAE,EAAE,CAAC;IACnC
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.js
CHANGED
|
@@ -76,7 +76,7 @@ export function extendSchema(base, opts) {
|
|
|
76
76
|
const tables = { ...base.tables };
|
|
77
77
|
for (const t of opts.tables) {
|
|
78
78
|
const m = t[SCHEMA];
|
|
79
|
-
if (m.local
|
|
79
|
+
if (!m.local) {
|
|
80
80
|
throw new Error(`extendSchema: table "${m.name}" is not local-only — synced tables must be generated from the daemon schema.`);
|
|
81
81
|
}
|
|
82
82
|
addTableMeta(tables, m, "extendSchema");
|
|
@@ -150,7 +150,9 @@ function assertRefinementMatches(m, baseMeta) {
|
|
|
150
150
|
cols.every((c) => baseMeta.columns[c] !== undefined && m.columns[c].type === baseMeta.columns[c].type) &&
|
|
151
151
|
m.primaryKey.length === baseMeta.primaryKey.length &&
|
|
152
152
|
m.primaryKey.every((k, i) => baseMeta.primaryKey[i] === k) &&
|
|
153
|
-
|
|
153
|
+
// Locality must match EXACTLY, including the persisted-vs-session variant (a refinement
|
|
154
|
+
// flipping `true` ↔ `"session"` would silently change the table's durability).
|
|
155
|
+
(m.local ?? false) === (baseMeta.local ?? false);
|
|
154
156
|
if (!matches) {
|
|
155
157
|
throw new Error(`refineSchema: table "${m.name}" does not match the schema's table of that name — pass the ` +
|
|
156
158
|
`output of refineTable over the SAME generated def (identical columns, kinds, primary key, ` +
|
|
@@ -158,14 +160,42 @@ function assertRefinementMatches(m, baseMeta) {
|
|
|
158
160
|
}
|
|
159
161
|
}
|
|
160
162
|
/** Whether `table` is a {@link TableMeta.local local-only} table in `schema` (an unknown table
|
|
161
|
-
* reads as non-local). The single locality predicate the backends key off.
|
|
163
|
+
* reads as non-local). The single locality predicate the backends key off. BOTH variants —
|
|
164
|
+
* `true` and `"session"` — are local here; the persisted/ephemeral split matters only to the
|
|
165
|
+
* persistence plane ({@link persistedLocalTableNames}). */
|
|
162
166
|
export function isLocalTable(schema, table) {
|
|
163
|
-
return schema.tables[table]?.local
|
|
167
|
+
return Boolean(schema.tables[table]?.local);
|
|
164
168
|
}
|
|
165
|
-
/** The set of local-only table names in `schema` (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4)
|
|
169
|
+
/** The set of local-only table names in `schema` (`201-LOCAL-ONLY-TABLES-DESIGN.md` §4) — BOTH
|
|
170
|
+
* variants (`true` and `"session"`); every locality rule except persistence keys off this set. */
|
|
166
171
|
export function localTableNames(schema) {
|
|
167
172
|
return new Set(Object.keys(schema.tables).filter((n) => schema.tables[n].local));
|
|
168
173
|
}
|
|
174
|
+
/** The subset of {@link localTableNames} eligible for the persistence plane — `local: true` only.
|
|
175
|
+
* A `local: "session"` table stays outside it: never persisted, never replicated across tabs
|
|
176
|
+
* (`207-LOCAL-TABLE-PERSISTENCE-DESIGN.md` §5.4). */
|
|
177
|
+
export function persistedLocalTableNames(schema) {
|
|
178
|
+
return new Set(Object.keys(schema.tables).filter((n) => schema.tables[n].local === true));
|
|
179
|
+
}
|
|
180
|
+
/** A stable fingerprint of the schema's PERSISTED local tables only — the persistence gate's
|
|
181
|
+
* `schemaHash` (`207-LOCAL-TABLE-PERSISTENCE-DESIGN.md` §3.3 / P7). Per `local: true` table:
|
|
182
|
+
* `(name, ordered column names + types + optionality, pk columns)`. Column order is kept (rows are
|
|
183
|
+
* positional); tables are sorted by name so registration order can't skew it; synced-table AND
|
|
184
|
+
* `local: "session"` changes never move it (reshaping an ephemeral table must not wipe durable
|
|
185
|
+
* data). The value is the canonical descriptor itself, not a digest — local-table sets are tiny,
|
|
186
|
+
* and exactness (no collision can ever skip a P7 clear) beats compactness here. */
|
|
187
|
+
export function localSchemaHash(schema) {
|
|
188
|
+
// Derived from persistedLocalTableNames — the hash's contract is "fingerprints exactly the
|
|
189
|
+
// tables the plane persists", so the two must share one predicate, not two copies of it.
|
|
190
|
+
const tables = [...persistedLocalTableNames(schema)]
|
|
191
|
+
.sort()
|
|
192
|
+
.map((n) => {
|
|
193
|
+
const meta = schema.tables[n];
|
|
194
|
+
const cols = Object.keys(meta.columns).map((c) => [c, meta.columns[c].type, meta.columns[c].optional === true]);
|
|
195
|
+
return [n, cols, meta.primaryKey];
|
|
196
|
+
});
|
|
197
|
+
return `v1:${JSON.stringify(tables)}`;
|
|
198
|
+
}
|
|
169
199
|
// ----------------------------- relationships (FRAGMENT-COMPOSITION-DESIGN §4.2, named edges) -----
|
|
170
200
|
//
|
|
171
201
|
// A relationship is the correlation (`parent.col → child.col`) declared ONCE as a value, so `sub`,
|
package/dist/schema.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,uFAAuF;AACvF,EAAE;AACF,6EAA6E;AAC7E,2FAA2F;AAC3F,sFAAsF;AACtF,0FAA0F;AAC1F,qCAAqC;AAGrC,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AA8BhD,MAAM,OAAO,GAAG,CAAI,IAAa,EAAE,QAAQ,GAAG,KAAK,EAAiB,EAAE,CAAC,CAAC;IACtE,IAAI;IACJ,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IACzC,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAW,IAAI,EAAE,IAAI,CAAC;CAC9C,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,MAAM,GAAG,GAA6C,EAAE,CAAC,OAAO,CAAI,QAAQ,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,MAAM,GAAG,GAA6C,EAAE,CAAC,OAAO,CAAI,QAAQ,CAAC,CAAC;AAC3F,MAAM,CAAC,MAAM,OAAO,GAAG,GAAwB,EAAE,CAAC,OAAO,CAAU,SAAS,CAAC,CAAC;AAC9E,MAAM,CAAC,MAAM,IAAI,GAAG,GAA+B,EAAE,CAAC,OAAO,CAAI,MAAM,CAAC,CAAC;AAEzE,0FAA0F;AAC1F,MAAM,CAAC,MAAM,MAAM,GAAkB,MAAM,CAAC,eAAe,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
|
package/dist/store.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type { Ast } from "./ast.ts";
|
|
|
2
2
|
import { type Query, type QueryRoot } from "./query.ts";
|
|
3
3
|
import type { ColsMap, InsertOf, RowOf, Schema } from "./schema.ts";
|
|
4
4
|
import type { Backend, ChangeEvent, QueryId, ResultType, WireValue } from "./types.ts";
|
|
5
|
-
import { type ArrayView } from "./view.ts";
|
|
5
|
+
import { type ArrayView, type ViewChangeListener } from "./view.ts";
|
|
6
6
|
/** One query's SSR snapshot, keyed by its `viewKey` ({@link stableKey} of the AST): the
|
|
7
7
|
* pre-projected first-paint `rows` plus the `cvMin` watermark they reflect (SSR-DESIGN.md §6.2).
|
|
8
8
|
* Serializable as-is into the HTML — `rows` are already JSON values (json columns parsed). */
|
|
@@ -81,8 +81,15 @@ export declare class Store<S extends ColsMap> {
|
|
|
81
81
|
private readonly pendingChanges;
|
|
82
82
|
constructor(schema: Schema<S>, backend: Backend);
|
|
83
83
|
/** Materialize any fluent query object. Named queries subscribe remotely by `(name,args)`;
|
|
84
|
-
* ad-hoc builder queries are local-only for local-first backends.
|
|
85
|
-
|
|
84
|
+
* ad-hoc builder queries are local-only for local-first backends.
|
|
85
|
+
*
|
|
86
|
+
* `opts.onChanges` binds a narrator to this view's DIFF stream ({@link ArrayView.onChanges}) — the
|
|
87
|
+
* per-view seam that replaces filtering the store-global {@link subscribeChanges} by `qid`. It is
|
|
88
|
+
* wired BEFORE the backend registers the query, so a synchronous backend's first `snapshot` (fired
|
|
89
|
+
* inside `registerQuery`, before this returns) is delivered too. */
|
|
90
|
+
materialize<Q extends Query<any, any, any>>(query: Q, opts?: {
|
|
91
|
+
onChanges?: ViewChangeListener;
|
|
92
|
+
}): ReturnType<Q["materialize"]>;
|
|
86
93
|
/** True when the backend can retain a remote named query independently from the local
|
|
87
94
|
* materialized AST view. React uses this to keep one local view per AST while still sending
|
|
88
95
|
* every mounted `(name,args)` lease through the backend. */
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAEpC,OAAO,EAAW,KAAK,KAAK,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACpE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAiC,OAAO,EAAe,UAAU,EAAc,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/I,OAAO,EAAE,KAAK,SAAS,
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
CHANGED
|
@@ -20,7 +20,9 @@ export class Store {
|
|
|
20
20
|
syncLeases = new Map();
|
|
21
21
|
// SSR seeds (SSR-DESIGN.md §6), keyed by `viewKey`: a view materialized for one of these is
|
|
22
22
|
// seeded for first paint; a seed is consumed (dropped) the moment its query's first live
|
|
23
|
-
// `hello`
|
|
23
|
+
// SNAPSHOT lands — NOT its `hello` — so the seed bridges the `hello`→snapshot gap (the live
|
|
24
|
+
// data arrives on the snapshot, a round-trip after the hello) and later mounts of a now-live
|
|
25
|
+
// query don't re-seed stale SSR data.
|
|
24
26
|
seeds = new Map();
|
|
25
27
|
// Public per-query change subscribers ({@link subscribeChanges}). `undefined` until the first
|
|
26
28
|
// subscription, so an app that never narrates (nor attaches devtools) pays one undefined-check per
|
|
@@ -88,10 +90,15 @@ export class Store {
|
|
|
88
90
|
this.query = queries(this.schema, (query) => this.materialize(query), { includeLocal: true });
|
|
89
91
|
}
|
|
90
92
|
/** Materialize any fluent query object. Named queries subscribe remotely by `(name,args)`;
|
|
91
|
-
* ad-hoc builder queries are local-only for local-first backends.
|
|
92
|
-
|
|
93
|
+
* ad-hoc builder queries are local-only for local-first backends.
|
|
94
|
+
*
|
|
95
|
+
* `opts.onChanges` binds a narrator to this view's DIFF stream ({@link ArrayView.onChanges}) — the
|
|
96
|
+
* per-view seam that replaces filtering the store-global {@link subscribeChanges} by `qid`. It is
|
|
97
|
+
* wired BEFORE the backend registers the query, so a synchronous backend's first `snapshot` (fired
|
|
98
|
+
* inside `registerQuery`, before this returns) is delivered too. */
|
|
99
|
+
materialize(query, opts) {
|
|
93
100
|
const remote = typeof query.name === "string" ? { name: query.name, args: query.args } : undefined;
|
|
94
|
-
return this.registerMaterialized(query.ast(), remote).view;
|
|
101
|
+
return this.registerMaterialized(query.ast(), remote, opts?.onChanges).view;
|
|
95
102
|
}
|
|
96
103
|
/** True when the backend can retain a remote named query independently from the local
|
|
97
104
|
* materialized AST view. React uses this to keep one local view per AST while still sending
|
|
@@ -262,7 +269,7 @@ export class Store {
|
|
|
262
269
|
return out;
|
|
263
270
|
}
|
|
264
271
|
// --- internals ---------------------------------------------------------------
|
|
265
|
-
registerMaterialized(ast, remote) {
|
|
272
|
+
registerMaterialized(ast, remote, onChanges) {
|
|
266
273
|
const qid = this.nextId++;
|
|
267
274
|
this.asts.set(qid, ast);
|
|
268
275
|
// Pre-create the view (PENDING) so `materialize` is synchronous for ANY backend — a remote
|
|
@@ -284,6 +291,10 @@ export class Store {
|
|
|
284
291
|
}
|
|
285
292
|
baseDestroy();
|
|
286
293
|
};
|
|
294
|
+
// Bind the narrator BEFORE `registerQuery` fires (a synchronous backend dispatches this query's
|
|
295
|
+
// first `hello`+`snapshot` inside it) so the initial snapshot reaches the change listener too.
|
|
296
|
+
if (onChanges)
|
|
297
|
+
view.onChanges(onChanges);
|
|
287
298
|
// If the backend rejects the registration (E3: a remote query naming a local-only table), roll
|
|
288
299
|
// back the per-qid state we just created — otherwise the view + ast entry leak (the caller never
|
|
289
300
|
// gets a handle to `destroy()` them, since the throw aborts before we return).
|
|
@@ -321,21 +332,30 @@ export class Store {
|
|
|
321
332
|
onEvent(qid, ev) {
|
|
322
333
|
if (ev.type === "hello") {
|
|
323
334
|
const ast = this.asts.get(qid);
|
|
324
|
-
// First live hello for this view ⇒ retire its SSR seed: the maintained tree now owns the
|
|
325
|
-
// result, so no future mount of the same query should flash the stale first-paint rows.
|
|
326
|
-
if (ast)
|
|
327
|
-
this.seeds.delete(stableKey(ast));
|
|
328
335
|
const types = ast ? this.viewTypes(ev.schema, ast) : undefined;
|
|
329
336
|
// Reset the (pre-created or existing) view IN PLACE — first hello OR a re-hydrate (new
|
|
330
|
-
// epoch) — so the materialized reference the caller holds survives a re-subscribe.
|
|
337
|
+
// epoch) — so the materialized reference the caller holds survives a re-subscribe. The SSR
|
|
338
|
+
// seed is KEPT across the reset and retired on the first `snapshot` below (the live data
|
|
339
|
+
// lands then, not on the hello), so a seeded query bridges the gap instead of flashing empty.
|
|
331
340
|
const view = this.views.get(qid) ?? this.views.set(qid, new FlatArrayView(undefined, undefined, qid)).get(qid);
|
|
332
341
|
view.reset(ev.schema, types);
|
|
333
342
|
}
|
|
334
343
|
else if (ev.type === "snapshot") {
|
|
335
|
-
|
|
344
|
+
// The first live snapshot IS the query's hydration point (even an empty one — 0 rows is an
|
|
345
|
+
// authoritative answer): retire the SSR seed now, from the view (so `data` switches to the
|
|
346
|
+
// maintained tree) AND from the seeds map (so no later mount re-seeds a now-live query),
|
|
347
|
+
// BEFORE folding so the fold's notify already reflects the live tree with no empty gap.
|
|
348
|
+
const ast = this.asts.get(qid);
|
|
349
|
+
if (ast)
|
|
350
|
+
this.seeds.delete(stableKey(ast));
|
|
351
|
+
this.views.get(qid)?.retireSeed();
|
|
352
|
+
this.applyAndTrack(qid, ev.adds, "snapshot");
|
|
336
353
|
}
|
|
337
354
|
else {
|
|
338
|
-
|
|
355
|
+
// A `catchUp` batch is a query's initial hydration delivered as a delta (the optimistic
|
|
356
|
+
// backend hydrates through its reconcile cycle), so phase it as a `snapshot` — a narrator's
|
|
357
|
+
// "what CHANGED" default then ignores the initial rows instead of narrating each as an add.
|
|
358
|
+
this.applyAndTrack(qid, ev.events, ev.catchUp ? "snapshot" : "batch");
|
|
339
359
|
}
|
|
340
360
|
// Fan the same post-fold frame out to subscribers (narration, devtools, …). Inside a commit
|
|
341
361
|
// bracket the frames are BUFFERED and delivered together at the boundary (after every view has
|
|
@@ -357,12 +377,12 @@ export class Store {
|
|
|
357
377
|
/** Fold a batch into its view, then notify now or — inside a commit bracket — defer the view's
|
|
358
378
|
* notification to the commit boundary, so all sibling views fold first (cross-view-atomic
|
|
359
379
|
* notification; see `commitDepth`). */
|
|
360
|
-
applyAndTrack(qid, events) {
|
|
380
|
+
applyAndTrack(qid, events, phase) {
|
|
361
381
|
const view = this.views.get(qid);
|
|
362
382
|
if (!view)
|
|
363
383
|
return;
|
|
364
384
|
const deferring = this.commitDepth > 0;
|
|
365
|
-
if (view.applyChanges(events, this.removedSubtreeWanted > 0, deferring) && deferring) {
|
|
385
|
+
if (view.applyChanges(events, this.removedSubtreeWanted > 0, deferring, phase) && deferring) {
|
|
366
386
|
this.pendingFlush.add(view);
|
|
367
387
|
}
|
|
368
388
|
}
|