@gonvex/client 0.1.32 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +125 -95
  2. package/dist/control.d.ts +416 -0
  3. package/dist/control.js +210 -0
  4. package/dist/control.js.map +1 -0
  5. package/dist/error-reporter.d.ts +20 -6
  6. package/dist/error-reporter.js +55 -27
  7. package/dist/error-reporter.js.map +1 -1
  8. package/dist/index.d.ts +176 -133
  9. package/dist/index.js +1177 -1140
  10. package/dist/index.js.map +1 -1
  11. package/dist/indexeddb-replica.d.ts +20 -0
  12. package/dist/indexeddb-replica.js +193 -0
  13. package/dist/indexeddb-replica.js.map +1 -0
  14. package/dist/kv-stores.d.ts +2 -48
  15. package/dist/kv-stores.js +24 -411
  16. package/dist/kv-stores.js.map +1 -1
  17. package/dist/local-replica.d.ts +240 -0
  18. package/dist/local-replica.js +590 -0
  19. package/dist/local-replica.js.map +1 -0
  20. package/dist/optimistic.d.ts +34 -55
  21. package/dist/optimistic.js +70 -269
  22. package/dist/optimistic.js.map +1 -1
  23. package/dist/outbox.d.ts +25 -25
  24. package/dist/outbox.js +27 -27
  25. package/dist/outbox.js.map +1 -1
  26. package/dist/query-expression.d.ts +58 -0
  27. package/dist/query-expression.js +164 -0
  28. package/dist/query-expression.js.map +1 -0
  29. package/dist/replica-integrity.d.ts +5 -0
  30. package/dist/replica-integrity.js +58 -0
  31. package/dist/replica-integrity.js.map +1 -0
  32. package/package.json +4 -4
  33. package/dist/browser-cache-client.d.ts +0 -77
  34. package/dist/browser-cache-client.js +0 -156
  35. package/dist/browser-cache-client.js.map +0 -1
  36. package/dist/browser-cache-shared-worker.d.ts +0 -35
  37. package/dist/browser-cache-shared-worker.js +0 -118
  38. package/dist/browser-cache-shared-worker.js.map +0 -1
  39. package/dist/browser-cache.d.ts +0 -43
  40. package/dist/browser-cache.js +0 -67
  41. package/dist/browser-cache.js.map +0 -1
  42. package/dist/browser-capabilities.d.ts +0 -21
  43. package/dist/browser-capabilities.js +0 -31
  44. package/dist/browser-capabilities.js.map +0 -1
  45. package/dist/cache-coordinator.d.ts +0 -37
  46. package/dist/cache-coordinator.js +0 -109
  47. package/dist/cache-coordinator.js.map +0 -1
  48. package/dist/cache.d.ts +0 -74
  49. package/dist/cache.js +0 -120
  50. package/dist/cache.js.map +0 -1
  51. package/dist/persistent-cache.d.ts +0 -41
  52. package/dist/persistent-cache.js +0 -103
  53. package/dist/persistent-cache.js.map +0 -1
  54. package/dist/query-cache.d.ts +0 -88
  55. package/dist/query-cache.js +0 -346
  56. package/dist/query-cache.js.map +0 -1
  57. package/dist/sync-store.d.ts +0 -96
  58. package/dist/sync-store.js +0 -500
  59. package/dist/sync-store.js.map +0 -1
package/README.md CHANGED
@@ -1,11 +1,42 @@
1
1
  # @gonvex/client
2
2
 
3
- Browser client for Gonvex realtime queries, mutations, actions, telemetry, and
4
- local cache helpers.
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
+
34
+ `authenticate` installs a new authentication scope and resolves only after the
35
+ runtime accepts it. It exists for provider-owned transitions such as developer
36
+ mode; React applications should use `GonvexAuthProvider`. The runtime rotates
37
+ single-use developer credentials on every connection, and the client retains
38
+ their successor only in memory.
39
+
9
40
  ## Install
10
41
 
11
42
  ```bash
@@ -16,6 +47,7 @@ npm install @gonvex/client
16
47
 
17
48
  ```ts
18
49
  import { GonvexClient } from "@gonvex/client";
50
+ import { api } from "./gonvex/_generated/api";
19
51
 
