@rindle/client 0.4.2 → 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/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/mutation-ops.d.ts +73 -13
- package/dist/mutation-ops.d.ts.map +1 -1
- package/dist/mutation-ops.js +22 -3
- package/dist/mutation-ops.js.map +1 -1
- package/dist/schema.d.ts +130 -22
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +61 -6
- package/dist/schema.js.map +1 -1
- package/dist/store.d.ts +17 -6
- 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/index.ts +6 -1
- package/src/mutation-ops.ts +98 -24
- package/src/schema.ts +189 -37
- package/src/store.ts +43 -16
- 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/index.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export * from "./ssr.ts";
|
|
|
7
7
|
export { resolveChange, subRow } from "./resolve.ts";
|
|
8
8
|
export type { NamedRow, ResolvedChange } from "./resolve.ts";
|
|
9
9
|
export type { KeyedRow, MutationOp, ServerWriteTx } from "./mutation-ops.ts";
|
|
10
|
-
export { driveMutationAsync, driveMutationSync, isGeneratorMutator, isoTx, shared, } from "./mutation-ops.ts";
|
|
11
|
-
export type { ArgSchema, AsyncEffectExec, BatchEffect, IsoTx, MutationGen, MutatorCtx, ReadEffect, SharedMutator, SharedMutatorWithArgs, SyncEffectExec, YieldEffect, } from "./mutation-ops.ts";
|
|
10
|
+
export { defineMutators, driveMutationAsync, driveMutationSync, isGeneratorMutator, isoTx, shared, } from "./mutation-ops.ts";
|
|
11
|
+
export type { ArgSchema, AsyncEffectExec, BatchEffect, IsoTx, IsoTxOf, MutationGen, MutatorCtx, QueryArg, QueryEffect, QueryResultRow, ReadEffect, SharedMutator, SharedMutatorWithArgs, SyncEffectExec, YieldEffect, } from "./mutation-ops.ts";
|
|
12
12
|
export { stableKey } from "./key.ts";
|
|
13
13
|
export { COMPARATOR_VERSION, compareNumber, compareRows, compareString, compareValue } from "./compare.ts";
|
|
14
14
|
export type { Backend, BackendDevObserver, BackendServerDelta, ChangeEvent, ColType, FlatChange, FlatOp, Mutation, MutationEnvelope, NormalizedEvent, NormalizedOp, NormalizedSource, NormalizedTableSchema, OptimisticSource, PathSeg, ProgressFrame, QueryId, RemoteQuery, ResultType, WireNode, WireProjection, WireRel, WireSchema, WireValue, } from "./types.ts";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AAEzB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE7D,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,EACL,MAAM,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,KAAK,EACL,WAAW,EACX,UAAU,EACV,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3G,YAAY,EACV,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,OAAO,EACP,UAAU,EACV,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,OAAO,EACP,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,OAAO,EACP,UAAU,EACV,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG9F,YAAY,EACV,SAAS,EACT,GAAG,EACH,KAAK,EACL,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAa,GACd,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AAEzB,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACrD,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAE7D,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAE7E,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,EACL,MAAM,GACP,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,SAAS,EACT,eAAe,EACf,WAAW,EACX,KAAK,EACL,OAAO,EACP,WAAW,EACX,UAAU,EACV,QAAQ,EACR,WAAW,EACX,cAAc,EACd,UAAU,EACV,aAAa,EACb,qBAAqB,EACrB,cAAc,EACd,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE3G,YAAY,EACV,OAAO,EACP,kBAAkB,EAClB,kBAAkB,EAClB,WAAW,EACX,OAAO,EACP,UAAU,EACV,MAAM,EACN,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAChB,qBAAqB,EACrB,gBAAgB,EAChB,OAAO,EACP,aAAa,EACb,OAAO,EACP,WAAW,EACX,UAAU,EACV,QAAQ,EACR,cAAc,EACd,OAAO,EACP,UAAU,EACV,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG9F,YAAY,EACV,SAAS,EACT,GAAG,EACH,KAAK,EACL,SAAS,EACT,kBAAkB,EAClB,WAAW,EACX,GAAG,EACH,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,aAAa,GACd,MAAM,UAAU,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @rindle/client — the backend-agnostic flat-change client core: typed schema, query builder,
|
|
2
2
|
// comparator, Backend seam, and the ArrayView contract. (The ArrayView folding impl + the
|
|
3
3
|
// Store/backend wiring land in later slices.)
|
|
4
|
-
export * from "./schema.js"; // table/columns/primaryKey, createSchema/extendSchema, refineTable/refineSchema, string/number/boolean/json, Col, RowOf, Row, TableDef, tableSpec, SCHEMA
|
|
4
|
+
export * from "./schema.js"; // table/columns/primaryKey, createSchema/extendSchema, refineTable/refineSchema, string/number/boolean/json, Col, RowOf, Row, InsertOf, Insert, insertPlan, insertCell, TableDef, tableSpec, SCHEMA
|
|
5
5
|
export * from "./operators.js"; // eq/ne/gt/ge/lt/le/like/notLike/ilike/notIlike/inList/notInList/is/isNot, and/or, Pred, Cond, Arg
|
|
6
6
|
export * from "./query.js"; // Query, QueryRoot, queries/newQueryBuilder, defineQuery/NamedQuery, exists, notExists, defineFragment/Fragment/FragmentRef/isFragment
|
|
7
7
|
export * from "./view.js"; // ArrayView, SingularArrayView, FlatArrayView, SingularView, ViewTypes
|
|
@@ -9,7 +9,7 @@ export * from "./store.js"; // Store, WriteTx, AssembledNode, DehydratedQuery, D
|
|
|
9
9
|
export * from "./ssr.js"; // createServerStore, ServerStore, OneShotBackend, OneShotQueryFn, OneShotResult, ServerStoreOptions
|
|
10
10
|
export { resolveChange, subRow } from "./resolve.js"; // positional FlatChange → named rows (inverse of the wire encoder)
|
|
11
11
|
// shared (generator) mutator seam — one body, sync on the client + async on the server
|
|
12
|
-
export { driveMutationAsync, driveMutationSync, isGeneratorMutator, isoTx, shared, } from "./mutation-ops.js";
|
|
12
|
+
export { defineMutators, driveMutationAsync, driveMutationSync, isGeneratorMutator, isoTx, shared, } from "./mutation-ops.js";
|
|
13
13
|
export { stableKey } from "./key.js"; // canonical viewKey for an AST (shared with @rindle/react)
|
|
14
14
|
export { COMPARATOR_VERSION, compareNumber, compareRows, compareString, compareValue } from "./compare.js";
|
|
15
15
|
export { CLIENT_MUTATIONS_SCHEMA, CLIENT_MUTATIONS_TABLE, LMID_QUERY_NAME } from "./types.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,0FAA0F;AAC1F,8CAA8C;AAE9C,cAAc,aAAa,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,0FAA0F;AAC1F,8CAA8C;AAE9C,cAAc,aAAa,CAAC,CAAC,oMAAoM;AACjO,cAAc,gBAAgB,CAAC,CAAC,mGAAmG;AACnI,cAAc,YAAY,CAAC,CAAC,uIAAuI;AACnK,cAAc,WAAW,CAAC,CAAC,uEAAuE;AAClG,cAAc,YAAY,CAAC,CAAC,kEAAkE;AAC9F,cAAc,UAAU,CAAC,CAAC,oGAAoG;AAE9H,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC,CAAC,mEAAmE;AAIzH,uFAAuF;AACvF,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,KAAK,EACL,MAAM,GACP,MAAM,mBAAmB,CAAC;AAmB3B,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC,CAAC,2DAA2D;AAEjG,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AA6B3G,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/mutation-ops.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { Ast } from "./ast.ts";
|
|
2
|
+
import type { ColsMap, InsertOf, PkColsOf, PkMap, PkOf, Schema, UpdateOf } from "./schema.ts";
|
|
1
3
|
import type { WireValue } from "./types.ts";
|
|
2
4
|
/** A keyed row: column name → cell. The ergonomic write shape (validated against the schema at
|
|
3
5
|
* runtime). JSON columns carry their raw JSON string (a {@link WireValue}), never a parsed object. */
|
|
@@ -64,23 +66,60 @@ export type BatchEffect = {
|
|
|
64
66
|
kind: "all";
|
|
65
67
|
effects: readonly YieldEffect[];
|
|
66
68
|
};
|
|
67
|
-
/**
|
|
68
|
-
*
|
|
69
|
-
|
|
69
|
+
/** A row returned by a {@link QueryEffect}: column name → cell, plus each materialized relationship
|
|
70
|
+
* name → its nested row(s) — an array (a plural relationship) or a single row / `null` (a `.one()`
|
|
71
|
+
* relationship). Recursive. Presented identically on both tiers (a `view.data` row of the same
|
|
72
|
+
* query). */
|
|
73
|
+
export type QueryResultRow = {
|
|
74
|
+
[key: string]: WireValue | QueryResultRow | QueryResultRow[];
|
|
75
|
+
};
|
|
76
|
+
/** What {@link IsoTx.query} accepts: a query handle whose `.ast()` lowers to the wire {@link Ast} —
|
|
77
|
+
* exactly what the typed query builder produces (`newQueryBuilder(schema).<table>…`, the tier-agnostic
|
|
78
|
+
* server-scope builder both tiers can construct because it performs no I/O). Structural so the seam
|
|
79
|
+
* need not carry the builder's heavy generics. */
|
|
80
|
+
export type QueryArg = {
|
|
81
|
+
ast(): Ast;
|
|
82
|
+
};
|
|
83
|
+
/** A full-shape read a generator mutator yields — a `where`/`orderBy`/`limit`/join query over the
|
|
84
|
+
* state this mutator is mutating (read-your-writes, like {@link ReadEffect} but an arbitrary shape).
|
|
85
|
+
* Evaluates to {@link QueryResultRow}`[]` — ALWAYS an array of the matching rows, in the query's
|
|
86
|
+
* order, on BOTH tiers (a root `.one()` is not unwrapped here: take `[0]`). The single next-type is
|
|
87
|
+
* a row, so cast the yield (`(yield tx.query(q)) as unknown as QueryResultRow[]`), same as `all`. */
|
|
88
|
+
export type QueryEffect = {
|
|
89
|
+
kind: "query";
|
|
90
|
+
query: QueryArg;
|
|
91
|
+
};
|
|
92
|
+
/** Everything a generator mutator may `yield`: a write {@link MutationOp}, a point {@link ReadEffect},
|
|
93
|
+
* a full-query {@link QueryEffect}, or a {@link BatchEffect} fan-out. */
|
|
94
|
+
export type YieldEffect = MutationOp | ReadEffect | BatchEffect | QueryEffect;
|
|
70
95
|
/** The tier-AGNOSTIC effect factory a generator mutator writes against. Every method just BUILDS an
|
|
71
96
|
* effect to `yield` — it performs no I/O and holds no state, so the single {@link isoTx} instance is
|
|
72
97
|
* shared by every mutator on both tiers; only the driver differs. `insert`/`upsert`/`insertIgnore`
|
|
73
98
|
* take a FULL row; `update`/`delete` take the pk columns (`update` adds the columns to change). */
|
|
74
|
-
export interface IsoTx {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
99
|
+
export interface IsoTx<S extends ColsMap = ColsMap, P extends Record<string, string> = PkMap<S>> {
|
|
100
|
+
/** Insert a FULL row — nullable columns may be omitted (filled `null`, design 206 §6.2/§7). */
|
|
101
|
+
insert<N extends keyof S & string>(table: N, row: InsertOf<S[N]>): MutationOp;
|
|
102
|
+
/** Update the row identified by its pk columns (REQUIRED); only the named non-pk columns change. */
|
|
103
|
+
update<N extends keyof S & string>(table: N, row: UpdateOf<S[N], PkColsOf<S, P, N>>): MutationOp;
|
|
104
|
+
/** Insert, or fully replace on pk conflict (a FULL row, like {@link IsoTx.insert}). */
|
|
105
|
+
upsert<N extends keyof S & string>(table: N, row: InsertOf<S[N]>): MutationOp;
|
|
106
|
+
/** Insert a FULL row, or do nothing if the pk already exists. */
|
|
107
|
+
insertIgnore<N extends keyof S & string>(table: N, row: InsertOf<S[N]>): MutationOp;
|
|
108
|
+
/** Delete the row identified by its pk columns. */
|
|
109
|
+
delete<N extends keyof S & string>(table: N, pk: PkOf<S[N], PkColsOf<S, P, N>>): MutationOp;
|
|
110
|
+
/** Read one row by primary key (read-your-writes). */
|
|
111
|
+
row<N extends keyof S & string>(table: N, pk: PkOf<S[N], PkColsOf<S, P, N>>): ReadEffect;
|
|
112
|
+
/** Run a full query (`where`/`orderBy`/`limit`/join) over the state this mutator is mutating —
|
|
113
|
+
* read-your-writes, like {@link row} but for arbitrary shapes. Pass a query from the tier-agnostic
|
|
114
|
+
* builder, e.g. `tx.query(q.issue.where("ownerId", "=", ctx.user))` where `q = newQueryBuilder(schema)`.
|
|
115
|
+
* The `yield` evaluates to {@link QueryResultRow}`[]` (cast it — the generator's single next-type is a row). */
|
|
116
|
+
query(query: QueryArg): QueryEffect;
|
|
81
117
|
all(effects: readonly YieldEffect[]): BatchEffect;
|
|
82
118
|
}
|
|
83
|
-
/** The one shared effect factory (stateless — see {@link IsoTx}).
|
|
119
|
+
/** The one shared effect factory (stateless — see {@link IsoTx}). Its methods just BUILD a
|
|
120
|
+
* {@link MutationOp}, so the single instance serves every schema; the generic {@link IsoTx} view is
|
|
121
|
+
* applied at the authoring site (a `json<T>` cell is a parsed object here and is stringified by the
|
|
122
|
+
* funnels, {@link toCell}), hence the cast — the runtime shape is schema-agnostic. */
|
|
84
123
|
export declare const isoTx: IsoTx;
|
|
85
124
|
/** The minimal per-invocation context a shared mutator sees on BOTH tiers: the acting principal. The
|
|
86
125
|
* server injects its AUTHENTICATED user; the client injects its local user. (The server's own
|
|
@@ -116,6 +155,23 @@ export type SharedMutatorWithArgs<Args, Ctx extends MutatorCtx = MutatorCtx> = S
|
|
|
116
155
|
* registered value is byte-for-byte what the client drove before: {@link isGeneratorMutator} still
|
|
117
156
|
* detects it and it still `satisfies ClientRegistry`. */
|
|
118
157
|
export declare function shared<Args, Ctx extends MutatorCtx = MutatorCtx>(args: ArgSchema<Args>, run: SharedMutator<Args, Ctx>): SharedMutatorWithArgs<Args, Ctx>;
|
|
158
|
+
/** Bind a schema to the mutator authoring surface: `const { shared } = defineMutators(schema)`.
|
|
159
|
+
*
|
|
160
|
+
* The returned `shared` is the schema-typed twin of the bare {@link shared} — its `tx` is an
|
|
161
|
+
* {@link IsoTx} parameterized by THIS schema, so `tx.insert`/`update`/`upsert`/`insertIgnore`/`delete`
|
|
162
|
+
* check the table name, every column name, each column's value type, nullable-omit on inserts, and
|
|
163
|
+
* the exact primary-key columns on `update`/`delete` — all at compile time. It registers identically
|
|
164
|
+
* to the bare form (same runtime value, still `satisfies ClientRegistry`, still an isomorphic
|
|
165
|
+
* generator the server drives): only the AUTHORING types tighten. `schema` is read for its TYPE only
|
|
166
|
+
* (never at runtime). Reads (`yield tx.row(...)`) stay loosely typed — a generator's single
|
|
167
|
+
* next-type can't carry a per-table row. */
|
|
168
|
+
/** The typed {@link IsoTx} for a given schema — `IsoTxOf<typeof schema>`. Use it to annotate a helper
|
|
169
|
+
* that a mutator body passes its `tx` to (e.g. `const ensureUser = (tx: IsoTxOf<typeof schema>, …)`),
|
|
170
|
+
* so the helper gets the same table/column/pk typing the `defineMutators` `shared` callback does. */
|
|
171
|
+
export type IsoTxOf<Sch extends Schema> = Sch extends Schema<infer S, infer P> ? IsoTx<S, P> : never;
|
|
172
|
+
export declare function defineMutators<S extends ColsMap, P extends Record<string, string>>(_schema: Schema<S, P>): {
|
|
173
|
+
shared<Args, Ctx extends MutatorCtx = MutatorCtx>(args: ArgSchema<Args>, run: (tx: IsoTx<S, P>, args: Args, ctx: Ctx) => MutationGen): SharedMutatorWithArgs<Args, Ctx>;
|
|
174
|
+
};
|
|
119
175
|
/** True iff `fn` is a generator function (a shared/isomorphic mutator) rather than a plain function —
|
|
120
176
|
* the driver accepts both forms. Detected structurally (native `GeneratorFunction`). */
|
|
121
177
|
export declare function isGeneratorMutator(fn: unknown): fn is (...args: never[]) => MutationGen;
|
|
@@ -124,17 +180,21 @@ export declare function isGeneratorMutator(fn: unknown): fn is (...args: never[]
|
|
|
124
180
|
export interface SyncEffectExec {
|
|
125
181
|
apply(op: MutationOp): void;
|
|
126
182
|
read(table: string, pk: KeyedRow): KeyedRow | undefined;
|
|
183
|
+
query(q: QueryArg): QueryResultRow[];
|
|
127
184
|
}
|
|
128
185
|
/** Drive a generator mutator SYNCHRONOUSLY (the client, inside the wasm write transaction). Each
|
|
129
|
-
* yielded write applies immediately; each read is resolved and fed back; a batch
|
|
186
|
+
* yielded write applies immediately; each read (point or query) is resolved and fed back; a batch
|
|
187
|
+
* runs in order. */
|
|
130
188
|
export declare function driveMutationSync(gen: MutationGen, exec: SyncEffectExec): void;
|
|
131
189
|
/** A tier's ASYNCHRONOUS effect executor (the server transaction): every op is a Promise. */
|
|
132
190
|
export interface AsyncEffectExec {
|
|
133
191
|
apply(op: MutationOp): Promise<void>;
|
|
134
192
|
read(table: string, pk: KeyedRow): Promise<KeyedRow | undefined>;
|
|
193
|
+
query(q: QueryArg): Promise<QueryResultRow[]>;
|
|
135
194
|
}
|
|
136
195
|
/** Drive a generator mutator ASYNCHRONOUSLY (the server, against the open transaction). Writes are
|
|
137
196
|
* awaited (harmless: a single interactive Postgres connection serializes statements anyway, and the
|
|
138
|
-
* daemon backend resolves them instantly), reads suspend, and a batch fans out with
|
|
197
|
+
* daemon backend resolves them instantly), reads (point or query) suspend, and a batch fans out with
|
|
198
|
+
* `Promise.all`. */
|
|
139
199
|
export declare function driveMutationAsync(gen: MutationGen, exec: AsyncEffectExec): Promise<void>;
|
|
140
200
|
//# sourceMappingURL=mutation-ops.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation-ops.d.ts","sourceRoot":"","sources":["../src/mutation-ops.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;uGACuG;AACvG,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAEjD;6FAC6F;AAC7F,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,GAChD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,QAAQ,CAAA;CAAE,CAAC;AAEpD;;;;uFAIuF;AACvF,MAAM,WAAW,aAAa;IAC5B,gFAAgF;IAChF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD;0BACsB;IACtB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,uFAAuF;IACvF,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD;sDACkD;IAClD,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,6EAA6E;IAC7E,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD;;kDAE8C;IAC9C,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;CACjE;
|
|
1
|
+
{"version":3,"file":"mutation-ops.d.ts","sourceRoot":"","sources":["../src/mutation-ops.ts"],"names":[],"mappings":"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"}
|
package/dist/mutation-ops.js
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
// insertIgnore/delete over a keyed row), not HOW — the server renders dialect SQL and the
|
|
4
4
|
// client applies to its local engine. This lives in `@rindle/client` (the leaf both tiers
|
|
5
5
|
// import); the SQL renderer that consumes it is server-only (`@rindle/api-server`).
|
|
6
|
-
/** The one shared effect factory (stateless — see {@link IsoTx}).
|
|
6
|
+
/** The one shared effect factory (stateless — see {@link IsoTx}). Its methods just BUILD a
|
|
7
|
+
* {@link MutationOp}, so the single instance serves every schema; the generic {@link IsoTx} view is
|
|
8
|
+
* applied at the authoring site (a `json<T>` cell is a parsed object here and is stringified by the
|
|
9
|
+
* funnels, {@link toCell}), hence the cast — the runtime shape is schema-agnostic. */
|
|
7
10
|
export const isoTx = {
|
|
8
11
|
insert: (table, row) => ({ kind: "insert", table, row }),
|
|
9
12
|
update: (table, row) => ({ kind: "update", table, row }),
|
|
@@ -11,6 +14,7 @@ export const isoTx = {
|
|
|
11
14
|
insertIgnore: (table, row) => ({ kind: "insertIgnore", table, row }),
|
|
12
15
|
delete: (table, pk) => ({ kind: "delete", table, pk }),
|
|
13
16
|
row: (table, pk) => ({ kind: "row", table, pk }),
|
|
17
|
+
query: (query) => ({ kind: "query", query }),
|
|
14
18
|
all: (effects) => ({ kind: "all", effects }),
|
|
15
19
|
};
|
|
16
20
|
/** Co-locate a shared (generator) mutator with the validator for its args — pairing the arg SHAPE
|
|
@@ -22,6 +26,15 @@ export const isoTx = {
|
|
|
22
26
|
export function shared(args, run) {
|
|
23
27
|
return Object.assign(run, { args });
|
|
24
28
|
}
|
|
29
|
+
export function defineMutators(_schema) {
|
|
30
|
+
return {
|
|
31
|
+
shared(args, run) {
|
|
32
|
+
// The typed `tx: IsoTx<S, P>` is authoring-only; the driver invokes with the loose `isoTx`
|
|
33
|
+
// singleton (cast at the call boundary, like the bare `shared`), so the value is registry-shaped.
|
|
34
|
+
return Object.assign(run, { args });
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
25
38
|
/** True iff `fn` is a generator function (a shared/isomorphic mutator) rather than a plain function —
|
|
26
39
|
* the driver accepts both forms. Detected structurally (native `GeneratorFunction`). */
|
|
27
40
|
export function isGeneratorMutator(fn) {
|
|
@@ -29,11 +42,14 @@ export function isGeneratorMutator(fn) {
|
|
|
29
42
|
fn.constructor?.name === "GeneratorFunction");
|
|
30
43
|
}
|
|
31
44
|
/** Drive a generator mutator SYNCHRONOUSLY (the client, inside the wasm write transaction). Each
|
|
32
|
-
* yielded write applies immediately; each read is resolved and fed back; a batch
|
|
45
|
+
* yielded write applies immediately; each read (point or query) is resolved and fed back; a batch
|
|
46
|
+
* runs in order. */
|
|
33
47
|
export function driveMutationSync(gen, exec) {
|
|
34
48
|
const run = (eff) => {
|
|
35
49
|
if (eff.kind === "row")
|
|
36
50
|
return exec.read(eff.table, eff.pk);
|
|
51
|
+
if (eff.kind === "query")
|
|
52
|
+
return exec.query(eff.query);
|
|
37
53
|
if (eff.kind === "all")
|
|
38
54
|
return eff.effects.map(run);
|
|
39
55
|
exec.apply(eff);
|
|
@@ -44,11 +60,14 @@ export function driveMutationSync(gen, exec) {
|
|
|
44
60
|
}
|
|
45
61
|
/** Drive a generator mutator ASYNCHRONOUSLY (the server, against the open transaction). Writes are
|
|
46
62
|
* awaited (harmless: a single interactive Postgres connection serializes statements anyway, and the
|
|
47
|
-
* daemon backend resolves them instantly), reads suspend, and a batch fans out with
|
|
63
|
+
* daemon backend resolves them instantly), reads (point or query) suspend, and a batch fans out with
|
|
64
|
+
* `Promise.all`. */
|
|
48
65
|
export async function driveMutationAsync(gen, exec) {
|
|
49
66
|
const run = async (eff) => {
|
|
50
67
|
if (eff.kind === "row")
|
|
51
68
|
return exec.read(eff.table, eff.pk);
|
|
69
|
+
if (eff.kind === "query")
|
|
70
|
+
return (await exec.query(eff.query));
|
|
52
71
|
if (eff.kind === "all")
|
|
53
72
|
return (await Promise.all(eff.effects.map(run)));
|
|
54
73
|
await exec.apply(eff);
|
package/dist/mutation-ops.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mutation-ops.js","sourceRoot":"","sources":["../src/mutation-ops.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,oFAAoF;AACpF,0FAA0F;AAC1F,0FAA0F;AAC1F,oFAAoF;
|
|
1
|
+
{"version":3,"file":"mutation-ops.js","sourceRoot":"","sources":["../src/mutation-ops.ts"],"names":[],"mappings":"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"}
|