@rebasepro/client 0.23.0 → 0.24.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 +7 -5
- package/dist/api-keys.d.ts +29 -11
- package/dist/auth.d.ts +41 -9
- package/dist/backups.d.ts +2 -6
- package/dist/collection.d.ts +4 -4
- package/dist/cron.d.ts +2 -4
- package/dist/index.d.ts +36 -11
- package/dist/index.es.js +1097 -202
- package/dist/index.es.js.map +1 -1
- package/dist/offline-connectivity.d.ts +6 -0
- package/dist/offline-store.d.ts +8 -0
- package/dist/offline.d.ts +87 -3
- package/dist/query-contract.types.d.ts +23 -23
- package/dist/realtime-channel.d.ts +5 -0
- package/dist/sdk_query_builder.d.ts +2 -2
- package/dist/sql-script.d.ts +10 -0
- package/dist/transport.d.ts +15 -2
- package/dist/websocket.d.ts +110 -17
- package/package.json +4 -4
|
@@ -90,6 +90,12 @@ export declare class ConnectivityMonitor {
|
|
|
90
90
|
private readonly clearTimer;
|
|
91
91
|
/** Called when the backoff window expires, to drive an automatic retry. */
|
|
92
92
|
onRetryDue?: () => void;
|
|
93
|
+
/**
|
|
94
|
+
* Called when the browser says the connection is back. Its own hook rather
|
|
95
|
+
* than {@link onRetryDue}: a client with automatic retries off sets no retry
|
|
96
|
+
* callback, and the `online` event has to wake it all the same.
|
|
97
|
+
*/
|
|
98
|
+
onOnline?: () => void;
|
|
93
99
|
private readonly handleOnline;
|
|
94
100
|
private readonly handleOffline;
|
|
95
101
|
constructor(options?: ConnectivityOptions);
|
package/dist/offline-store.d.ts
CHANGED
|
@@ -67,6 +67,14 @@ export interface PendingMutation {
|
|
|
67
67
|
generatedId?: boolean;
|
|
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
|
+
/**
|
|
71
|
+
* For an `update` that consecutive edits were coalesced into: each edit,
|
|
72
|
+
* in the order the app made them. `data` is their merge, and is what is
|
|
73
|
+
* sent. Kept so a refusal of the merge over one field can be told apart
|
|
74
|
+
* from a refusal of the rest: the merge is split back into these and each
|
|
75
|
+
* replayed on its own, so only the edit the server refuses is undone.
|
|
76
|
+
*/
|
|
77
|
+
parts?: Record<string, unknown>[];
|
|
70
78
|
upsert?: boolean;
|
|
71
79
|
/**
|
|
72
80
|
* The request as it was already sent once, for a write tried online
|
package/dist/offline.d.ts
CHANGED
|
@@ -96,6 +96,24 @@ export interface OfflineStatus {
|
|
|
96
96
|
lastError?: string;
|
|
97
97
|
}
|
|
98
98
|
export type { LiveResult, ObserveOptions, RowSnapshotMeta } from "./collection.js";
|
|
99
|
+
/** Which queue {@link OfflineApi.pending} and {@link OfflineApi.clear} act on. */
|
|
100
|
+
export interface OfflineQueueOptions {
|
|
101
|
+
/**
|
|
102
|
+
* The writes queued while nobody was signed in, instead of the current
|
|
103
|
+
* user's.
|
|
104
|
+
*
|
|
105
|
+
* While someone is signed in these are held back: they were not made by
|
|
106
|
+
* that user, so they are never replayed under their credentials, and never
|
|
107
|
+
* moved into their queue. `pending({ orphaned: true })` lists them so the
|
|
108
|
+
* app can decide — re-issue them as the signed-in user, or discard them
|
|
109
|
+
* with `clear({ orphaned: true })`. Left alone, they replay the next time
|
|
110
|
+
* nobody is signed in.
|
|
111
|
+
*
|
|
112
|
+
* With nobody signed in they are the current queue, and are not orphaned:
|
|
113
|
+
* `pending({ orphaned: true })` lists nothing.
|
|
114
|
+
*/
|
|
115
|
+
orphaned?: boolean;
|
|
116
|
+
}
|
|
99
117
|
/** What `client.offline` exposes to the app. */
|
|
100
118
|
export interface OfflineApi {
|
|
101
119
|
/** Replay the queue now. Resolves with what was flushed and what remains. */
|
|
@@ -103,8 +121,12 @@ export interface OfflineApi {
|
|
|
103
121
|
flushed: number;
|
|
104
122
|
remaining: number;
|
|
105
123
|
}>;
|
|
106
|
-
/**
|
|
107
|
-
|
|
124
|
+
/**
|
|
125
|
+
* The queued mutations for the current user, oldest first — or, with
|
|
126
|
+
* `{ orphaned: true }`, the ones queued while nobody was signed in (see
|
|
127
|
+
* {@link OfflineQueueOptions.orphaned}).
|
|
128
|
+
*/
|
|
129
|
+
pending(options?: OfflineQueueOptions): Promise<PendingMutation[]>;
|
|
108
130
|
/** The current engine state — connectivity, queue depth, last sync. */
|
|
109
131
|
status(): OfflineStatus;
|
|
110
132
|
/** Subscribe to {@link OfflineStatus} changes (for a sync indicator). */
|
|
@@ -114,8 +136,12 @@ export interface OfflineApi {
|
|
|
114
136
|
* Destructive: queued writes are lost, not replayed. For "discard my
|
|
115
137
|
* offline changes" flows, not for sign-out (scoping already isolates
|
|
116
138
|
* users).
|
|
139
|
+
*
|
|
140
|
+
* With `{ orphaned: true }`, drop the writes queued while nobody was
|
|
141
|
+
* signed in, and the local rows they were applied to, instead — the
|
|
142
|
+
* current user's are left alone.
|
|
117
143
|
*/
|
|
118
|
-
clear(): Promise<void>;
|
|
144
|
+
clear(options?: OfflineQueueOptions): Promise<void>;
|
|
119
145
|
/** Subscribe to queue-size changes (for a "pending changes" badge). */
|
|
120
146
|
onQueueChange(listener: (count: number) => void): () => void;
|
|
121
147
|
}
|
|
@@ -140,6 +166,26 @@ export declare class OfflineManager {
|
|
|
140
166
|
private readonly inners;
|
|
141
167
|
private readonly connectivity;
|
|
142
168
|
private scope;
|
|
169
|
+
/**
|
|
170
|
+
* Set while the manager does not know whose data it holds: from
|
|
171
|
+
* {@link holdScope} until the next {@link setScope}. `scope` is not
|
|
172
|
+
* consulted meanwhile — no operation gets a {@link ScopeTicket}, and
|
|
173
|
+
* nothing reads or writes the store.
|
|
174
|
+
*/
|
|
175
|
+
private scopeHold?;
|
|
176
|
+
/**
|
|
177
|
+
* Bumped by every change of user, and by `clear()`.
|
|
178
|
+
*
|
|
179
|
+
* The load paths always re-checked the scope after their awaits; the write
|
|
180
|
+
* paths did not. A `find` sent for user A that answered after B had signed
|
|
181
|
+
* in wrote A's RLS-filtered rows into B's local database, under B's keys,
|
|
182
|
+
* and showed them to B's observers; a replay acknowledged after the switch
|
|
183
|
+
* dequeued a key under B's scope that did not exist, so A's write stayed
|
|
184
|
+
* queued and was sent again when A came back. Every write now carries the
|
|
185
|
+
* {@link ScopeTicket} its operation started under, and one that is stale
|
|
186
|
+
* touches nothing that belongs to the new user.
|
|
187
|
+
*/
|
|
188
|
+
private scopeEpoch;
|
|
143
189
|
/** The local database: normalized rows and query snapshots per collection. */
|
|
144
190
|
private collections;
|
|
145
191
|
/** In-memory mirror of the current scope's queue, in replay order. */
|
|
@@ -185,6 +231,35 @@ export declare class OfflineManager {
|
|
|
185
231
|
* across a sign-out/sign-in on a shared browser.
|
|
186
232
|
*/
|
|
187
233
|
setScope(uid: string | undefined): void;
|
|
234
|
+
/**
|
|
235
|
+
* Forget whose data this is until the next {@link setScope}.
|
|
236
|
+
*
|
|
237
|
+
* For the time before the client knows who is signed in — a session being
|
|
238
|
+
* restored on load. Taking that time for "signed out" would put the user's
|
|
239
|
+
* own writes in the signed-out queue, which no sign-in replays, and show
|
|
240
|
+
* them the signed-out local database instead of theirs.
|
|
241
|
+
*
|
|
242
|
+
* Meanwhile the manager has no scope at all. Every operation — a read, a
|
|
243
|
+
* write, a replay, `pending()`, `clear()` — waits for one and then runs
|
|
244
|
+
* for whoever it turns out to be, and nothing reads or writes the store.
|
|
245
|
+
* A realtime frame arriving in between is handed on as the server sent it
|
|
246
|
+
* and kept nowhere.
|
|
247
|
+
*/
|
|
248
|
+
holdScope(): void;
|
|
249
|
+
/** Let go of the current user's rows and queue: in memory, and on screen. */
|
|
250
|
+
private leaveScope;
|
|
251
|
+
/** Settles once the manager knows whose data it holds — see {@link holdScope}. */
|
|
252
|
+
private scopeKnown;
|
|
253
|
+
/**
|
|
254
|
+
* The user an operation is running for — see {@link scopeEpoch} — or
|
|
255
|
+
* nothing while that is not known ({@link holdScope}). An operation takes
|
|
256
|
+
* it as `this.ticket() ?? await this.nextTicket()`, before its first await.
|
|
257
|
+
*/
|
|
258
|
+
private ticket;
|
|
259
|
+
/** The ticket of whoever the held scope turns out to be. */
|
|
260
|
+
private nextTicket;
|
|
261
|
+
/** Whether an operation's user is still the signed-in one. */
|
|
262
|
+
private isCurrent;
|
|
188
263
|
/**
|
|
189
264
|
* Throw away every local row, for a scope change or an explicit clear.
|
|
190
265
|
*
|
|
@@ -353,6 +428,15 @@ export declare class OfflineManager {
|
|
|
353
428
|
* silently lose writes the server would have accepted.
|
|
354
429
|
*/
|
|
355
430
|
private rejectMutation;
|
|
431
|
+
/**
|
|
432
|
+
* Put the separate edits a coalesced `update` was made of back in the
|
|
433
|
+
* queue, in its place and in their order, each with the row as it stood
|
|
434
|
+
* before it as its rollback — so a refusal undoes that edit alone.
|
|
435
|
+
*
|
|
436
|
+
* Their ids extend the merged op's, which sorts them after it and before
|
|
437
|
+
* whatever was queued next, so the queue's order survives a reload.
|
|
438
|
+
*/
|
|
439
|
+
private splitCoalesced;
|
|
356
440
|
/** Every row id a mutation writes to. */
|
|
357
441
|
private idsOf;
|
|
358
442
|
private drop;
|
|
@@ -67,11 +67,11 @@ export declare const orderByColumn: FindParams<ContractRow>;
|
|
|
67
67
|
* turning every typo into an unsorted 200 in production.
|
|
68
68
|
*/
|
|
69
69
|
export declare const orderByTypo: FindParams<ContractRow>;
|
|
70
|
-
export declare const fluentScore: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
71
|
-
export declare const fluentColumn: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
72
|
-
export declare const fluentTypo: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
70
|
+
export declare const fluentScore: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
71
|
+
export declare const fluentColumn: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
72
|
+
export declare const fluentTypo: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
73
73
|
/** Vector search must be reachable from the builder, and chain. */
|
|
74
|
-
export declare const fluentVector: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
74
|
+
export declare const fluentVector: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
75
75
|
/**
|
|
76
76
|
* `array-contains` takes an **element** of the column, not the column.
|
|
77
77
|
*
|
|
@@ -82,38 +82,38 @@ export declare const fluentVector: (qb: SDKQueryBuilderInterface<ContractRow>) =
|
|
|
82
82
|
* did compile, `["featured"]`, builds `@> ARRAY[$1]` with the whole array bound
|
|
83
83
|
* as the single element and matches nothing, forever, with no error anywhere.
|
|
84
84
|
*/
|
|
85
|
-
export declare const fluentArrayContains: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
86
|
-
export declare const fluentArrayContainsWrapped: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
85
|
+
export declare const fluentArrayContains: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
86
|
+
export declare const fluentArrayContainsWrapped: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
87
87
|
/**
|
|
88
88
|
* Same defect, and the case the relation compiler was specifically built for:
|
|
89
89
|
* a to-many relation is emitted as `Array<TargetRow>`, and the compiler answers
|
|
90
90
|
* `array-contains` on it by comparing **ids**. So the id must be accepted even
|
|
91
91
|
* though it is not the element type.
|
|
92
92
|
*/
|
|
93
|
-
export declare const fluentRelationContains: (qb: SDKQueryBuilderInterface<PostRow>, tagId: string) => SDKQueryBuilderInterface<PostRow>;
|
|
94
|
-
export declare const fluentRelationIn: (qb: SDKQueryBuilderInterface<PostRow>, tagIds: string[]) => SDKQueryBuilderInterface<PostRow>;
|
|
95
|
-
export declare const fluentRelationTypo: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow>;
|
|
93
|
+
export declare const fluentRelationContains: (qb: SDKQueryBuilderInterface<PostRow>, tagId: string) => SDKQueryBuilderInterface<PostRow, import("@rebasepro/types").IncludeSpec>;
|
|
94
|
+
export declare const fluentRelationIn: (qb: SDKQueryBuilderInterface<PostRow>, tagIds: string[]) => SDKQueryBuilderInterface<PostRow, import("@rebasepro/types").IncludeSpec>;
|
|
95
|
+
export declare const fluentRelationTypo: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow, import("@rebasepro/types").IncludeSpec>;
|
|
96
96
|
/** The list operators take a list of elements — or one, read as a one-element list. */
|
|
97
|
-
export declare const fluentInList: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
98
|
-
export declare const fluentInScalar: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
99
|
-
export declare const fluentInNested: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
97
|
+
export declare const fluentInList: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
98
|
+
export declare const fluentInScalar: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
99
|
+
export declare const fluentInNested: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
100
100
|
/** A comparison takes one value. `eq(column, ["a","b"])` is not a query anyone meant. */
|
|
101
|
-
export declare const fluentEqArray: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
101
|
+
export declare const fluentEqArray: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
102
102
|
/**
|
|
103
103
|
* A pattern is a string on every column type. The driver casts, so refusing
|
|
104
104
|
* `"%3%"` on a numeric column was the type being stricter than the runtime.
|
|
105
105
|
*/
|
|
106
|
-
export declare const fluentLikeOnNumber: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
106
|
+
export declare const fluentLikeOnNumber: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
107
107
|
/** The null operators ignore their value; `null` is the conventional spelling. */
|
|
108
|
-
export declare const fluentIsNull: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
108
|
+
export declare const fluentIsNull: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
109
109
|
/**
|
|
110
110
|
* A caller holding an unnarrowed operator — a dynamic filter UI — must keep
|
|
111
111
|
* compiling. `WhereValueFor` distributes over the operator, so this is the
|
|
112
112
|
* union of every branch rather than a `never`.
|
|
113
113
|
*/
|
|
114
|
-
export declare const fluentDynamicOp: (qb: SDKQueryBuilderInterface<ContractRow>, op: WhereFilterOp) => SDKQueryBuilderInterface<ContractRow>;
|
|
114
|
+
export declare const fluentDynamicOp: (qb: SDKQueryBuilderInterface<ContractRow>, op: WhereFilterOp) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
115
115
|
/** Two conditions on one column: the shape the Mongo compiler used to drop. */
|
|
116
|
-
export declare const fluentRange: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
116
|
+
export declare const fluentRange: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
117
117
|
/** The object form must accept exactly what the fluent form accepts. */
|
|
118
118
|
export declare const paramsArrayContains: FindParams<ContractRow>;
|
|
119
119
|
/** …and the array-of-tuples form the builder produces from two `.where()` calls. */
|
|
@@ -150,11 +150,11 @@ export declare const rowStaysIndexable: (result: FindResult<ContractRow>) => Rec
|
|
|
150
150
|
* which is to say: on every project that took the trouble to be typed.
|
|
151
151
|
*/
|
|
152
152
|
/** 1. A relation path. `find({ where: { "author.name": … } })` always compiled. */
|
|
153
|
-
export declare const fluentRelationPath: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow>;
|
|
153
|
+
export declare const fluentRelationPath: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow, import("@rebasepro/types").IncludeSpec>;
|
|
154
154
|
/** 2. A JSON path into a `jsonb` column — `?metadata->>tier=eq.gold` on the wire. */
|
|
155
|
-
export declare const fluentJsonPath: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
155
|
+
export declare const fluentJsonPath: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
156
156
|
/** 3. An aggregate over a to-many relation, which `FindParams.orderBy` takes. */
|
|
157
|
-
export declare const fluentRelationAggregateSort: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow>;
|
|
157
|
+
export declare const fluentRelationAggregateSort: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow, import("@rebasepro/types").IncludeSpec>;
|
|
158
158
|
/** 4. Paging past the `limit` ceiling, which the client can do and the builder could not. */
|
|
159
159
|
export declare const fluentIterate: (qb: SDKQueryBuilderInterface<ContractRow>) => AsyncIterableIterator<ContractRow>;
|
|
160
160
|
export declare const fluentFindAll: (qb: SDKQueryBuilderInterface<ContractRow>) => Promise<ContractRow[]>;
|
|
@@ -164,9 +164,9 @@ export declare const fluentFindAll: (qb: SDKQueryBuilderInterface<ContractRow>)
|
|
|
164
164
|
* `unknown` — turning every one of the assertions above into a silent pass.
|
|
165
165
|
* These are the same refusals as before.
|
|
166
166
|
*/
|
|
167
|
-
export declare const fluentPathTypo: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
168
|
-
export declare const fluentPathDoesNotLoosenValues: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow>;
|
|
169
|
-
export declare const fluentAggregateSortTypo: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow>;
|
|
167
|
+
export declare const fluentPathTypo: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
168
|
+
export declare const fluentPathDoesNotLoosenValues: (qb: SDKQueryBuilderInterface<ContractRow>) => SDKQueryBuilderInterface<ContractRow, import("@rebasepro/types").IncludeSpec>;
|
|
169
|
+
export declare const fluentAggregateSortTypo: (qb: SDKQueryBuilderInterface<PostRow>) => SDKQueryBuilderInterface<PostRow, import("@rebasepro/types").IncludeSpec>;
|
|
170
170
|
/** And the object form still takes all four, unchanged. */
|
|
171
171
|
export declare const paramsRelationPath: FindParams<PostRow>;
|
|
172
172
|
export declare const paramsJsonPath: FindParams<ContractRow>;
|
|
@@ -98,6 +98,11 @@ export interface ChannelTransport {
|
|
|
98
98
|
sendMessage(message: Record<string, unknown>): Promise<unknown>;
|
|
99
99
|
onChannelMessage(channel: string, handler: (message: ChannelMessage) => void): () => void;
|
|
100
100
|
onReconnect(handler: () => void): () => void;
|
|
101
|
+
/**
|
|
102
|
+
* Notified once per outage that outlasted a blip. Optional so a test
|
|
103
|
+
* transport need not implement it.
|
|
104
|
+
*/
|
|
105
|
+
onConnectionLost?(handler: (error: RebaseApiError) => void): () => void;
|
|
101
106
|
}
|
|
102
107
|
export declare class RebaseRealtimeChannel {
|
|
103
108
|
readonly name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FindResult, LogicalCondition, PageWalkOptions, RelationAggregateSort, SDKCollectionClient, SDKQueryBuilderInterface, WhereFilterOp, WhereValueFor, type AggregateParams, type
|
|
1
|
+
import { FindResult, LogicalCondition, PageWalkOptions, RelationAggregateSort, SDKCollectionClient, SDKQueryBuilderInterface, WhereFilterOp, WhereValueFor, type AggregateParams, type AggregateResult, type ComputedSortField, type FieldPath, type IncludeSpec, type NonColumnFieldPath, type NullsPlacement } from "@rebasepro/types";
|
|
2
2
|
/**
|
|
3
3
|
* SDK Query Builder — returns flat rows (`FindResult<M>`) instead of
|
|
4
4
|
* Entity-wrapped results (`FindResponse<M>`).
|
|
@@ -173,7 +173,7 @@ export declare class SDKQueryBuilder<M extends Record<string, unknown> = Record<
|
|
|
173
173
|
* // [{ status: "paid", sum_total: 41822.5 }, …]
|
|
174
174
|
* ```
|
|
175
175
|
*/
|
|
176
|
-
aggregate(params: Omit<AggregateParams<M>, "where" | "logical" | "searchString">): Promise<
|
|
176
|
+
aggregate(params: Omit<AggregateParams<M>, "where" | "logical" | "searchString">): Promise<AggregateResult>;
|
|
177
177
|
/**
|
|
178
178
|
* Page through everything this query matches, one row at a time.
|
|
179
179
|
*
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { SqlScriptResult } from "@rebasepro/types";
|
|
2
|
+
/**
|
|
3
|
+
* Read an `EXECUTE_SQL_SUCCESS` payload for a script run as a
|
|
4
|
+
* {@link SqlScriptResult}, checking every field rather than trusting the frame.
|
|
5
|
+
*
|
|
6
|
+
* A server from before scripts existed answers `{ result }` alone, with parsed
|
|
7
|
+
* values. Its rows are kept, as text, and no column is said to come from
|
|
8
|
+
* anywhere — the console edits no cell of a result it cannot trace.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readSqlScriptResult(payload: unknown): SqlScriptResult;
|
package/dist/transport.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AggregateParams, FindParams as TypesFindParams, FindResponse as TypesFindResponse } from "@rebasepro/types";
|
|
1
|
+
import { AggregateParams, FindParams as TypesFindParams, FindResponse as TypesFindResponse, type IncludeSpec } from "@rebasepro/types";
|
|
2
2
|
export { RebaseApiError } from "@rebasepro/types";
|
|
3
3
|
export type { RebaseErrorInit } from "@rebasepro/types";
|
|
4
4
|
export interface RebaseClientConfig {
|
|
@@ -95,6 +95,19 @@ export interface RebaseClientConfig {
|
|
|
95
95
|
* is not settable here; it always comes from the token.
|
|
96
96
|
*/
|
|
97
97
|
headers?: Record<string, string>;
|
|
98
|
+
/**
|
|
99
|
+
* Run every request as this user, named by uid: sent as
|
|
100
|
+
* `x-rebase-impersonate` on each HTTP request, and with the realtime
|
|
101
|
+
* socket's sign-in.
|
|
102
|
+
*
|
|
103
|
+
* For checking what one user can see and do against the real row-level
|
|
104
|
+
* security, as Studio's API explorer and JS editor do. Only an
|
|
105
|
+
* administrator's own session may — the server refuses it for anyone
|
|
106
|
+
* else, and always for an API key or the service key. The data API,
|
|
107
|
+
* custom functions and realtime run as the named user; every other route
|
|
108
|
+
* refuses the request rather than answer as the administrator.
|
|
109
|
+
*/
|
|
110
|
+
impersonate?: string;
|
|
98
111
|
}
|
|
99
112
|
/**
|
|
100
113
|
* Facts about the surrounding client that the transport cannot read off its own
|
|
@@ -124,7 +137,7 @@ export declare const ANONYMOUS_SERVER_CLIENT_WARNING: string;
|
|
|
124
137
|
* `orderBy` went back to accepting any column name — the alias, not the
|
|
125
138
|
* definition, was where the typing was lost.
|
|
126
139
|
*/
|
|
127
|
-
export type FindParams<M extends Record<string, unknown> = Record<string, unknown
|
|
140
|
+
export type FindParams<M extends Record<string, unknown> = Record<string, unknown>, Inc = IncludeSpec> = TypesFindParams<M, Inc>;
|
|
128
141
|
export type FindResponse<T> = TypesFindResponse<T extends Record<string, unknown> ? T : Record<string, unknown>>;
|
|
129
142
|
/**
|
|
130
143
|
* Refuse a filter whose *value* is missing.
|
package/dist/websocket.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { DeleteProps, CollectionConfig, FetchCollectionProps, ListenCollectionProps, FetchOneProps, SaveProps, CollectionUpdateMeta, ChannelMessage, TableMetadata, BranchInfo } from "@rebasepro/types";
|
|
1
|
+
import { DeleteProps, CollectionConfig, FetchCollectionProps, ListenCollectionProps, FetchOneProps, SaveProps, CollectionUpdateMeta, ChannelMessage, TableMetadata, BranchInfo, RebaseApiError, type RealtimeConnectionState } from "@rebasepro/types";
|
|
2
|
+
import type { SqlScriptResult } from "@rebasepro/types";
|
|
2
3
|
export interface RebaseWebSocketConfig {
|
|
3
4
|
websocketUrl: string;
|
|
4
5
|
/** Optional auth token getter for WebSocket authentication */
|
|
@@ -7,6 +8,11 @@ export interface RebaseWebSocketConfig {
|
|
|
7
8
|
WebSocket?: typeof WebSocket;
|
|
8
9
|
/** Callback to handle unauthorized requests or token expiration (refreshes auth session) */
|
|
9
10
|
onUnauthorized?: () => Promise<boolean>;
|
|
11
|
+
/**
|
|
12
|
+
* Sign in as this user rather than as the token's own — the socket half of
|
|
13
|
+
* `RebaseClientConfig.impersonate`. Sent with every `AUTHENTICATE`.
|
|
14
|
+
*/
|
|
15
|
+
impersonate?: string;
|
|
10
16
|
}
|
|
11
17
|
export declare class RebaseWebSocketClient {
|
|
12
18
|
private websocketUrl;
|
|
@@ -18,16 +24,41 @@ export declare class RebaseWebSocketClient {
|
|
|
18
24
|
/** Set by `close()`. Blocks any later operation from silently redialling. */
|
|
19
25
|
private closedByCaller;
|
|
20
26
|
/**
|
|
21
|
-
* Set when the backoff budget ran out, cleared by
|
|
22
|
-
* fresh one.
|
|
27
|
+
* Set when the backoff budget ran out with nothing registered, cleared by
|
|
28
|
+
* anything that earns a fresh one.
|
|
23
29
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* stopped for the rest of the page's life, with a reload the only cure.
|
|
30
|
+
* Only reachable with nothing registered: while a subscription or a joined
|
|
31
|
+
* channel exists the client never stops retrying (see `attemptReconnect`).
|
|
32
|
+
* And it is not final like {@link closedByCaller} — nobody *asked* for the
|
|
33
|
+
* socket to stay down, so the next subscribe dials again.
|
|
29
34
|
*/
|
|
30
35
|
private gaveUp;
|
|
36
|
+
/** See {@link state}. */
|
|
37
|
+
private connectionState;
|
|
38
|
+
private stateListeners;
|
|
39
|
+
/**
|
|
40
|
+
* Whether the current outage has been reported to the registrations as
|
|
41
|
+
* `CONNECTION_LOST`. Once per outage: cleared when a socket opens.
|
|
42
|
+
*/
|
|
43
|
+
private outageReported;
|
|
44
|
+
/**
|
|
45
|
+
* Where the connection is.
|
|
46
|
+
*
|
|
47
|
+
* - `idle` — no socket, and none wanted yet (the connection is lazy), or
|
|
48
|
+
* the last one was dropped by a sign-out.
|
|
49
|
+
* - `connecting` — dialling, with no outage in progress.
|
|
50
|
+
* - `connected` — the socket is open.
|
|
51
|
+
* - `reconnecting` — the socket dropped and the client is redialling. A
|
|
52
|
+
* blip, so far: nothing has been reported.
|
|
53
|
+
* - `disconnected` — the outage has outlasted the blip budget. Every
|
|
54
|
+
* subscription and joined channel has been told `CONNECTION_LOST` once;
|
|
55
|
+
* the client keeps redialling (every 30 s at most) while any exists.
|
|
56
|
+
* - `closed` — `client.close()`. Final.
|
|
57
|
+
*/
|
|
58
|
+
get state(): RealtimeConnectionState;
|
|
59
|
+
/** Called on every change of {@link state}. Returns the unsubscribe. */
|
|
60
|
+
onStateChange(listener: (state: RealtimeConnectionState) => void): () => void;
|
|
61
|
+
private setState;
|
|
31
62
|
/**
|
|
32
63
|
* Whether a socket exists at all (open or still opening).
|
|
33
64
|
*
|
|
@@ -41,6 +72,13 @@ export declare class RebaseWebSocketClient {
|
|
|
41
72
|
onChannelMessage(channel: string, handler: (message: ChannelMessage) => void): () => void;
|
|
42
73
|
/** Notified after the socket comes back, so channels can re-join. */
|
|
43
74
|
onReconnect(handler: () => void): () => void;
|
|
75
|
+
/**
|
|
76
|
+
* Notified once per outage that outlasted a blip, with the
|
|
77
|
+
* `CONNECTION_LOST` error — so a joined channel can tell its `onError`
|
|
78
|
+
* handlers, as subscriptions are told through theirs.
|
|
79
|
+
*/
|
|
80
|
+
onConnectionLost(handler: (error: RebaseApiError) => void): () => void;
|
|
81
|
+
private connectionLostHandlers;
|
|
44
82
|
on(event: "connect" | "disconnect" | "reconnect" | "error", cb: (...args: unknown[]) => void): () => boolean;
|
|
45
83
|
private emit;
|
|
46
84
|
private collectionSubscriptions;
|
|
@@ -49,7 +87,18 @@ export declare class RebaseWebSocketClient {
|
|
|
49
87
|
private backendToEntityKey;
|
|
50
88
|
private pendingRequests;
|
|
51
89
|
private reconnectAttempts;
|
|
90
|
+
/**
|
|
91
|
+
* With nothing registered, how many failed redials before the client stops
|
|
92
|
+
* trying until something asks for the connection. With a registration it
|
|
93
|
+
* never stops.
|
|
94
|
+
*/
|
|
52
95
|
private maxReconnectAttempts;
|
|
96
|
+
/**
|
|
97
|
+
* How many failed redials make a blip an outage: the point at which every
|
|
98
|
+
* registration is told `CONNECTION_LOST` and {@link state} reads
|
|
99
|
+
* `disconnected`. Three is 2 + 4 + 8 s of backoff, about 14 s down.
|
|
100
|
+
*/
|
|
101
|
+
private lostAfterAttempts;
|
|
53
102
|
/**
|
|
54
103
|
* Whether a socket of this client has ever opened — what makes the next
|
|
55
104
|
* open a *reconnect*, whose server has forgotten every subscription and
|
|
@@ -71,6 +120,7 @@ export declare class RebaseWebSocketClient {
|
|
|
71
120
|
private WebSocketConstructor;
|
|
72
121
|
onUnauthorized?: () => Promise<boolean>;
|
|
73
122
|
private refreshInProgress;
|
|
123
|
+
private readonly impersonate?;
|
|
74
124
|
constructor(config: RebaseWebSocketConfig);
|
|
75
125
|
/**
|
|
76
126
|
* Open the socket if it is not open (or opening) already.
|
|
@@ -81,12 +131,20 @@ export declare class RebaseWebSocketClient {
|
|
|
81
131
|
*/
|
|
82
132
|
ensureConnected(): void;
|
|
83
133
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
134
|
+
* Dial now rather than at the next backoff step — the browser says the
|
|
135
|
+
* network is back, or the tab is in front of the user again. Both are the
|
|
136
|
+
* usual end of an outage; neither fires for a server restart, which the
|
|
137
|
+
* backoff covers.
|
|
138
|
+
*/
|
|
139
|
+
private retryNow;
|
|
140
|
+
/**
|
|
141
|
+
* `online` and `visibilitychange`. Registered lazily so a Node client, or a
|
|
142
|
+
* page that never subscribes, adds no listener.
|
|
87
143
|
*/
|
|
88
|
-
private
|
|
144
|
+
private installNetworkListeners;
|
|
145
|
+
private removeNetworkListeners;
|
|
89
146
|
private onlineListener;
|
|
147
|
+
private visibilityListener;
|
|
90
148
|
/**
|
|
91
149
|
* Authenticate the WebSocket connection
|
|
92
150
|
*/
|
|
@@ -122,6 +180,27 @@ export declare class RebaseWebSocketClient {
|
|
|
122
180
|
*/
|
|
123
181
|
private shouldSendQueued;
|
|
124
182
|
private attemptReconnect;
|
|
183
|
+
/**
|
|
184
|
+
* Whether anything depends on the socket coming back: a subscription, a
|
|
185
|
+
* joined channel, or a request whose caller is still waiting for a socket.
|
|
186
|
+
*/
|
|
187
|
+
private hasRegistrations;
|
|
188
|
+
/**
|
|
189
|
+
* Tell every subscription and joined channel, once per outage, that the
|
|
190
|
+
* connection is down and their data is not updating.
|
|
191
|
+
*
|
|
192
|
+
* Registrations are kept — that is the difference from a subscribe that
|
|
193
|
+
* failed. When the socket is back they are re-subscribed, and the fresh
|
|
194
|
+
* `onUpdate` is the recovery.
|
|
195
|
+
*/
|
|
196
|
+
private reportConnectionLost;
|
|
197
|
+
private notifyError;
|
|
198
|
+
/**
|
|
199
|
+
* A listener attaching while an outage is being reported is told at once:
|
|
200
|
+
* its subscribe waits for a socket that may be minutes away, and nothing
|
|
201
|
+
* else would say so.
|
|
202
|
+
*/
|
|
203
|
+
private reportOutageTo;
|
|
125
204
|
private isAuthError;
|
|
126
205
|
private handleAuthFailure;
|
|
127
206
|
/**
|
|
@@ -138,6 +217,15 @@ export declare class RebaseWebSocketClient {
|
|
|
138
217
|
* Not part of the stable surface — prefer `client.realtime.channel(name)`.
|
|
139
218
|
*/
|
|
140
219
|
sendMessage(message: Record<string, unknown>): Promise<unknown>;
|
|
220
|
+
/**
|
|
221
|
+
* Frames whose caller has already been answered — timed out, or rejected
|
|
222
|
+
* by a close. One can still be on its way to the socket (awaiting
|
|
223
|
+
* authentication), and must not be written after its caller was told it
|
|
224
|
+
* failed.
|
|
225
|
+
*/
|
|
226
|
+
private abandonedFrames;
|
|
227
|
+
/** Whether the server answers this frame with a response envelope. */
|
|
228
|
+
private expectsResponse;
|
|
141
229
|
private doSendMessage;
|
|
142
230
|
fetchCollection<M extends Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
|
|
143
231
|
fetchOne<M extends Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
|
|
@@ -147,6 +235,16 @@ export declare class RebaseWebSocketClient {
|
|
|
147
235
|
database?: string;
|
|
148
236
|
role?: string;
|
|
149
237
|
}): Promise<Record<string, unknown>[]>;
|
|
238
|
+
/**
|
|
239
|
+
* Run a script a person wrote — the Studio console — and get back what its
|
|
240
|
+
* last statement returned, every value as the database's text, with the
|
|
241
|
+
* table column each result column was read from. See
|
|
242
|
+
* `SQLAdmin.runSqlScript`.
|
|
243
|
+
*/
|
|
244
|
+
runSqlScript(sql: string, options?: {
|
|
245
|
+
database?: string;
|
|
246
|
+
role?: string;
|
|
247
|
+
}): Promise<SqlScriptResult>;
|
|
150
248
|
fetchAvailableDatabases(): Promise<string[]>;
|
|
151
249
|
fetchAvailableRoles(): Promise<string[]>;
|
|
152
250
|
fetchApplicationRoles(): Promise<string[]>;
|
|
@@ -233,11 +331,6 @@ export declare class RebaseWebSocketClient {
|
|
|
233
331
|
private armPendingSubscribeWatchdogs;
|
|
234
332
|
private sendCollectionSubscribeWatchdog;
|
|
235
333
|
private sendEntitySubscribeWatchdog;
|
|
236
|
-
/**
|
|
237
|
-
* Fail every subscription that never received data. Called when reconnection
|
|
238
|
-
* is given up on, so views surface an error instead of spinning forever.
|
|
239
|
-
*/
|
|
240
|
-
private failAllPendingSubscriptions;
|
|
241
334
|
/**
|
|
242
335
|
* Re-send all active subscriptions to the backend after a reconnect.
|
|
243
336
|
* The server wipes subscription state when a client disconnects, so
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.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/utils": "0.
|
|
46
|
-
"@rebasepro/types": "0.
|
|
44
|
+
"@rebasepro/common": "0.24.0",
|
|
45
|
+
"@rebasepro/utils": "0.24.0",
|
|
46
|
+
"@rebasepro/types": "0.24.0"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@jest/globals": "^30.4.1",
|