20
52
  const client = new GonvexClient("ws://localhost:8080/ws", {
21
53
  project: "my-project",
@@ -24,8 +56,8 @@ const client = new GonvexClient("ws://localhost:8080/ws", {
24
56
 
25
57
  client.connect();
26
58
 
27
- const unsubscribe = client.subscribeQuery(
28
- { kind: "query", path: "tasks.list" },
59
+ const unsubscribe = client.subscribeLiveQuery(
60
+ api.tasks.list,
29
61
  { status: "open" },
30
62
  (message) => {
31
63
  if (message.type === "query.result") {
@@ -34,8 +66,8 @@ const unsubscribe = client.subscribeQuery(
34
66
  },
35
67
  );
36
68
 
37
- await client.mutation(
38
- { kind: "mutation", path: "tasks.create" },
69
+ await client.reducer(
70
+ { kind: "reducer", path: "tasks.create" },
39
71
  { title: "Ship Gonvex" },
40
72
  );
41
73
 
@@ -43,110 +75,94 @@ unsubscribe();
43
75
  client.close();
44
76
  ```
45
77
 
46
- ## Transparent Browser Cache
78
+ ## Local Replica
47
79
 
48
- Live query results are persisted automatically in supported browsers when the
49
- runtime advertises a safe cache scope. A warm `subscribeQuery`, `watchQuery`, or
50
- React `useQuery` can replay its last result while the normal server subscription
51
- runs in parallel. The server result always wins and refreshes the snapshot,
52
- including results produced by realtime invalidation.
80
+ The Local Replica is the only client-side server-state store. It owns normalized
81
+ entities, live-query/replica windows, revision metadata, and optimistic command
82
+ patches. One-shot Query results are transient and are never persisted.
53
83
 
54
- Caching is isolated by runtime deployment, project, tenant, user, and current
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:
84
+ Inject a durable adapter when desired; omit it for memory-only operation:
58
85
 
59
86
  ```ts
60
- const client = new GonvexClient(url, { queryCache: false });
61
-
62
- await client.clearQueryCache();
63
- await client.clearQueryCache({ allScopes: true });
87
+ const client = new GonvexClient(url, {
88
+ localReplica: { storage: indexedDbReplicaStorage },
89
+ });
64
90
  ```
65
91
 
66
- Dexie is loaded asynchronously only after a cache-capable session is confirmed,
67
- so IndexedDB setup does not delay the WebSocket query path.
92
+ All window updates are applied atomically before listeners are notified. Live
93
+ Query windows and Replica Collections reference the same normalized entities,
94
+ so every view of an entity converges together.
68
95
 
69
- ## Durable Sync Collections
96
+ ## Replica Collections
70
97
 
71
- Sync functions materialize bounded, authorized single-table collections in a
72
- normalized IndexedDB store and resume them from a durable Postgres cursor:
98
+ Replica Collections materialize bounded, authorized entity sets in normalized
99
+ IndexedDB storage and resume them from a durable Postgres revision:
73
100
 
74
101
  ```ts
75
- const watch = client.watchSync<Task>(
76
- { kind: "sync", path: "tasks.recent" },
102
+ const watch = client.watchReplica<Task>(
103
+ api.tasks.recent,
77
104
  { workspaceId: "workspace-a" },
78
105
  );
79
106
 
80
107
  const stop = watch.onUpdate(() => {
81
- render(watch.localSyncResult() ?? []);
108
+ const state = watch.localReplicaState();
109
+ render(state?.rows ?? []);
110
+ console.log(state?.completeness, state?.truncated, state?.computedRevision);
82
111
  console.log(watch.status()); // { isLoading, isUpToDate }
83
112
  });
84
113
  ```
85
114
 
86
- Configure or disable the sync store when constructing the client:
115
+ Configure Local Replica persistence when constructing the client:
87
116
 
88
117
  ```ts
89
118
  const client = new GonvexClient(url, {
90
- sync: {
91
- databaseName: "my-product-sync",
92
- maxBytes: 150 * 1024 * 1024,
93
- },
119
+ localReplica: { storage: indexedDbReplicaStorage },
94
120
  });
95
-
96
- const memoryOnly = new GonvexClient(url, { sync: false });
97
121
  ```
98
122
 
99
- The default global IndexedDB budget is 100 MiB. Server-declared per-collection
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.
123
+ ## Optimistic Reducers
103
124
 
104
- ## Entity-level optimistic mutations
125
+ Every public interactive Reducer declares its optimistic transaction. The
126
+ authoritative transaction and optimistic patches are reconciled in LocalReplica.
105
127
 
106
- Declare how a mutation maps its arguments to an entity row and how each live
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:
128
+ Generated references carry this metadata, so a normal Reducer call is enough:
124
129
 
125
130
  ```ts
126
- await client.mutation(api.tasks.update, {
131
+ await client.reducer(api.tasks.update, {
127
132
  taskId,
128
133
  updates: { priority_id: priorityId },
129
134
  });
130
135
  ```
131
136
 
132
- The client persists the pending mutation before exposing it, layers it over
133
- every matching sync and query subscription, and notifies watchers immediately.
134
- An RPC result marks the write accepted but does not remove it; the overlay is
135
- retired only when every source that displayed it reports the mutation id (or a
136
- restored committed snapshot already matches). Deterministic server errors
137
- remove the overlay and repaint the authoritative rows. This keeps stale query
138
- or sync frames from briefly reverting the UI.
137
+ The client persists the pending command, applies it through LocalReplica, and
138
+ notifies watchers immediately. Reducer success includes an
139
+ `originCommandId` and committed revision. The overlay is removed only after the
140
+ corresponding authoritative transaction has been applied locally, preventing an
141
+ empty or stale frame between optimistic and committed state.
139
142
 
140
- The authoritative sync/query cache stays immutable. Durable pending state lives
141
- in the mutation outbox and is re-applied to cached rows after reload. Outbox
143
+ Authoritative entity state and optimistic entities are materialized by one
144
+ LocalReplica graph. Durable pending state lives in the command outbox and is
145
+ re-applied after reload. Outbox
142
146
  rows are isolated by project, tenant, and authenticated identity; an account
143
147
  switch removes the previous identity's overlay and can never replay its writes
144
148
  under the new session. Unscoped rows from the pre-isolation schema are removed
145
149
  during migration because their owner cannot be proven. If an opaque credential
146
150
  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. For
148
- a complex projection that cannot be derived from one nested fields argument,
149
- callers can still provide explicit `optimistic` entity patches.
151
+ outbox is deliberately session-only rather than risking cross-user replay.
152
+
153
+ ## Offline Live Queries
154
+
155
+ Generated Live Query references include the same structured plan Gonvex
156
+ compiles to PostgreSQL. While offline, the client executes that plan over the
157
+ normalized cached corpus:
158
+
159
+ ```ts
160
+ const result = client.offlineLiveQuery(api.tasks.grid, args);
161
+ // { rows, completeness: "complete" | "partial", supported }
162
+ ```
163
+
164
+ `supported: false` means the plan is explicitly server-only. A partial result
165
+ must be labeled as cached data rather than presented as the full database.
150
166
 
151
167
  ## Lightweight Error Tracking
152
168
 
@@ -165,9 +181,23 @@ const client = new GonvexClient(url, {
165
181
  });
166
182
  ```
167
183
 
168
- Use `GonvexErrorReporter` directly when integrating an existing application
169
- logger. See the Error Tracking guide in the full documentation for privacy,
170
- grouping, persistence, and dashboard details.
184
+ Error registration and envelopes use native persistent-protocol frames. No
185
+ browser HTTP ingestion endpoint is needed.
186
+
187
+ Applications can report an explicit event through the public API:
188
+
189
+ ```ts
190
+ await client.reportError("envelope", {
191
+ events: [{
192
+ message: "Task preview failed",
193
+ level: "error",
194
+ context: { component: "TaskPreview" },
195
+ }],
196
+ });
197
+ ```
198
+
199
+ `new GonvexErrorReporter({ client })` adds global browser error capture and
200
+ re-registers its telemetry session after reconnect.
171
201
 
172
202
  ## Connection reliability
173
203
 
@@ -180,7 +210,7 @@ reconnect.
180
210
  client.connectionState();
181
211
  // {
182
212
  // isWebSocketConnected, hasEverConnected, connectionCount, connectionRetries,
183
- // hasInflightRequests, inflightMutations, inflightActions, inflightOneShotQueries
213
+ // hasInflightRequests, inflightReducers, inflightActions, inflightOneShotQueries
184
214
  // }
185
215
 
186
216
  const stop = client.subscribeToConnectionState((state) => {
@@ -193,7 +223,7 @@ const stop = client.subscribeToConnectionState((state) => {
193
223
  | Operation | Default |
194
224
  | --- | --- |
195
225
  | One-shot `query()` | 20s |
196
- | `mutation()` | 20s |
226
+ | `reducer()` | 20s |
197
227
  | `action()` | 60s |
198
228
 
199
229
  Override per client (`timeouts` option) or per call (`{ timeoutMs }`). Use `0` to disable.
@@ -202,43 +232,43 @@ Override per client (`timeouts` option) or per call (`{ timeoutMs }`). Use `0` t
202
232
 
203
233
  Rejected operations throw `GonvexClientError` with `code`:
204
234
 
205
- - `server` runtime executed the function and returned an error
206
- - `timeout` no response within the timeout
207
- - `disconnected` socket dropped while the operation was pending
208
- - `closed` client was explicitly closed
209
- - `auth` authentication rejected
235
+ - `server`: runtime executed the function and returned an error
236
+ - `timeout`: no response within the timeout
237
+ - `disconnected`: socket dropped while the operation was pending
238
+ - `closed`: client was explicitly closed
239
+ - `auth`: authentication rejected
210
240
 
211
- ### Mutation / action disconnect policy
241
+ ### Reducer / Action disconnect policy
212
242
 
213
- Actions and mutations without `{ offline: "queue" }` fail closed after a
243
+ Actions and Reducers without `{ offline: "queue" }` fail closed after a
214
244
  disconnect. They reject with `code: "disconnected"` (or `timeout` / `closed`).
215
- Optimistic mutations are persisted before transport even in fail-closed mode,
245
+ Optimistic Reducers are persisted before transport even in fail-closed mode,
216
246
  so a process reload cannot expose an older cached row while an accepted write
217
247
  is still waiting for its authoritative subscription update.
218
248
 
219
- Pass `{ offline: "queue" }` to a mutation to durably accept a transport failure
249
+ Pass `{ offline: "queue" }` to a Reducer to durably accept a transport failure
220
250
  and replay the same idempotency key after reconnect, whether or not that
221
- mutation also declares optimistic UI metadata. Actions are never queued.
251
+ Reducer also declares optimistic UI metadata. Actions are never queued.
222
252
  Deterministic server errors are never queued and always roll an optimistic
223
253
  entity overlay back when one exists.
224
254
 
225
- Live queries keep last-good data at the React layer (`useQueryResult`) and
226
- resubscribe after reconnect. Call `client.retryQuery(ref, args)` to force a
227
- re-request after a server error or soft timeout.
255
+ Live Queries persist their last verified window in the Local Replica and
256
+ resubscribe after reconnect. Call `client.retryLiveQuery(ref, args)` to force a
257
+ re-request after a server error. `useQueryResult` is for one-shot Queries.
228
258
 
229
259
  ## Exports
230
260
 
231
261
  The package exports:
232
262
 
233
263
  - `GonvexClient`
234
- - `ConvexReactClient` compatibility alias
235
264
  - `GonvexClientError`, `ConnectionState`, timeout defaults
236
- - transparent persistent query caching and lower-level experimental cache helpers
237
- - durable entity-level optimistic overlays for query and sync projections
238
- - opt-in mutation outbox replay with stable idempotency keys
239
- - `subscribeSync`, `watchSync`, and normalized persistent sync storage
265
+ - normalized Local Replica entities and Live Query windows
266
+ - durable optimistic Reducer overlays reconciled by command ID and revision
267
+ - opt-in command outbox replay with stable idempotency keys
268
+ - `subscribeReplica`, `watchReplica`, and persistent Replica storage
269
+ - `watchControlQuery` for authorized Control Plane live Queries
240
270
  - browser capability and telemetry helpers
241
- - `GonvexErrorReporter` and automatic operation error reporting
271
+ - `reportError`, `GonvexErrorReporter`, and automatic operation error reporting
242
272
 
243
273
  ## Related Packages
244
274