@gonvex/client 0.1.32 → 0.3.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 +119 -95
- package/dist/control.d.ts +416 -0
- package/dist/control.js +210 -0
- package/dist/control.js.map +1 -0
- package/dist/error-reporter.d.ts +20 -6
- package/dist/error-reporter.js +55 -27
- package/dist/error-reporter.js.map +1 -1
- package/dist/index.d.ts +166 -133
- package/dist/index.js +1114 -1139
- package/dist/index.js.map +1 -1
- package/dist/indexeddb-replica.d.ts +20 -0
- package/dist/indexeddb-replica.js +193 -0
- package/dist/indexeddb-replica.js.map +1 -0
- package/dist/kv-stores.d.ts +2 -48
- package/dist/kv-stores.js +24 -411
- package/dist/kv-stores.js.map +1 -1
- package/dist/local-replica.d.ts +240 -0
- package/dist/local-replica.js +590 -0
- package/dist/local-replica.js.map +1 -0
- package/dist/optimistic.d.ts +34 -55
- package/dist/optimistic.js +70 -269
- package/dist/optimistic.js.map +1 -1
- package/dist/outbox.d.ts +25 -25
- package/dist/outbox.js +27 -27
- package/dist/outbox.js.map +1 -1
- package/dist/query-expression.d.ts +58 -0
- package/dist/query-expression.js +164 -0
- package/dist/query-expression.js.map +1 -0
- package/dist/replica-integrity.d.ts +5 -0
- package/dist/replica-integrity.js +58 -0
- package/dist/replica-integrity.js.map +1 -0
- package/package.json +4 -4
- package/dist/browser-cache-client.d.ts +0 -77
- package/dist/browser-cache-client.js +0 -156
- package/dist/browser-cache-client.js.map +0 -1
- package/dist/browser-cache-shared-worker.d.ts +0 -35
- package/dist/browser-cache-shared-worker.js +0 -118
- package/dist/browser-cache-shared-worker.js.map +0 -1
- package/dist/browser-cache.d.ts +0 -43
- package/dist/browser-cache.js +0 -67
- package/dist/browser-cache.js.map +0 -1
- package/dist/browser-capabilities.d.ts +0 -21
- package/dist/browser-capabilities.js +0 -31
- package/dist/browser-capabilities.js.map +0 -1
- package/dist/cache-coordinator.d.ts +0 -37
- package/dist/cache-coordinator.js +0 -109
- package/dist/cache-coordinator.js.map +0 -1
- package/dist/cache.d.ts +0 -74
- package/dist/cache.js +0 -120
- package/dist/cache.js.map +0 -1
- package/dist/persistent-cache.d.ts +0 -41
- package/dist/persistent-cache.js +0 -103
- package/dist/persistent-cache.js.map +0 -1
- package/dist/query-cache.d.ts +0 -88
- package/dist/query-cache.js +0 -346
- package/dist/query-cache.js.map +0 -1
- package/dist/sync-store.d.ts +0 -96
- package/dist/sync-store.js +0 -500
- package/dist/sync-store.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,11 +1,36 @@
|
|
|
1
1
|
# @gonvex/client
|
|
2
2
|
|
|
3
|
-
Browser client for Gonvex
|
|
4
|
-
|
|
3
|
+
Browser client for Gonvex Queries, Reducers, Actions, Live Queries, the
|
|
4
|
+
persistent Local Replica, and telemetry.
|
|
5
5
|
|
|
6
6
|
Most React apps should use `@gonvex/react`, which wraps this package with hooks.
|
|
7
7
|
Use `@gonvex/client` directly when you want lower-level control.
|
|
8
8
|
|
|
9
|
+
## Control Plane
|
|
10
|
+
|
|
11
|
+
Host-owned account, tenant-directory, invitation, agent-auth, voice, and support
|
|
12
|
+
functions use the same persistent connection as tenant functions:
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { control } from "@gonvex/client";
|
|
16
|
+
|
|
17
|
+
const account = await client.query(control.accounts.me, {});
|
|
18
|
+
const tenants = await client.query(control.tenants.mine, {});
|
|
19
|
+
await client.reducer(control.tenants.updateTimezone, { timezone: "America/Los_Angeles" });
|
|
20
|
+
|
|
21
|
+
const stop = client.watchControlQuery(control.support.sessions, {}, (result) => {
|
|
22
|
+
renderSessions(result);
|
|
23
|
+
});
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The references include argument/result schemas and their authorization class.
|
|
27
|
+
They never accept database URLs. Tenant-admin references always operate on the
|
|
28
|
+
active, authoritatively admitted tenant.
|
|
29
|
+
|
|
30
|
+
Control Plane live Queries resubscribe on reconnect and refresh after an
|
|
31
|
+
authorized Control Plane Reducer. Use `watchControlQuery` instead of refetching
|
|
32
|
+
after a write.
|
|
33
|
+
|
|
9
34
|
## Install
|
|
10
35
|
|
|
11
36
|
```bash
|
|
@@ -16,6 +41,7 @@ npm install @gonvex/client
|
|
|
16
41
|
|
|
17
42
|
```ts
|
|
18
43
|
import { GonvexClient } from "@gonvex/client";
|
|
44
|
+
import { api } from "./gonvex/_generated/api";
|
|
19
45
|
|
|
20
46
|
const client = new GonvexClient("ws://localhost:8080/ws", {
|
|
21
47
|
project: "my-project",
|
|
@@ -24,8 +50,8 @@ const client = new GonvexClient("ws://localhost:8080/ws", {
|
|
|
24
50
|
|
|
25
51
|
client.connect();
|
|
26
52
|
|
|
27
|
-
const unsubscribe = client.
|
|
28
|
-
|
|
53
|
+
const unsubscribe = client.subscribeLiveQuery(
|
|
54
|
+
api.tasks.list,
|
|
29
55
|
{ status: "open" },
|
|
30
56
|
(message) => {
|
|
31
57
|
if (message.type === "query.result") {
|
|
@@ -34,8 +60,8 @@ const unsubscribe = client.subscribeQuery(
|
|
|
34
60
|
},
|
|
35
61
|
);
|
|
36
62
|
|
|
37
|
-
await client.
|
|
38
|
-
{ kind: "
|
|
63
|
+
await client.reducer(
|
|
64
|
+
{ kind: "reducer", path: "tasks.create" },
|
|
39
65
|
{ title: "Ship Gonvex" },
|
|
40
66
|
);
|
|
41
67
|
|
|
@@ -43,110 +69,94 @@ unsubscribe();
|
|
|
43
69
|
client.close();
|
|
44
70
|
```
|
|
45
71
|
|
|
46
|
-
##
|
|
72
|
+
## Local Replica
|
|
47
73
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
runs in parallel. The server result always wins and refreshes the snapshot,
|
|
52
|
-
including results produced by realtime invalidation.
|
|
74
|
+
The Local Replica is the only client-side server-state store. It owns normalized
|
|
75
|
+
entities, live-query/replica windows, revision metadata, and optimistic command
|
|
76
|
+
patches. One-shot Query results are transient and are never persisted.
|
|
53
77
|
|
|
54
|
-
|
|
55
|
-
permissions. New clients connected to older runtimes stay server-only.
|
|
56
|
-
|
|
57
|
-
No setup is required. To opt out or clear the disposable cache:
|
|
78
|
+
Inject a durable adapter when desired; omit it for memory-only operation:
|
|
58
79
|
|
|
59
80
|
```ts
|
|
60
|
-
const client = new GonvexClient(url, {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
await client.clearQueryCache({ allScopes: true });
|
|
81
|
+
const client = new GonvexClient(url, {
|
|
82
|
+
localReplica: { storage: indexedDbReplicaStorage },
|
|
83
|
+
});
|
|
64
84
|
```
|
|
65
85
|
|
|
66
|
-
|
|
67
|
-
|
|
86
|
+
All window updates are applied atomically before listeners are notified. Live
|
|
87
|
+
Query windows and Replica Collections reference the same normalized entities,
|
|
88
|
+
so every view of an entity converges together.
|
|
68
89
|
|
|
69
|
-
##
|
|
90
|
+
## Replica Collections
|
|
70
91
|
|
|
71
|
-
|
|
72
|
-
|
|
92
|
+
Replica Collections materialize bounded, authorized entity sets in normalized
|
|
93
|
+
IndexedDB storage and resume them from a durable Postgres revision:
|
|
73
94
|
|
|
74
95
|
```ts
|
|
75
|
-
const watch = client.
|
|
76
|
-
|
|
96
|
+
const watch = client.watchReplica<Task>(
|
|
97
|
+
api.tasks.recent,
|
|
77
98
|
{ workspaceId: "workspace-a" },
|
|
78
99
|
);
|
|
79
100
|
|
|
80
101
|
const stop = watch.onUpdate(() => {
|
|
81
|
-
|
|
102
|
+
const state = watch.localReplicaState();
|
|
103
|
+
render(state?.rows ?? []);
|
|
104
|
+
console.log(state?.completeness, state?.truncated, state?.computedRevision);
|
|
82
105
|
console.log(watch.status()); // { isLoading, isUpToDate }
|
|
83
106
|
});
|
|
84
107
|
```
|
|
85
108
|
|
|
86
|
-
Configure
|
|
109
|
+
Configure Local Replica persistence when constructing the client:
|
|
87
110
|
|
|
88
111
|
```ts
|
|
89
112
|
const client = new GonvexClient(url, {
|
|
90
|
-
|
|
91
|
-
databaseName: "my-product-sync",
|
|
92
|
-
maxBytes: 150 * 1024 * 1024,
|
|
93
|
-
},
|
|
113
|
+
localReplica: { storage: indexedDbReplicaStorage },
|
|
94
114
|
});
|
|
95
|
-
|
|
96
|
-
const memoryOnly = new GonvexClient(url, { sync: false });
|
|
97
115
|
```
|
|
98
116
|
|
|
99
|
-
|
|
100
|
-
row/byte budgets still apply, and least-recently-used collections are evicted
|
|
101
|
-
first. Storage is isolated by runtime, project, tenant, authenticated identity,
|
|
102
|
-
and permissions.
|
|
117
|
+
## Optimistic Reducers
|
|
103
118
|
|
|
104
|
-
|
|
119
|
+
Every public interactive Reducer declares its optimistic transaction. The
|
|
120
|
+
authoritative transaction and optimistic patches are reconciled in LocalReplica.
|
|
105
121
|
|
|
106
|
-
|
|
107
|
-
read projects that entity:
|
|
108
|
-
|
|
109
|
-
```go
|
|
110
|
-
app.Mutation(
|
|
111
|
-
"tasks.update",
|
|
112
|
-
updateTask,
|
|
113
|
-
gonvex.OptimisticMutation("tasks").RowIDArg("taskId").FieldsArg("updates"),
|
|
114
|
-
)
|
|
115
|
-
|
|
116
|
-
app.Query(
|
|
117
|
-
"tasks.byWorkspace",
|
|
118
|
-
tasksByWorkspace,
|
|
119
|
-
gonvex.OptimisticProjection("tasks").Key("_id").ResultPath("page"),
|
|
120
|
-
)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Generated references carry this metadata, so a normal mutation call is enough:
|
|
122
|
+
Generated references carry this metadata, so a normal Reducer call is enough:
|
|
124
123
|
|
|
125
124
|
```ts
|
|
126
|
-
await client.
|
|
125
|
+
await client.reducer(api.tasks.update, {
|
|
127
126
|
taskId,
|
|
128
127
|
updates: { priority_id: priorityId },
|
|
129
128
|
});
|
|
130
129
|
```
|
|
131
130
|
|
|
132
|
-
The client persists the pending
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
remove the overlay and repaint the authoritative rows. This keeps stale query
|
|
138
|
-
or sync frames from briefly reverting the UI.
|
|
131
|
+
The client persists the pending command, applies it through LocalReplica, and
|
|
132
|
+
notifies watchers immediately. Reducer success includes an
|
|
133
|
+
`originCommandId` and committed revision. The overlay is removed only after the
|
|
134
|
+
corresponding authoritative transaction has been applied locally, preventing an
|
|
135
|
+
empty or stale frame between optimistic and committed state.
|
|
139
136
|
|
|
140
|
-
|
|
141
|
-
|
|
137
|
+
Authoritative entity state and optimistic entities are materialized by one
|
|
138
|
+
LocalReplica graph. Durable pending state lives in the command outbox and is
|
|
139
|
+
re-applied after reload. Outbox
|
|
142
140
|
rows are isolated by project, tenant, and authenticated identity; an account
|
|
143
141
|
switch removes the previous identity's overlay and can never replay its writes
|
|
144
142
|
under the new session. Unscoped rows from the pre-isolation schema are removed
|
|
145
143
|
during migration because their owner cannot be proven. If an opaque credential
|
|
146
144
|
does not expose a stable identity (and no `identity` hint is supplied), its
|
|
147
|
-
outbox is deliberately session-only rather than risking cross-user replay.
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
outbox is deliberately session-only rather than risking cross-user replay.
|
|
146
|
+
|
|
147
|
+
## Offline Live Queries
|
|
148
|
+
|
|
149
|
+
Generated Live Query references include the same structured plan Gonvex
|
|
150
|
+
compiles to PostgreSQL. While offline, the client executes that plan over the
|
|
151
|
+
normalized cached corpus:
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const result = client.offlineLiveQuery(api.tasks.grid, args);
|
|
155
|
+
// { rows, completeness: "complete" | "partial", supported }
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
`supported: false` means the plan is explicitly server-only. A partial result
|
|
159
|
+
must be labeled as cached data rather than presented as the full database.
|
|
150
160
|
|
|
151
161
|
## Lightweight Error Tracking
|
|
152
162
|
|
|
@@ -165,9 +175,23 @@ const client = new GonvexClient(url, {
|
|
|
165
175
|
});
|
|
166
176
|
```
|
|
167
177
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
178
|
+
Error registration and envelopes use native persistent-protocol frames. No
|
|
179
|
+
browser HTTP ingestion endpoint is needed.
|
|
180
|
+
|
|
181
|
+
Applications can report an explicit event through the public API:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
await client.reportError("envelope", {
|
|
185
|
+
events: [{
|
|
186
|
+
message: "Task preview failed",
|
|
187
|
+
level: "error",
|
|
188
|
+
context: { component: "TaskPreview" },
|
|
189
|
+
}],
|
|
190
|
+
});
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`new GonvexErrorReporter({ client })` adds global browser error capture and
|
|
194
|
+
re-registers its telemetry session after reconnect.
|
|
171
195
|
|
|
172
196
|
## Connection reliability
|
|
173
197
|
|
|
@@ -180,7 +204,7 @@ reconnect.
|
|
|
180
204
|
client.connectionState();
|
|
181
205
|
// {
|
|
182
206
|
// isWebSocketConnected, hasEverConnected, connectionCount, connectionRetries,
|
|
183
|
-
// hasInflightRequests,
|
|
207
|
+
// hasInflightRequests, inflightReducers, inflightActions, inflightOneShotQueries
|
|
184
208
|
// }
|
|
185
209
|
|
|
186
210
|
const stop = client.subscribeToConnectionState((state) => {
|
|
@@ -193,7 +217,7 @@ const stop = client.subscribeToConnectionState((state) => {
|
|
|
193
217
|
| Operation | Default |
|
|
194
218
|
| --- | --- |
|
|
195
219
|
| One-shot `query()` | 20s |
|
|
196
|
-
| `
|
|
220
|
+
| `reducer()` | 20s |
|
|
197
221
|
| `action()` | 60s |
|
|
198
222
|
|
|
199
223
|
Override per client (`timeouts` option) or per call (`{ timeoutMs }`). Use `0` to disable.
|
|
@@ -202,43 +226,43 @@ Override per client (`timeouts` option) or per call (`{ timeoutMs }`). Use `0` t
|
|
|
202
226
|
|
|
203
227
|
Rejected operations throw `GonvexClientError` with `code`:
|
|
204
228
|
|
|
205
|
-
- `server
|
|
206
|
-
- `timeout
|
|
207
|
-
- `disconnected
|
|
208
|
-
- `closed
|
|
209
|
-
- `auth
|
|
229
|
+
- `server`: runtime executed the function and returned an error
|
|
230
|
+
- `timeout`: no response within the timeout
|
|
231
|
+
- `disconnected`: socket dropped while the operation was pending
|
|
232
|
+
- `closed`: client was explicitly closed
|
|
233
|
+
- `auth`: authentication rejected
|
|
210
234
|
|
|
211
|
-
###
|
|
235
|
+
### Reducer / Action disconnect policy
|
|
212
236
|
|
|
213
|
-
Actions and
|
|
237
|
+
Actions and Reducers without `{ offline: "queue" }` fail closed after a
|
|
214
238
|
disconnect. They reject with `code: "disconnected"` (or `timeout` / `closed`).
|
|
215
|
-
Optimistic
|
|
239
|
+
Optimistic Reducers are persisted before transport even in fail-closed mode,
|
|
216
240
|
so a process reload cannot expose an older cached row while an accepted write
|
|
217
241
|
is still waiting for its authoritative subscription update.
|
|
218
242
|
|
|
219
|
-
Pass `{ offline: "queue" }` to a
|
|
243
|
+
Pass `{ offline: "queue" }` to a Reducer to durably accept a transport failure
|
|
220
244
|
and replay the same idempotency key after reconnect, whether or not that
|
|
221
|
-
|
|
245
|
+
Reducer also declares optimistic UI metadata. Actions are never queued.
|
|
222
246
|
Deterministic server errors are never queued and always roll an optimistic
|
|
223
247
|
entity overlay back when one exists.
|
|
224
248
|
|
|
225
|
-
Live
|
|
226
|
-
resubscribe after reconnect. Call `client.
|
|
227
|
-
re-request after a server error
|
|
249
|
+
Live Queries persist their last verified window in the Local Replica and
|
|
250
|
+
resubscribe after reconnect. Call `client.retryLiveQuery(ref, args)` to force a
|
|
251
|
+
re-request after a server error. `useQueryResult` is for one-shot Queries.
|
|
228
252
|
|
|
229
253
|
## Exports
|
|
230
254
|
|
|
231
255
|
The package exports:
|
|
232
256
|
|
|
233
257
|
- `GonvexClient`
|
|
234
|
-
- `ConvexReactClient` compatibility alias
|
|
235
258
|
- `GonvexClientError`, `ConnectionState`, timeout defaults
|
|
236
|
-
-
|
|
237
|
-
- durable
|
|
238
|
-
- opt-in
|
|
239
|
-
- `
|
|
259
|
+
- normalized Local Replica entities and Live Query windows
|
|
260
|
+
- durable optimistic Reducer overlays reconciled by command ID and revision
|
|
261
|
+
- opt-in command outbox replay with stable idempotency keys
|
|
262
|
+
- `subscribeReplica`, `watchReplica`, and persistent Replica storage
|
|
263
|
+
- `watchControlQuery` for authorized Control Plane live Queries
|
|
240
264
|
- browser capability and telemetry helpers
|
|
241
|
-
- `GonvexErrorReporter
|
|
265
|
+
- `reportError`, `GonvexErrorReporter`, and automatic operation error reporting
|
|
242
266
|
|
|
243
267
|
## Related Packages
|
|
244
268
|
|