@rebasepro/client 0.22.0 → 0.23.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/dist/admin.d.ts +13 -8
- package/dist/auth.d.ts +24 -5
- package/dist/backups.d.ts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +718 -132
- package/dist/index.es.js.map +1 -1
- package/dist/offline-connectivity.d.ts +8 -0
- package/dist/offline-query.d.ts +9 -5
- package/dist/offline-store.d.ts +22 -0
- package/dist/offline.d.ts +30 -0
- package/dist/realtime-channel.d.ts +30 -0
- package/dist/transport.d.ts +17 -0
- package/dist/websocket.d.ts +34 -0
- package/package.json +4 -4
|
@@ -30,6 +30,14 @@ export declare function isNetworkError(error: unknown): boolean;
|
|
|
30
30
|
* server's own message says, and exactly what this SDK used not to do.
|
|
31
31
|
*/
|
|
32
32
|
export declare function isIdempotencyInProgressError(error: unknown): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Is this key held for a different request than the one just sent with it?
|
|
35
|
+
*
|
|
36
|
+
* For a create the queue already sent once, that is an answer rather than a
|
|
37
|
+
* failure: the key is live only because the first attempt got through and
|
|
38
|
+
* committed, and only its response went missing.
|
|
39
|
+
*/
|
|
40
|
+
export declare function isIdempotencyKeyReusedError(error: unknown): boolean;
|
|
33
41
|
/** Is this failure worth another attempt later? */
|
|
34
42
|
export declare function isRetryableError(error: unknown): boolean;
|
|
35
43
|
/**
|
package/dist/offline-query.d.ts
CHANGED
|
@@ -15,7 +15,11 @@ export { DEFAULT_LIST_LIMIT as DEFAULT_PAGE_SIZE } from "@rebasepro/types";
|
|
|
15
15
|
* which is how NULL propagates through a comparison.
|
|
16
16
|
*/
|
|
17
17
|
export declare function compareValues(a: unknown, b: unknown): number | undefined;
|
|
18
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* Equality with a string filter value allowed to name a number, a boolean or
|
|
20
|
+
* an instant, but never across NULL. Two strings are equal only when they are
|
|
21
|
+
* the same string, as text is in Postgres.
|
|
22
|
+
*/
|
|
19
23
|
export declare function looseEquals(a: unknown, b: unknown): boolean;
|
|
20
24
|
/** Evaluate one canonical operator against one row value. */
|
|
21
25
|
export declare function matchesOperator(rowValue: unknown, op: WhereFilterOp, filterValue: unknown): boolean;
|
|
@@ -72,14 +76,14 @@ export declare function isExactlyEvaluable(params?: FindParams): boolean;
|
|
|
72
76
|
*
|
|
73
77
|
* Asked of the rows rather than of the query, because unlike a filter this one
|
|
74
78
|
* *is* decidable from the data in hand: {@link compareValues} reaches the
|
|
75
|
-
* collator only when
|
|
76
|
-
*
|
|
79
|
+
* collator only when neither operand is a number, and `toComparable` has
|
|
80
|
+
* already turned dates and relations into numbers and ids by then. If every
|
|
77
81
|
* value on the sort column normalises to a number, the collator is unreachable
|
|
78
82
|
* and the local order is the server's order.
|
|
79
83
|
*
|
|
80
84
|
* A text column is therefore refused — see {@link isExactlyEvaluable} for why
|
|
81
|
-
* the two cannot be made to agree —
|
|
82
|
-
*
|
|
85
|
+
* the two cannot be made to agree — including one holding only digits, which
|
|
86
|
+
* Postgres orders as text ("10", "100", "9").
|
|
83
87
|
*
|
|
84
88
|
* Nulls are fine either way: they are ordered by an explicit rule (last
|
|
85
89
|
* ascending, first descending) that matches Postgres and never reaches the
|
package/dist/offline-store.d.ts
CHANGED
|
@@ -68,6 +68,28 @@ export interface PendingMutation {
|
|
|
68
68
|
/** The payload: a row for create/update, an array of rows for createMany. */
|
|
69
69
|
data?: Record<string, unknown> | Record<string, unknown>[];
|
|
70
70
|
upsert?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* The request as it was already sent once, for a write tried online
|
|
73
|
+
* that failed on the network before it was queued. Nothing says whether
|
|
74
|
+
* it reached the server — only the answer may have been lost — so replay
|
|
75
|
+
* keeps its key. For a create, sent with the rows as queued (their minted
|
|
76
|
+
* ids, which later offline writes may reference), the server either takes
|
|
77
|
+
* them, or, having committed the first attempt, refuses the key as held
|
|
78
|
+
* for a different request, and this request is sent again for its stored
|
|
79
|
+
* answer. Replaying under a fresh key wrote the rows a second time.
|
|
80
|
+
*
|
|
81
|
+
* An `update` or `delete` carries only the key: its queued request is the
|
|
82
|
+
* one it sent, since nothing is merged into an op that has `sent`.
|
|
83
|
+
*/
|
|
84
|
+
sent?: {
|
|
85
|
+
idempotencyKey: string;
|
|
86
|
+
/** `create`: the row it was called with; `createMany`: the rows. */
|
|
87
|
+
data?: Record<string, unknown> | Record<string, unknown>[];
|
|
88
|
+
/** `create`: the `id` argument it was called with. */
|
|
89
|
+
id?: string | number;
|
|
90
|
+
/** `createMany`: the conflict target the batch was sent with. */
|
|
91
|
+
onConflict?: readonly string[];
|
|
92
|
+
};
|
|
71
93
|
queuedAt: number;
|
|
72
94
|
/** How many times replay has been attempted (diagnostics for a stuck queue). */
|
|
73
95
|
attempts?: number;
|
package/dist/offline.d.ts
CHANGED
|
@@ -221,6 +221,19 @@ export declare class OfflineManager {
|
|
|
221
221
|
* query the server has never answered here.
|
|
222
222
|
*/
|
|
223
223
|
private answer;
|
|
224
|
+
/**
|
|
225
|
+
* Answer a projected query (`fields`, `distinct`) from the server's own
|
|
226
|
+
* answer to it.
|
|
227
|
+
*
|
|
228
|
+
* The server's rows are the answer. The local database contributes only
|
|
229
|
+
* what it knows better: a row with unsynced writes shows them, narrowed to
|
|
230
|
+
* the columns the server sent, or drops out if it was deleted here or
|
|
231
|
+
* edited out of a filter this side can evaluate; and rows created here
|
|
232
|
+
* join the first page when there is somewhere to put them. A distinct
|
|
233
|
+
* query, or an order on a column the projection does not carry, leaves
|
|
234
|
+
* them out, and the result says it is partial.
|
|
235
|
+
*/
|
|
236
|
+
private projectedAnswer;
|
|
224
237
|
private localFind;
|
|
225
238
|
/**
|
|
226
239
|
* A temporary id of the type this collection's ids actually have.
|
|
@@ -256,6 +269,12 @@ export declare class OfflineManager {
|
|
|
256
269
|
* Rows that came back unchanged keep their identity and revision, so a
|
|
257
270
|
* refetch that changed nothing does not re-render every live query that
|
|
258
271
|
* touches them — or rewrite them all to disk.
|
|
272
|
+
*
|
|
273
|
+
* A `projection` (see {@link isProjection}) only refreshes the columns it
|
|
274
|
+
* carries on rows already held, and never creates one. It is not a row:
|
|
275
|
+
* stored as one, it replaced the full row every other query reads and
|
|
276
|
+
* wrote the narrowed copy to disk, so a list lost its columns because a
|
|
277
|
+
* dropdown elsewhere asked for titles.
|
|
259
278
|
*/
|
|
260
279
|
private ingest;
|
|
261
280
|
/**
|
|
@@ -292,6 +311,17 @@ export declare class OfflineManager {
|
|
|
292
311
|
}>;
|
|
293
312
|
private flush;
|
|
294
313
|
private replay;
|
|
314
|
+
/**
|
|
315
|
+
* Send a queued write under its key.
|
|
316
|
+
*
|
|
317
|
+
* A write the queue sent once already, before it was queued, carries that
|
|
318
|
+
* attempt's key and is sent under it again, as queued. If the server holds
|
|
319
|
+
* the key for a different request, the difference is the ids minted here
|
|
320
|
+
* — so the first attempt committed and only its answer was lost, and
|
|
321
|
+
* sending that request again returns the stored answer instead of a
|
|
322
|
+
* second copy of the rows.
|
|
323
|
+
*/
|
|
324
|
+
private sendQueuedWrite;
|
|
295
325
|
/**
|
|
296
326
|
* Take the server's version of a row the client created offline.
|
|
297
327
|
*
|
|
@@ -133,6 +133,27 @@ export declare class RebaseRealtimeChannel {
|
|
|
133
133
|
*/
|
|
134
134
|
private pendingLive;
|
|
135
135
|
private catchUpInFlight;
|
|
136
|
+
/**
|
|
137
|
+
* Whether {@link lastSeq} is a position this client reached, rather than
|
|
138
|
+
* the zero of a handle that has not caught up yet.
|
|
139
|
+
*
|
|
140
|
+
* A client joining for the first time asks for whatever is retained, so
|
|
141
|
+
* history pruned before it ever joined is not something it missed. Only a
|
|
142
|
+
* client with a position can have a hole in what it saw.
|
|
143
|
+
*/
|
|
144
|
+
private positioned;
|
|
145
|
+
/** The page size the catch-up in flight asked for, reused for its later pages. */
|
|
146
|
+
private catchUpLimit;
|
|
147
|
+
/**
|
|
148
|
+
* `latestSeq` of an empty page that left this client behind, while a
|
|
149
|
+
* second request confirms it.
|
|
150
|
+
*
|
|
151
|
+
* The server reads the retained messages and then the cursor, so a message
|
|
152
|
+
* committed between the two shows up in `latestSeq` and not in the page.
|
|
153
|
+
* The second request returns it if it exists; only a second empty page
|
|
154
|
+
* means the messages up to this seq are gone.
|
|
155
|
+
*/
|
|
156
|
+
private emptyTailSeq;
|
|
136
157
|
/**
|
|
137
158
|
* Deadline for a catch-up response.
|
|
138
159
|
*
|
|
@@ -234,6 +255,12 @@ export declare class RebaseRealtimeChannel {
|
|
|
234
255
|
* retention cannot be served; `CHANNEL_BUS_PAYLOAD_TOO_LARGE` when a
|
|
235
256
|
* broadcast on an ephemeral channel is too big to cross the bus.
|
|
236
257
|
*
|
|
258
|
+
* `CHANNEL_HISTORY_GAP` is raised by the client: a catch-up found that the
|
|
259
|
+
* server no longer retains messages this client never received, so its
|
|
260
|
+
* state has a hole a replay cannot fill. `details` is `{ from, to }`, the
|
|
261
|
+
* inclusive range of sequence numbers missed. Resync the channel's state
|
|
262
|
+
* from its source of truth.
|
|
263
|
+
*
|
|
237
264
|
* These used to be dropped on the floor — there is no promise to reject on
|
|
238
265
|
* a fire-and-forget frame, so a forbidden broadcast looked exactly like a
|
|
239
266
|
* delivered one. With no handler attached they are logged as a warning,
|
|
@@ -271,6 +298,9 @@ export declare class RebaseRealtimeChannel {
|
|
|
271
298
|
private stopHeartbeat;
|
|
272
299
|
/** Fold an incoming frame into the roster and fan it out. */
|
|
273
300
|
private handle;
|
|
301
|
+
/** Tell the app that retention dropped messages it never received. */
|
|
302
|
+
private reportHistoryGap;
|
|
303
|
+
private emitError;
|
|
274
304
|
/** Deliver everything held back during a catch-up, in sequence order. */
|
|
275
305
|
private flushPendingLive;
|
|
276
306
|
private deliver;
|
package/dist/transport.d.ts
CHANGED
|
@@ -126,6 +126,23 @@ export declare const ANONYMOUS_SERVER_CLIENT_WARNING: string;
|
|
|
126
126
|
*/
|
|
127
127
|
export type FindParams<M extends Record<string, unknown> = Record<string, unknown>> = TypesFindParams<M>;
|
|
128
128
|
export type FindResponse<T> = TypesFindResponse<T extends Record<string, unknown> ? T : Record<string, unknown>>;
|
|
129
|
+
/**
|
|
130
|
+
* Refuse a filter whose *value* is missing.
|
|
131
|
+
*
|
|
132
|
+
* `where: { status: ["==", undefined] }` used to serialize to the literal
|
|
133
|
+
* string, so `status=eq.undefined` went out on the wire and the server dutifully
|
|
134
|
+
* looked for rows whose status is the four-letter word "undefined". The caller
|
|
135
|
+
* saw an empty page, not an error — the classic shape of a variable that was
|
|
136
|
+
* never set.
|
|
137
|
+
*
|
|
138
|
+
* Dropping the condition instead would be worse than sending it: the query
|
|
139
|
+
* would come back *unfiltered*, which for an ownership or tenant filter means
|
|
140
|
+
* returning rows the caller never asked to see. So this is a hard error, and
|
|
141
|
+
* both correct spellings are named in the message: omit the key to skip the
|
|
142
|
+
* filter, or use `["is-null", null]` to match SQL NULL (which still
|
|
143
|
+
* serializes — `null` is a value, `undefined` is the absence of one).
|
|
144
|
+
*/
|
|
145
|
+
export declare function assertNoUndefinedFilterValues(where: Record<string, unknown>): void;
|
|
129
146
|
export declare function buildQueryString(params?: FindParams): string;
|
|
130
147
|
/**
|
|
131
148
|
* The query string for `GET /<collection>/aggregate`.
|
package/dist/websocket.d.ts
CHANGED
|
@@ -50,6 +50,17 @@ export declare class RebaseWebSocketClient {
|
|
|
50
50
|
private pendingRequests;
|
|
51
51
|
private reconnectAttempts;
|
|
52
52
|
private maxReconnectAttempts;
|
|
53
|
+
/**
|
|
54
|
+
* Whether a socket of this client has ever opened — what makes the next
|
|
55
|
+
* open a *reconnect*, whose server has forgotten every subscription and
|
|
56
|
+
* channel membership and must be told them again.
|
|
57
|
+
*
|
|
58
|
+
* Not `reconnectAttempts > 0`: that counter is a backoff budget, and a
|
|
59
|
+
* fresh one (after giving up, or after a sign-out dropped the socket)
|
|
60
|
+
* starts at zero, so the socket that finally came back looked like a first
|
|
61
|
+
* connect and nothing was re-sent.
|
|
62
|
+
*/
|
|
63
|
+
private hadConnection;
|
|
53
64
|
private isConnected;
|
|
54
65
|
private messageQueue;
|
|
55
66
|
private requestTimeoutMs;
|
|
@@ -95,6 +106,21 @@ export declare class RebaseWebSocketClient {
|
|
|
95
106
|
disconnect(permanent?: boolean): void;
|
|
96
107
|
private initWebSocket;
|
|
97
108
|
private processMessageQueue;
|
|
109
|
+
/**
|
|
110
|
+
* Whether a frame that waited in the queue should go out on the socket
|
|
111
|
+
* that just opened. Only a subscribe can be refused, and one is exactly
|
|
112
|
+
* when sending it would give the server a subscription this side will not
|
|
113
|
+
* read:
|
|
114
|
+
*
|
|
115
|
+
* - Its registration is gone, or has been given a new id, while it waited.
|
|
116
|
+
* A listener that unmounted before the socket opened (a StrictMode double
|
|
117
|
+
* mount does exactly this) sends no `unsubscribe`, because there was no
|
|
118
|
+
* socket to send it on, so the server would keep that one forever.
|
|
119
|
+
* - This open is a reconnect. `resubscribeAll` sends every registration
|
|
120
|
+
* under a fresh id right after the queue drains, so the queued copy would
|
|
121
|
+
* be the second of two. It is handed back to that pass instead.
|
|
122
|
+
*/
|
|
123
|
+
private shouldSendQueued;
|
|
98
124
|
private attemptReconnect;
|
|
99
125
|
private isAuthError;
|
|
100
126
|
private handleAuthFailure;
|
|
@@ -191,6 +217,14 @@ export declare class RebaseWebSocketClient {
|
|
|
191
217
|
* firing mid-reconnect would tear down healthy subscriptions.
|
|
192
218
|
*/
|
|
193
219
|
private suspendSubscribeWatchdogs;
|
|
220
|
+
/**
|
|
221
|
+
* Drop every registration's cached rows, keeping the registrations.
|
|
222
|
+
*
|
|
223
|
+
* A registration without rows counts as not loaded, so the next listener
|
|
224
|
+
* to attach asks the server (or waits for the resubscribe) instead of
|
|
225
|
+
* being handed what is cached.
|
|
226
|
+
*/
|
|
227
|
+
private forgetCachedData;
|
|
194
228
|
/**
|
|
195
229
|
* Arm watchdogs for subscribes that were requested while offline and have
|
|
196
230
|
* just been flushed to the socket. Their timers were deliberately not set at
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "HTTP SDK client for the Rebase custom backend",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"rebase",
|
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
"./package.json": "./package.json"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@rebasepro/common": "0.
|
|
45
|
-
"@rebasepro/
|
|
46
|
-
"@rebasepro/
|
|
44
|
+
"@rebasepro/common": "0.23.0",
|
|
45
|
+
"@rebasepro/utils": "0.23.0",
|
|
46
|
+
"@rebasepro/types": "0.23.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@jest/globals": "^30.4.1",
|