@rebasepro/client 0.9.0 → 0.9.1-canary.0de22e0
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 +1 -1
- package/dist/admin.d.ts +1 -0
- package/dist/auth.d.ts +5 -0
- package/dist/backups.d.ts +13 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.es.js +828 -96
- package/dist/index.es.js.map +1 -1
- package/dist/realtime-channel.d.ts +243 -0
- package/dist/transport.d.ts +34 -0
- package/dist/websocket.d.ts +97 -3
- package/package.json +11 -10
- package/src/admin.ts +1 -1
- package/src/api-keys.ts +1 -1
- package/src/auth.ts +32 -0
- package/src/backups.ts +40 -0
- package/src/collection.ts +16 -0
- package/src/index.ts +112 -3
- package/src/realtime-channel.test.ts +542 -0
- package/src/realtime-channel.ts +539 -0
- package/src/realtime-optout.test.ts +245 -0
- package/src/realtime-row-identity.test.ts +254 -0
- package/src/sdk_query_builder.ts +4 -1
- package/src/transport-baseurl.test.ts +53 -0
- package/src/transport.ts +59 -3
- package/src/websocket.ts +516 -89
- package/dist/collection.test.d.ts +0 -1
- package/dist/cron.test.d.ts +0 -1
- package/dist/data-proxy.test.d.ts +0 -1
- package/dist/index.umd.js +0 -2484
- package/dist/index.umd.js.map +0 -1
package/src/index.ts
CHANGED
|
@@ -3,12 +3,14 @@ import { RebaseClientError } from "./errors";
|
|
|
3
3
|
import { createAuth, CreateAuthOptions } from "./auth";
|
|
4
4
|
import { createAdmin, CreateAdminOptions } from "./admin";
|
|
5
5
|
import { createCron, CreateCronOptions } from "./cron";
|
|
6
|
+
import { createBackups } from "./backups";
|
|
6
7
|
import { createApiKeys, CreateApiKeysOptions } from "./api-keys";
|
|
7
8
|
import { CollectionClient, createCollectionClient } from "./collection";
|
|
8
9
|
import { createFunctionsClient } from "./functions";
|
|
9
10
|
import { createStorage } from "./storage";
|
|
10
11
|
import { ClientStorageSourceRegistry } from "./storage-registry";
|
|
11
12
|
import { RebaseWebSocketClient } from "./websocket";
|
|
13
|
+
import { RebaseRealtimeChannel, type ChannelOptions } from "./realtime-channel";
|
|
12
14
|
import {
|
|
13
15
|
DEFAULT_STORAGE_SOURCE_KEY,
|
|
14
16
|
InsertOf,
|
|
@@ -59,6 +61,8 @@ export type { RebaseUser, RebaseTokens } from "./auth";
|
|
|
59
61
|
export type { CreateAdminOptions } from "./admin";
|
|
60
62
|
export type { AdminUser } from "./admin";
|
|
61
63
|
export type { CreateCronOptions } from "./cron";
|
|
64
|
+
export { createBackups } from "./backups";
|
|
65
|
+
export type { CreateBackupsOptions } from "./backups";
|
|
62
66
|
export type {
|
|
63
67
|
ApiKeyMasked,
|
|
64
68
|
ApiKeyPermission,
|
|
@@ -70,9 +74,19 @@ export type {
|
|
|
70
74
|
export type { FunctionInvokeOptions, FunctionsClient } from "./functions";
|
|
71
75
|
|
|
72
76
|
// Realtime: the WebSocket client class is internal to `createRebaseClient()`,
|
|
73
|
-
// but re-exported (see @internal on the class) because the `client-
|
|
77
|
+
// but re-exported (see @internal on the class) because the `client-postgres`
|
|
74
78
|
// driver constructs it directly. Not a stable app-facing API.
|
|
75
79
|
export { RebaseWebSocketClient } from "./websocket";
|
|
80
|
+
export { RebaseRealtimeChannel } from "./realtime-channel";
|
|
81
|
+
export type {
|
|
82
|
+
PresenceState,
|
|
83
|
+
PresenceDiff,
|
|
84
|
+
BroadcastEvent,
|
|
85
|
+
ChannelTransport,
|
|
86
|
+
ChannelOptions,
|
|
87
|
+
ChannelHistoryEntry,
|
|
88
|
+
ChannelHistoryResult
|
|
89
|
+
} from "./realtime-channel";
|
|
76
90
|
|
|
77
91
|
export interface CreateRebaseClientOptions extends RebaseClientConfig {
|
|
78
92
|
auth?: CreateAuthOptions;
|
|
@@ -135,14 +149,42 @@ export type CreateRebaseClientResult<DB = Record<string, unknown>> = Omit<Rebase
|
|
|
135
149
|
auth: ReturnType<typeof createAuth>;
|
|
136
150
|
admin: ReturnType<typeof createAdmin>;
|
|
137
151
|
cron: ReturnType<typeof createCron>;
|
|
152
|
+
backups: ReturnType<typeof createBackups>;
|
|
138
153
|
apiKeys: ReturnType<typeof createApiKeys>;
|
|
139
154
|
functions: ReturnType<typeof createFunctionsClient>;
|
|
140
155
|
ws?: RebaseWebSocketClient;
|
|
156
|
+
/**
|
|
157
|
+
* Broadcast and presence channels.
|
|
158
|
+
*
|
|
159
|
+
* Was missing from this type while present on the returned object, which
|
|
160
|
+
* made `client.realtime.channel(...)` a type error and forced every adopter
|
|
161
|
+
* to cast around the feature before they could reach it.
|
|
162
|
+
*/
|
|
163
|
+
realtime: {
|
|
164
|
+
/**
|
|
165
|
+
* Join a broadcast/presence channel. Repeated calls with the same name
|
|
166
|
+
* return the same channel object. Throws only when the client was
|
|
167
|
+
* created with `realtime: false`.
|
|
168
|
+
*
|
|
169
|
+
* Pass `{ history: true }` to have the channel replay what it missed on
|
|
170
|
+
* join and on every reconnect, for channels the server retains.
|
|
171
|
+
*/
|
|
172
|
+
channel: (name: string, options?: ChannelOptions) => RebaseRealtimeChannel;
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* Release the realtime socket and its reconnect timer.
|
|
176
|
+
*
|
|
177
|
+
* An open socket keeps the Node event loop alive, so a script that does not
|
|
178
|
+
* call this will not exit on its own. Safe when realtime was never started
|
|
179
|
+
* (`realtime: false`), and safe to call twice.
|
|
180
|
+
*/
|
|
181
|
+
close: () => void;
|
|
141
182
|
storage: StorageSource;
|
|
142
183
|
storageRegistry: StorageSourceRegistry;
|
|
143
184
|
createStorageSource: (storageId: string) => StorageSource;
|
|
144
185
|
fetchStorageSources: () => Promise<StorageSourceDefinition[]>;
|
|
145
186
|
call: <T = unknown>(endpoint: string, payload?: unknown) => Promise<T>;
|
|
187
|
+
collection: <M extends Record<string, unknown> = Record<string, unknown>>(slug: string) => CollectionClient<M>;
|
|
146
188
|
data: TypedDataLayer<DB>;
|
|
147
189
|
};
|
|
148
190
|
|
|
@@ -187,6 +229,7 @@ export function createRebaseClient<DB = Record<string, unknown>>(options: Create
|
|
|
187
229
|
const auth = createAuth(transport, options.auth);
|
|
188
230
|
const admin = createAdmin(transport, options.admin);
|
|
189
231
|
const cron = createCron(transport, options.cron);
|
|
232
|
+
const backups = createBackups(transport);
|
|
190
233
|
const apiKeys = createApiKeys(transport, options.apiKeys);
|
|
191
234
|
const storage = createStorage(transport);
|
|
192
235
|
const functions = createFunctionsClient(transport);
|
|
@@ -233,9 +276,17 @@ export function createRebaseClient<DB = Record<string, unknown>>(options: Create
|
|
|
233
276
|
return storageSourcesPromise;
|
|
234
277
|
};
|
|
235
278
|
|
|
236
|
-
|
|
279
|
+
// Opting out has to happen before the URL is derived: `deriveWebSocketUrl`
|
|
280
|
+
// always produces one, so a truthy check alone can never leave the socket
|
|
281
|
+
// closed.
|
|
282
|
+
const realtimeEnabled = options.realtime !== false;
|
|
283
|
+
const resolvedWsUrl = realtimeEnabled
|
|
284
|
+
? (options.websocketUrl ?? deriveWebSocketUrl(options.baseUrl))
|
|
285
|
+
: undefined;
|
|
237
286
|
|
|
238
287
|
let ws: RebaseWebSocketClient | undefined;
|
|
288
|
+
/** One channel object per name — see `realtime.channel`. */
|
|
289
|
+
const realtimeChannels = new Map<string, RebaseRealtimeChannel>();
|
|
239
290
|
if (resolvedWsUrl) {
|
|
240
291
|
const wsOnUnauthorized = options.onUnauthorized || (async () => {
|
|
241
292
|
try {
|
|
@@ -263,9 +314,15 @@ export function createRebaseClient<DB = Record<string, unknown>>(options: Create
|
|
|
263
314
|
auth.onAuthStateChange((event, session) => {
|
|
264
315
|
if (!ws) return;
|
|
265
316
|
if (event === "SIGNED_OUT") {
|
|
317
|
+
// Not permanent: the client stays usable, and a later subscribe
|
|
318
|
+
// should reconnect anonymously.
|
|
266
319
|
ws.disconnect();
|
|
267
320
|
} else if (event === "SIGNED_IN" || event === "TOKEN_REFRESHED") {
|
|
268
|
-
|
|
321
|
+
// Only re-authenticate a socket that already exists. Signing in
|
|
322
|
+
// is not a request for realtime, and dialling here would undo
|
|
323
|
+
// lazy connect for every app with a login. A socket opened
|
|
324
|
+
// later authenticates itself from `getAuthToken` on open.
|
|
325
|
+
if (session?.accessToken && ws.hasSocket) {
|
|
269
326
|
ws.authenticate(session.accessToken).catch(console.warn);
|
|
270
327
|
}
|
|
271
328
|
}
|
|
@@ -395,6 +452,7 @@ export function createRebaseClient<DB = Record<string, unknown>>(options: Create
|
|
|
395
452
|
auth,
|
|
396
453
|
admin,
|
|
397
454
|
cron,
|
|
455
|
+
backups,
|
|
398
456
|
apiKeys,
|
|
399
457
|
functions,
|
|
400
458
|
storage,
|
|
@@ -402,6 +460,57 @@ export function createRebaseClient<DB = Record<string, unknown>>(options: Create
|
|
|
402
460
|
createStorageSource,
|
|
403
461
|
fetchStorageSources,
|
|
404
462
|
ws,
|
|
463
|
+
realtime: {
|
|
464
|
+
/**
|
|
465
|
+
* Join a broadcast/presence channel.
|
|
466
|
+
*
|
|
467
|
+
* Repeated calls with the same name return the same channel, so
|
|
468
|
+
* separate components can attach handlers without each opening its
|
|
469
|
+
* own membership — and `leave()` from one would otherwise silently
|
|
470
|
+
* cut off the others.
|
|
471
|
+
*/
|
|
472
|
+
channel: (name: string, options?: ChannelOptions): RebaseRealtimeChannel => {
|
|
473
|
+
// Only `realtime: false` gets here — a hard opt-out, so this
|
|
474
|
+
// stays an error. Being merely *unconnected* does not: the
|
|
475
|
+
// socket opens on the first channel operation, which is the
|
|
476
|
+
// whole point of asking for a channel before you use one.
|
|
477
|
+
if (!ws) {
|
|
478
|
+
throw new RebaseClientError(
|
|
479
|
+
"Realtime is disabled on this client (realtime: false), so channels are unavailable."
|
|
480
|
+
);
|
|
481
|
+
}
|
|
482
|
+
let existing = realtimeChannels.get(name);
|
|
483
|
+
if (!existing) {
|
|
484
|
+
existing = new RebaseRealtimeChannel(name, ws, options);
|
|
485
|
+
realtimeChannels.set(name, existing);
|
|
486
|
+
} else if (options?.history) {
|
|
487
|
+
// Same object by name, so options on a later call have no
|
|
488
|
+
// new channel to apply to. Asking for history upgrades the
|
|
489
|
+
// one that exists rather than being quietly ignored — but
|
|
490
|
+
// never the reverse, so a caller that omits the option
|
|
491
|
+
// cannot switch it off under one that asked for it.
|
|
492
|
+
existing.enableHistory();
|
|
493
|
+
}
|
|
494
|
+
return existing;
|
|
495
|
+
}
|
|
496
|
+
},
|
|
497
|
+
/**
|
|
498
|
+
* Release the realtime socket and its reconnect timer.
|
|
499
|
+
*
|
|
500
|
+
* Until this returns, the open socket keeps the Node event loop alive
|
|
501
|
+
* and the process will not exit on its own. Safe to call when realtime
|
|
502
|
+
* was never started, and safe to call twice.
|
|
503
|
+
*/
|
|
504
|
+
close: () => {
|
|
505
|
+
// Channels hold presence heartbeat timers, which would otherwise
|
|
506
|
+
// keep firing (and keep a Node process alive) after the socket
|
|
507
|
+
// they publish over is gone.
|
|
508
|
+
for (const channel of realtimeChannels.values()) void channel.leave();
|
|
509
|
+
realtimeChannels.clear();
|
|
510
|
+
// Permanent: nothing queued afterwards may redial and keep the
|
|
511
|
+
// event loop alive, which is the reason this method exists.
|
|
512
|
+
ws?.disconnect(true);
|
|
513
|
+
},
|
|
405
514
|
setToken: transport.setToken,
|
|
406
515
|
setAuthTokenGetter: transport.setAuthTokenGetter,
|
|
407
516
|
setOnUnauthorized: transport.setOnUnauthorized,
|