@gonvex/client 0.5.2-staging.14 → 0.5.2-staging.15
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 +49 -0
- package/dist/client-upgrades.js +2 -1
- package/dist/client-upgrades.js.map +1 -1
- package/dist/index.d.ts +169 -9
- package/dist/index.js +493 -39
- package/dist/index.js.map +1 -1
- package/dist/outbox.d.ts +91 -9
- package/dist/outbox.js +278 -90
- package/dist/outbox.js.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -259,6 +259,55 @@ Reducer also declares optimistic UI metadata. Actions are never queued.
|
|
|
259
259
|
Deterministic server errors are never queued and always roll an optimistic
|
|
260
260
|
entity overlay back when one exists.
|
|
261
261
|
|
|
262
|
+
### Queued intent lifecycle
|
|
263
|
+
|
|
264
|
+
Runtimes from 0.5.2-staging.15 classify every `reducer.error` as `rejected`,
|
|
265
|
+
`transient`, `update_required` or `unauthenticated` (also exposed as
|
|
266
|
+
`GonvexClientError.errorClass` / `retryable`). The outbox acts on it:
|
|
267
|
+
|
|
268
|
+
| Class | Outbox behaviour |
|
|
269
|
+
| --- | --- |
|
|
270
|
+
| `transient` (and timeouts) | Retry with exponential backoff. After `outbox.retry.maxAttempts` (default 10, backoff capped at `maxBackoffMs`, default 60s) the intent is parked as `failed`: kept durably with its prediction, and no longer blocking later intents. |
|
|
271
|
+
| `unauthenticated` | Keep the intent, re-authenticate, retry. Does not spend the retry budget. |
|
|
272
|
+
| `update_required` | Keep the intent pending and call `onUpdateRequired`. |
|
|
273
|
+
| `rejected` | Roll the prediction back, rebase later local intents, fire `onReducerRejection`, and keep the intent as a `rejected` record until the app discards or retries it. |
|
|
274
|
+
|
|
275
|
+
Older runtimes send no class: their errors are treated as rejections, except
|
|
276
|
+
the legacy stale-artifact and "authenticate with an active tenant" messages.
|
|
277
|
+
|
|
278
|
+
```ts
|
|
279
|
+
const intents = await client.listIntents(); // or subscribeIntents()/intentsSnapshot()
|
|
280
|
+
client.entityIntentStatus("tasks", taskId); // "syncing" | "failed" | "rejected" | undefined
|
|
281
|
+
await client.retryIntent(intent.id); // same idempotency key, fresh budget
|
|
282
|
+
await client.discardIntent(intent.id); // pending/failed/rejected only
|
|
283
|
+
await client.listOutboxScopes(); // includes identities that never returned
|
|
284
|
+
await client.purgeForeignOutboxScopes(); // never automatic
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`@gonvex/react` exposes the same list through `useOutboxIntents()` and a
|
|
288
|
+
per-row `useEntityIntentStatus(entity, id)`.
|
|
289
|
+
|
|
290
|
+
### Resetting the local cache
|
|
291
|
+
|
|
292
|
+
`client.resetLocalReplica({ keepOutbox = true })` implements a "Clear cache"
|
|
293
|
+
setting without the app touching replica storage. It deletes the active
|
|
294
|
+
identity's persisted replica rows (IndexedDB, Expo SQLite, or the configured
|
|
295
|
+
storage), keeps the saved offline session, closes and re-opens every active
|
|
296
|
+
Replica Collection and Live Query without cursors, and re-applies the
|
|
297
|
+
predictions of all live intents so they reappear on top of the fresh
|
|
298
|
+
snapshots. Pending, failed and rejected intents are kept unless
|
|
299
|
+
`keepOutbox: false` is passed explicitly (inflight intents are never dropped).
|
|
300
|
+
|
|
301
|
+
It is **online only**: offline (or before the socket is authenticated) it
|
|
302
|
+
rejects with `GonvexClientError` `code: "disconnected"` and changes nothing,
|
|
303
|
+
so the user is never left with an empty cache that cannot be refilled. React
|
|
304
|
+
apps can use `useResetLocalReplica()` for `{ reset, isResetting, error }`.
|
|
305
|
+
|
|
306
|
+
A parked intent lets later intents proceed; the server validates each of them
|
|
307
|
+
on its own, so an intent that depended on the parked one is rejected rather
|
|
308
|
+
than applied out of order. Retrying a parked intent after later intents
|
|
309
|
+
committed re-applies it on top of them.
|
|
310
|
+
|
|
262
311
|
Live Queries persist their last verified window in the Local Replica and
|
|
263
312
|
resubscribe after reconnect. Call `client.retryLiveQuery(ref, args)` to force a
|
|
264
313
|
re-request after a server error. `useQueryResult` is for one-shot Queries.
|
package/dist/client-upgrades.js
CHANGED
|
@@ -32,7 +32,8 @@ export function migrateClientData(snapshots, entries, chain) {
|
|
|
32
32
|
return { ...entry, path: intent.path, args: intent.args,
|
|
33
33
|
receiptPath: originalPath, patches: [],
|
|
34
34
|
// Committed responses awaiting a watermark must be reconciled by their original receipt too.
|
|
35
|
-
|
|
35
|
+
// Parked and rejected records wait for the user; an upgrade must not resend them.
|
|
36
|
+
state: entry.state === "failed" || entry.state === "rejected" ? entry.state : "pending", nextAttemptAt: 0 };
|
|
36
37
|
});
|
|
37
38
|
}
|
|
38
39
|
return { snapshots: nextSnapshots, entries: nextEntries };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client-upgrades.js","sourceRoot":"","sources":["../src/client-upgrades.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,EAAU,EAAE,UAAsC;IAC7F,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,IAAI,EAAE,CAAC;QACtF,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,OAAO,EAAE,EAAE,CAAC,CAAC;IAC7E,CAAC;IACD,MAAM,KAAK,GAAsB,EAAE,CAAC;IACpC,KAAK,IAAI,OAAO,GAAG,IAAI,EAAE,OAAO,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC;QACjD,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;QAC9D,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,IAAI,UAAU,CAAC,CAAC,CAAE,CAAC,EAAE,KAAK,OAAO,GAAG,CAAC,EAAE,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,yCAAyC,OAAO,OAAO,OAAO,GAAG,CAAC,EAAE,CAAC,CAAC;QACxF,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAE,CAAC,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,SAA0C,EAAE,OAAsC,EAClF,KAAiC;IAEjC,IAAI,aAAa,GAAG,eAAe,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,WAAW,GAAG,eAAe,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAChD,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,QAAQ,CAAC,EAAE,EAAE;YACzF,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;YACxE,8FAA8F;YAC9F,OAAO,IAAI,CAAC,MAAM,CAAC;YACnB,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QACvB,CAAC,CAAC,CAAC,CAAC;QACJ,WAAW,GAAG,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YACpC,MAAM,YAAY,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC;YACrD,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,eAAe,CAAC,KAAK,CAAC,IAAI,CAAc,EAAE,CAAC;mBAClG,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAiB,EAAE,CAAC;YACzD,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;YAC9G,OAAO,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI;gBACrD,WAAW,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE;gBACtC,6FAA6F;gBAC7F,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"client-upgrades.js","sourceRoot":"","sources":["../src/client-upgrades.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { BrowserTelemetryInfo, ExecutionScope, JsonValue, MessageTrace, ServerCapabilities, ServerMessage } from "@gonvex/protocol";
|
|
1
|
+
import type { BrowserTelemetryInfo, ExecutionScope, JsonValue, MessageTrace, ServerCapabilities, ServerMessage, ReducerErrorClass } from "@gonvex/protocol";
|
|
2
2
|
import type { LocalRuntimeBinding } from "@gonvex/local-runtime/worker-client";
|
|
3
3
|
import { type ErrorReporterOptions } from "./error-reporter.js";
|
|
4
4
|
export { GonvexErrorReporter } from "./error-reporter.js";
|
|
5
5
|
export type { ErrorReporterOptions, ErrorEventPayload, ErrorContext, ErrorAccount } from "./error-reporter.js";
|
|
6
6
|
import { type OptimisticPatch, type OptimisticTransactionDefinition } from "./optimistic.js";
|
|
7
|
-
import { type OutboxStore } from "./outbox.js";
|
|
7
|
+
import { type OutboxErrorClass, type ReducerOutboxScopeSummary, type ReducerOutboxState, type OutboxStore } from "./outbox.js";
|
|
8
8
|
import { type LocalReplicaStorage, type LocalReplicaView, type ReplicaRow, type LiveQueryResult, type ReplicaCollectionState, type ReplicaCollectionSubscriptionState, type ReplicaCollectionPlan } from "./local-replica.js";
|
|
9
9
|
import { type LiveQueryPlan, type OfflineLiveQueryResult } from "./query-expression.js";
|
|
10
10
|
export * from "./error-reporter.js";
|
|
@@ -95,12 +95,86 @@ export declare class GonvexClientError extends Error {
|
|
|
95
95
|
readonly code: GonvexClientErrorCode;
|
|
96
96
|
readonly path?: string;
|
|
97
97
|
readonly operation?: "query" | "reducer" | "action";
|
|
98
|
+
/**
|
|
99
|
+
* For `server` Reducer errors: the runtime's classification, or a legacy
|
|
100
|
+
* inference when an older runtime sent none. Undefined means a legacy
|
|
101
|
+
* runtime's unclassified error, which is handled as a rejection.
|
|
102
|
+
*/
|
|
103
|
+
readonly errorClass?: ReducerErrorClass;
|
|
104
|
+
/** True when the same call (same idempotency key) may succeed later. */
|
|
105
|
+
readonly retryable?: boolean;
|
|
98
106
|
constructor(message: string, options: {
|
|
99
107
|
code: GonvexClientErrorCode;
|
|
100
108
|
path?: string;
|
|
101
109
|
operation?: "query" | "reducer" | "action";
|
|
110
|
+
errorClass?: ReducerErrorClass;
|
|
111
|
+
retryable?: boolean;
|
|
102
112
|
});
|
|
103
113
|
}
|
|
114
|
+
/** One durable reducer intent, as exposed to application UI. */
|
|
115
|
+
export type OutboxIntent = {
|
|
116
|
+
/** The reducer id / idempotency key. Stable across retries, reloads and tabs. */
|
|
117
|
+
id: string;
|
|
118
|
+
/** Local queue sequence number. Lower numbers were issued first. */
|
|
119
|
+
entryId: number;
|
|
120
|
+
reducer: string;
|
|
121
|
+
state: ReducerOutboxState;
|
|
122
|
+
/** Failed deliveries counted toward the retry budget. */
|
|
123
|
+
attempts: number;
|
|
124
|
+
lastError?: string;
|
|
125
|
+
errorClass?: OutboxErrorClass;
|
|
126
|
+
createdAt: number;
|
|
127
|
+
nextAttemptAt: number;
|
|
128
|
+
/** When the intent became `failed` or `rejected`. */
|
|
129
|
+
settledAt?: number;
|
|
130
|
+
args: unknown;
|
|
131
|
+
/** Short, display-safe JSON preview of the arguments. */
|
|
132
|
+
argsSummary: string;
|
|
133
|
+
/** Rows this intent's optimistic prediction touched (best effort). */
|
|
134
|
+
entities: Array<{
|
|
135
|
+
entity: string;
|
|
136
|
+
id: string;
|
|
137
|
+
}>;
|
|
138
|
+
};
|
|
139
|
+
/** Per-row delivery status derived from the outbox. */
|
|
140
|
+
export type EntityIntentStatus = "syncing" | "failed" | "rejected";
|
|
141
|
+
export type ReducerRejectionEvent = {
|
|
142
|
+
reducerId: string;
|
|
143
|
+
path: string;
|
|
144
|
+
error: string;
|
|
145
|
+
errorClass?: OutboxErrorClass;
|
|
146
|
+
};
|
|
147
|
+
export type OutboxScope = ReducerOutboxScopeSummary & {
|
|
148
|
+
/** True for the identity this client is currently signed in as. */
|
|
149
|
+
current: boolean;
|
|
150
|
+
};
|
|
151
|
+
export type OutboxRetryOptions = {
|
|
152
|
+
/**
|
|
153
|
+
* Counted delivery failures (transient server errors and timeouts) before an
|
|
154
|
+
* intent is parked as `failed`. Default 10. `Infinity` retries forever.
|
|
155
|
+
*/
|
|
156
|
+
maxAttempts?: number;
|
|
157
|
+
/** Cap for the exponential backoff between attempts. Default 60s. */
|
|
158
|
+
maxBackoffMs?: number;
|
|
159
|
+
};
|
|
160
|
+
export type ResetLocalReplicaOptions = {
|
|
161
|
+
/**
|
|
162
|
+
* Keep the durable outbox (default true). Pending, failed and rejected
|
|
163
|
+
* intents survive the reset and their predictions are re-applied on top of
|
|
164
|
+
* the rehydrated data. `false` also deletes every intent of the active
|
|
165
|
+
* identity that is not already inflight or committed; use it only for an
|
|
166
|
+
* explicit "discard unsynced changes" action.
|
|
167
|
+
*/
|
|
168
|
+
keepOutbox?: boolean;
|
|
169
|
+
};
|
|
170
|
+
export type ResetLocalReplicaResult = {
|
|
171
|
+
/** Intents deleted because `keepOutbox: false` was requested. */
|
|
172
|
+
discardedIntents: number;
|
|
173
|
+
/** Replica Collections and Live Queries re-requested from the server. */
|
|
174
|
+
resubscribed: number;
|
|
175
|
+
};
|
|
176
|
+
export declare const DEFAULT_OUTBOX_MAX_ATTEMPTS = 10;
|
|
177
|
+
export declare const DEFAULT_OUTBOX_RETRY_MAX_BACKOFF_MS = 60000;
|
|
104
178
|
export type ConnectionState = {
|
|
105
179
|
isWebSocketConnected: boolean;
|
|
106
180
|
hasEverConnected: boolean;
|
|
@@ -191,6 +265,7 @@ export type GonvexClientOptions = GonvexClientAuth & {
|
|
|
191
265
|
databaseName?: string;
|
|
192
266
|
enabled?: boolean;
|
|
193
267
|
store?: OutboxStore;
|
|
268
|
+
retry?: OutboxRetryOptions;
|
|
194
269
|
};
|
|
195
270
|
/** Transactional normalized store used by Replica Collections and Live Queries. */
|
|
196
271
|
localReplica?: {
|
|
@@ -231,6 +306,12 @@ export declare class GonvexClient {
|
|
|
231
306
|
private readonly localCollectionKeys;
|
|
232
307
|
private readonly localExecutionTables;
|
|
233
308
|
private readonly reducerRejectionHandlers;
|
|
309
|
+
private readonly outboxRetry;
|
|
310
|
+
private unauthenticatedRetries;
|
|
311
|
+
private intentsSnapshotValue;
|
|
312
|
+
private readonly intentListeners;
|
|
313
|
+
private intentsRefreshRunning;
|
|
314
|
+
private intentsRefreshDirty;
|
|
234
315
|
private socket;
|
|
235
316
|
private readonly handlers;
|
|
236
317
|
private readonly querySubscriptions;
|
|
@@ -283,6 +364,8 @@ export declare class GonvexClient {
|
|
|
283
364
|
private peerRefreshDirty;
|
|
284
365
|
private readonly unsubscribeBrowserOnline;
|
|
285
366
|
private drainingOutbox;
|
|
367
|
+
/** A wake-up (timer, enqueue, reconnect) that arrived while a drain was running. */
|
|
368
|
+
private outboxDrainRequested;
|
|
286
369
|
private outboxDrainTimer;
|
|
287
370
|
private readonly sessionScopeHandlers;
|
|
288
371
|
private readonly errorReporter;
|
|
@@ -304,12 +387,68 @@ export declare class GonvexClient {
|
|
|
304
387
|
private updateRequired;
|
|
305
388
|
private lastOnlineAtMs;
|
|
306
389
|
constructor(url: string, options?: GonvexClientOptions);
|
|
307
|
-
/**
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
390
|
+
/**
|
|
391
|
+
* Authoritative sync failures arrive after an offline call returned locally.
|
|
392
|
+
* The intent also stays in the outbox as `rejected` until it is discarded
|
|
393
|
+
* or retried, so a UI that mounts later can still show it.
|
|
394
|
+
*/
|
|
395
|
+
onReducerRejection(listener: (event: ReducerRejectionEvent) => void): () => void;
|
|
396
|
+
/** Every durable intent of the current identity, oldest first. */
|
|
397
|
+
listIntents(): Promise<OutboxIntent[]>;
|
|
398
|
+
/**
|
|
399
|
+
* Synchronous snapshot for external stores (React `useSyncExternalStore`).
|
|
400
|
+
* Kept current while at least one {@link subscribeIntents} listener exists.
|
|
401
|
+
*/
|
|
402
|
+
intentsSnapshot(): readonly OutboxIntent[];
|
|
403
|
+
/** Observe intent changes; call {@link intentsSnapshot} for the new value. */
|
|
404
|
+
subscribeIntents(listener: () => void): () => void;
|
|
405
|
+
/**
|
|
406
|
+
* Delivery status for one row from the current snapshot: `failed` or
|
|
407
|
+
* `rejected` when an intent touching it needs attention, `syncing` while one
|
|
408
|
+
* is still queued, otherwise undefined.
|
|
409
|
+
*/
|
|
410
|
+
entityIntentStatus(entity: string, id: string): EntityIntentStatus | undefined;
|
|
411
|
+
/**
|
|
412
|
+
* Re-arm a `failed` or `rejected` intent with a fresh retry budget. The
|
|
413
|
+
* original idempotency key is reused, so an attempt that had actually
|
|
414
|
+
* committed is replayed by the server rather than applied twice.
|
|
415
|
+
*/
|
|
416
|
+
retryIntent(id: string): Promise<boolean>;
|
|
417
|
+
/**
|
|
418
|
+
* Drop a `pending`, `failed` or `rejected` intent: it will never be sent,
|
|
419
|
+
* its optimistic prediction is removed and later local intents are rebased.
|
|
420
|
+
* Inflight and committed intents cannot be discarded. Discarding an intent
|
|
421
|
+
* whose earlier attempt timed out does not undo a commit that the server
|
|
422
|
+
* may already have applied; the replica then shows the server's truth.
|
|
423
|
+
*/
|
|
424
|
+
discardIntent(id: string): Promise<boolean>;
|
|
425
|
+
/**
|
|
426
|
+
* Discard the persisted Local Replica of the active identity (IndexedDB,
|
|
427
|
+
* Expo SQLite or any configured storage) and rehydrate it from the server.
|
|
428
|
+
*
|
|
429
|
+
* Online only: without an authenticated connection the call rejects with a
|
|
430
|
+
* `GonvexClientError` (`code: "disconnected"`) and changes nothing, so the
|
|
431
|
+
* user is never left with an empty cache that cannot be refilled. The saved
|
|
432
|
+
* offline session (identity and replica directive) is kept.
|
|
433
|
+
*
|
|
434
|
+
* By default the outbox is untouched: pending, failed and rejected intents
|
|
435
|
+
* stay durable, and the predictions of every live intent are re-applied
|
|
436
|
+
* immediately and recomputed as server data arrives. Active Replica
|
|
437
|
+
* Collections and Live Queries are re-requested without their old cursors,
|
|
438
|
+
* so the server sends full snapshots.
|
|
439
|
+
*/
|
|
440
|
+
resetLocalReplica(options?: ResetLocalReplicaOptions): Promise<ResetLocalReplicaResult>;
|
|
441
|
+
/** Outbox owners with durable entries, including identities that never returned. */
|
|
442
|
+
listOutboxScopes(): Promise<OutboxScope[]>;
|
|
443
|
+
/**
|
|
444
|
+
* Permanently delete another identity's durable intents. The active scope
|
|
445
|
+
* cannot be purged this way; use {@link discardIntent} for its entries.
|
|
446
|
+
*/
|
|
447
|
+
purgeOutboxScope(scope: string): Promise<number>;
|
|
448
|
+
/** Purge every outbox scope except the current identity's. Never runs automatically. */
|
|
449
|
+
purgeForeignOutboxScopes(): Promise<number>;
|
|
450
|
+
private refreshIntents;
|
|
451
|
+
private publishIntents;
|
|
313
452
|
private restoreLocalSession;
|
|
314
453
|
private ensureLocalCollections;
|
|
315
454
|
private hydrateMissingLocalTable;
|
|
@@ -344,7 +483,7 @@ export declare class GonvexClient {
|
|
|
344
483
|
offlineLiveQuery<T extends ReplicaRow = ReplicaRow>(ref: FunctionReference, args?: JsonValue, options?: {
|
|
345
484
|
window?: boolean;
|
|
346
485
|
}): OfflineLiveQueryResult<T>;
|
|
347
|
-
/** Number of reducers
|
|
486
|
+
/** Number of reducers still queued for delivery (excludes failed and rejected intents). */
|
|
348
487
|
outboxCount(): Promise<number>;
|
|
349
488
|
connectionState(): ConnectionState;
|
|
350
489
|
/** Metadata advertised by the runtime in its latest session.ready frame. */
|
|
@@ -443,6 +582,25 @@ export declare class GonvexClient {
|
|
|
443
582
|
private rejectOptimisticReducer;
|
|
444
583
|
private ackOptimisticReducer;
|
|
445
584
|
private drainOutbox;
|
|
585
|
+
/**
|
|
586
|
+
* Record a non-rejection delivery failure and arm the next attempt.
|
|
587
|
+
* - update_required: keep the intent pending and stop for an app update.
|
|
588
|
+
* - unauthenticated: keep the intent, re-authenticate, retry with backoff
|
|
589
|
+
* that never spends the retry budget.
|
|
590
|
+
* - network: connectivity loss; retried on reconnect without spending budget.
|
|
591
|
+
* - transient (and timeouts): exponential backoff, parked as `failed` once
|
|
592
|
+
* the retry budget is spent.
|
|
593
|
+
*/
|
|
594
|
+
private recordDeliveryFailure;
|
|
595
|
+
/**
|
|
596
|
+
* Arm a timer for the earliest backed-off entry. Timer clocks and
|
|
597
|
+
* `Date.now()` can disagree by a millisecond, so a wake-up may find its
|
|
598
|
+
* entry not quite due; without this the queue would wait for an unrelated
|
|
599
|
+
* event to resume.
|
|
600
|
+
*/
|
|
601
|
+
private scheduleNextOutboxAttempt;
|
|
602
|
+
/** Re-send auth on the open socket so a lost tenant session is restored. */
|
|
603
|
+
private requestReauthentication;
|
|
446
604
|
private scheduleOutboxDrain;
|
|
447
605
|
reducer<T extends JsonValue = JsonValue, Args extends JsonValue = JsonValue>(ref: FunctionReference<Args, T>, args: Args, options: CallOptions & {
|
|
448
606
|
offline: "queue";
|
|
@@ -521,3 +679,5 @@ export declare class GonvexClient {
|
|
|
521
679
|
private sendNow;
|
|
522
680
|
private flushPendingMessages;
|
|
523
681
|
}
|
|
682
|
+
/** Row status from a list of intents: failed > rejected > syncing. */
|
|
683
|
+
export declare function entityStatusFromIntents(intents: readonly OutboxIntent[], entity: string, id: string): EntityIntentStatus | undefined;
|