@rebasepro/types 0.24.0 → 0.24.1-canary.g56af4a4
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/call_context.d.ts +8 -4
- package/dist/controllers/client.d.ts +62 -9
- package/dist/controllers/data.d.ts +412 -76
- package/dist/controllers/data_driver.d.ts +85 -1
- package/dist/controllers/index.d.ts +1 -0
- package/dist/controllers/storage-references.d.ts +114 -0
- package/dist/controllers/storage.d.ts +20 -79
- package/dist/index.es.js +139 -12
- package/dist/index.es.js.map +1 -1
- package/dist/types/auth_adapter.d.ts +32 -1
- package/dist/types/backend.d.ts +115 -1
- package/dist/types/backup.d.ts +19 -0
- package/dist/types/cron.d.ts +22 -7
- package/dist/types/entities.d.ts +11 -0
- package/dist/types/history.d.ts +15 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/project_manifest.d.ts +8 -0
- package/dist/types/properties.d.ts +28 -2
- package/dist/types/register.d.ts +43 -0
- package/dist/types/relations.d.ts +37 -1
- package/dist/types/resource_kinds.d.ts +7 -1
- package/dist/types/storage_authorize.d.ts +5 -0
- package/dist/types/websockets.d.ts +32 -1
- package/package.json +1 -1
|
@@ -303,6 +303,23 @@ export interface UserCreationPrepareResult {
|
|
|
303
303
|
/** Whether an invitation was sent (only relevant when hookHandledEmail is true). */
|
|
304
304
|
invitationSent: boolean;
|
|
305
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* What the door creating a user knows about its caller, for
|
|
308
|
+
* `AuthAdapter.prepareUserCreation()`.
|
|
309
|
+
*
|
|
310
|
+
* @group Auth
|
|
311
|
+
*/
|
|
312
|
+
export interface UserCreationOptions {
|
|
313
|
+
/**
|
|
314
|
+
* Whether the caller may waive the password policy with
|
|
315
|
+
* `skipPasswordPolicy: true` in the body. The doors set it for an
|
|
316
|
+
* administrator (the service key signs in as one) and for nobody else, so
|
|
317
|
+
* an adapter that reads the flag is taking an administrator at their word —
|
|
318
|
+
* an import of accounts whose passwords were set under another system's
|
|
319
|
+
* rules. Absent, the flag waives nothing.
|
|
320
|
+
*/
|
|
321
|
+
mayWaivePasswordPolicy?: boolean;
|
|
322
|
+
}
|
|
306
323
|
/**
|
|
307
324
|
* What a create body for an auth collection may name beyond the collection's
|
|
308
325
|
* own declared fields — see `AuthAdapter.describeUserCreationContract()`.
|
|
@@ -322,6 +339,16 @@ export interface UserCreationWriteContract {
|
|
|
322
339
|
* deletes it before the row is ever built.
|
|
323
340
|
*/
|
|
324
341
|
extraFields: string[];
|
|
342
|
+
/**
|
|
343
|
+
* Each body's `prepareUserCreation` touches nothing but its own result —
|
|
344
|
+
* no hook, no email, no write — so a bulk create may prepare several at
|
|
345
|
+
* once. The built-in adapter's path with no `onCreateUser`, no
|
|
346
|
+
* `onAdminCreateUser` and the default `hashPassword` is: it hashes a
|
|
347
|
+
* password and normalizes an email, and a 1,000-row bulk spent half a
|
|
348
|
+
* minute hashing one password at a time. Leave it unset when a hook runs:
|
|
349
|
+
* preparations then run one at a time, in the order the rows were given.
|
|
350
|
+
*/
|
|
351
|
+
independent?: boolean;
|
|
325
352
|
}
|
|
326
353
|
/**
|
|
327
354
|
* Result of `AuthAdapter.finalizeUserCreation()`.
|
|
@@ -505,11 +532,15 @@ export interface AuthAdapter {
|
|
|
505
532
|
*
|
|
506
533
|
* If not implemented, the collection saves values as-is (no password hashing).
|
|
507
534
|
*
|
|
535
|
+
* A chosen `password` is held to the password policy unless the body says
|
|
536
|
+
* `skipPasswordPolicy: true` and `options.mayWaivePasswordPolicy` is set.
|
|
537
|
+
*
|
|
508
538
|
* @param values - Raw request body from the client.
|
|
509
539
|
* @param collectionAuth - The parsed `AuthCollectionConfig` from the collection (if `auth` is an object).
|
|
540
|
+
* @param options - What the door knows about its caller.
|
|
510
541
|
* @returns Processed values ready for `driver.save()`, plus metadata for the post-save step.
|
|
511
542
|
*/
|
|
512
|
-
prepareUserCreation?(values: Record<string, unknown>, collectionAuth?: unknown): Promise<UserCreationPrepareResult>;
|
|
543
|
+
prepareUserCreation?(values: Record<string, unknown>, collectionAuth?: unknown, options?: UserCreationOptions): Promise<UserCreationPrepareResult>;
|
|
513
544
|
/**
|
|
514
545
|
* Describe what a create body for this auth collection is allowed to name,
|
|
515
546
|
* so unknown-field validation can run on it.
|
package/dist/types/backend.d.ts
CHANGED
|
@@ -252,6 +252,36 @@ export interface RealtimeChannelsConfig {
|
|
|
252
252
|
* Defaults to 1000. `REALTIME_MAX_SUBSCRIPTIONS_PER_SOCKET` wins over it.
|
|
253
253
|
*/
|
|
254
254
|
maxSubscriptionsPerSocket?: number;
|
|
255
|
+
/**
|
|
256
|
+
* How many sockets one user (one identity: a signed-in user, an API key's
|
|
257
|
+
* user, an impersonated user) may hold open on one server instance. The
|
|
258
|
+
* next one's `AUTHENTICATE` is refused with `TOO_MANY_SOCKETS` and the
|
|
259
|
+
* socket closed with 4002; the SDK keeps retrying with backoff, so it gets
|
|
260
|
+
* in once another closes. A positive whole number, checked at boot.
|
|
261
|
+
* Unset (the default) is no ceiling. `REALTIME_MAX_SOCKETS_PER_USER` wins
|
|
262
|
+
* over it.
|
|
263
|
+
*/
|
|
264
|
+
maxSocketsPerUser?: number;
|
|
265
|
+
/**
|
|
266
|
+
* The longest a change waits for the refetch that delivers it, in ms. A
|
|
267
|
+
* refetch waits for 300 ms without a change, and at most this long under a
|
|
268
|
+
* steady stream of writes. Defaults to 1000. A positive whole number,
|
|
269
|
+
* checked at boot. `REALTIME_REFETCH_MAX_WAIT_MS` wins over it.
|
|
270
|
+
*/
|
|
271
|
+
refetchMaxWaitMs?: number;
|
|
272
|
+
/**
|
|
273
|
+
* How long a big list subscription's total may be reused before it is
|
|
274
|
+
* counted again, in ms. Unset (the default), every frame counts. Frames
|
|
275
|
+
* that reuse it carry `meta.totalAsOf`. `REALTIME_RECOUNT_INTERVAL_MS`
|
|
276
|
+
* wins over it.
|
|
277
|
+
*/
|
|
278
|
+
recountIntervalMs?: number;
|
|
279
|
+
/**
|
|
280
|
+
* The smallest last total at which {@link recountIntervalMs} applies;
|
|
281
|
+
* smaller lists are counted on every frame. Defaults to 100 000.
|
|
282
|
+
* `REALTIME_RECOUNT_MIN_ROWS` wins over it.
|
|
283
|
+
*/
|
|
284
|
+
recountMinRows?: number;
|
|
255
285
|
}
|
|
256
286
|
/**
|
|
257
287
|
* Abstract realtime provider interface.
|
|
@@ -445,8 +475,19 @@ export interface SqlScriptResult {
|
|
|
445
475
|
tables: SqlScriptTable[];
|
|
446
476
|
/** The last statement's command: `SELECT`, `UPDATE`, `CREATE TABLE` … */
|
|
447
477
|
command?: string;
|
|
448
|
-
/**
|
|
478
|
+
/**
|
|
479
|
+
* How many rows the last statement returned or changed, when its command
|
|
480
|
+
* reports a count — or, for a {@link truncated} result, how many it
|
|
481
|
+
* returned, of which {@link rows} holds the first.
|
|
482
|
+
*/
|
|
449
483
|
rowCount?: number;
|
|
484
|
+
/**
|
|
485
|
+
* The last statement returned more than a run keeps, and {@link rows}
|
|
486
|
+
* holds only its first rows. The Studio console's door keeps at most
|
|
487
|
+
* 10,000 rows, or 32 Mi characters of values, of one result: a run that
|
|
488
|
+
* held every row of a million-row table took the server down with it.
|
|
489
|
+
*/
|
|
490
|
+
truncated?: boolean;
|
|
450
491
|
/** What the database said while the script ran, in order. */
|
|
451
492
|
notices: SqlScriptNotice[];
|
|
452
493
|
/**
|
|
@@ -457,6 +498,22 @@ export interface SqlScriptResult {
|
|
|
457
498
|
*/
|
|
458
499
|
effectiveRole?: string;
|
|
459
500
|
}
|
|
501
|
+
/**
|
|
502
|
+
* A query's result being read a batch at a time — see `SQLAdmin.openSqlCursor`.
|
|
503
|
+
*
|
|
504
|
+
* @group Admin
|
|
505
|
+
*/
|
|
506
|
+
export interface SqlCursor {
|
|
507
|
+
/** The result's column names, in order. */
|
|
508
|
+
readonly columns: string[];
|
|
509
|
+
/**
|
|
510
|
+
* The next rows, each value the text the database wrote for it and `null`
|
|
511
|
+
* for NULL — `null` once there are none left.
|
|
512
|
+
*/
|
|
513
|
+
read(): Promise<(string | null)[][] | null>;
|
|
514
|
+
/** End the read and give the session back. Safe to call more than once. */
|
|
515
|
+
close(): Promise<void>;
|
|
516
|
+
}
|
|
460
517
|
/**
|
|
461
518
|
* Administrative operations for SQL-based databases (PostgreSQL, MySQL, etc.).
|
|
462
519
|
* Used by the SQL Editor, RLS Editor, and schema browser.
|
|
@@ -472,12 +529,19 @@ export interface SQLAdmin {
|
|
|
472
529
|
* before it is reused. For SQL a person wrote (the Studio editor), which
|
|
473
530
|
* may `SET ROLE` and must not leave that on a pooled connection the
|
|
474
531
|
* server's own queries use next.
|
|
532
|
+
*
|
|
533
|
+
* `maxResultBytes` refuses a result whose rows take more than that many
|
|
534
|
+
* bytes, with an error whose `code` is `RESULT_TOO_LARGE`, rather than
|
|
535
|
+
* return it — never a part of it. On a session of its own the read stops
|
|
536
|
+
* there; elsewhere the rows are measured once read. The WebSocket
|
|
537
|
+
* `EXECUTE_SQL` door sets it for every caller.
|
|
475
538
|
*/
|
|
476
539
|
executeSql(sql: string, options?: {
|
|
477
540
|
database?: string;
|
|
478
541
|
role?: string;
|
|
479
542
|
params?: unknown[];
|
|
480
543
|
isolateSession?: boolean;
|
|
544
|
+
maxResultBytes?: number;
|
|
481
545
|
}): Promise<Record<string, unknown>[]>;
|
|
482
546
|
/**
|
|
483
547
|
* Run a script a person wrote — the Studio SQL console — and describe what
|
|
@@ -490,11 +554,35 @@ export interface SQLAdmin {
|
|
|
490
554
|
* no other. A script that leaves a transaction open is refused, and the
|
|
491
555
|
* transaction rolled back: nothing outlives the run to be committed or
|
|
492
556
|
* rolled back by a later one.
|
|
557
|
+
*
|
|
558
|
+
* Aborting `signal` cancels the run: the statement in progress is stopped
|
|
559
|
+
* (`pg_cancel_backend` on a Postgres server; the in-process development
|
|
560
|
+
* database cannot be interrupted, and the run ends on its own), and the
|
|
561
|
+
* promise rejects.
|
|
493
562
|
*/
|
|
494
563
|
runSqlScript?(sql: string, options?: {
|
|
495
564
|
database?: string;
|
|
496
565
|
role?: string;
|
|
566
|
+
signal?: AbortSignal;
|
|
497
567
|
}): Promise<SqlScriptResult>;
|
|
568
|
+
/**
|
|
569
|
+
* Read the whole result of ONE query a person wrote, a batch at a time —
|
|
570
|
+
* the Studio SQL console's "Download full result as CSV", which the
|
|
571
|
+
* console's 10,000-row cap would otherwise leave no way to get.
|
|
572
|
+
*
|
|
573
|
+
* Through a server-side cursor, in a read-only transaction, on a session
|
|
574
|
+
* of its own, as `role`: a write is refused by the database, and a second
|
|
575
|
+
* statement by the protocol. Each batch is read when the previous one has
|
|
576
|
+
* been consumed, so memory holds one batch whatever the result's size;
|
|
577
|
+
* every read runs under the session's `statement_timeout`. The session is
|
|
578
|
+
* reset and returned when the cursor is closed — which the caller must do,
|
|
579
|
+
* on success, failure or a reader that went away.
|
|
580
|
+
*/
|
|
581
|
+
openSqlCursor?(sql: string, options?: {
|
|
582
|
+
database?: string;
|
|
583
|
+
role?: string;
|
|
584
|
+
batchSize?: number;
|
|
585
|
+
}): Promise<SqlCursor>;
|
|
498
586
|
/**
|
|
499
587
|
* Fetch the available databases on the server.
|
|
500
588
|
*/
|
|
@@ -520,6 +608,21 @@ export interface SQLAdmin {
|
|
|
520
608
|
* Fetch the current database name.
|
|
521
609
|
*/
|
|
522
610
|
fetchCurrentDatabase?(): Promise<string | undefined>;
|
|
611
|
+
/**
|
|
612
|
+
* Hold a `LISTEN` on `channel`, on a connection of its own that stays open
|
|
613
|
+
* and comes back after a drop, and call `onNotify` with each payload — what
|
|
614
|
+
* wakes the job worker when another process enqueues, instead of a poll
|
|
615
|
+
* every two seconds.
|
|
616
|
+
*
|
|
617
|
+
* Resolves once listening, with the stop. Rejects when no such connection
|
|
618
|
+
* can be had — an in-process database, a first connect that fails — so the
|
|
619
|
+
* caller can fall back to polling. `onReconnect` is called each time a
|
|
620
|
+
* dropped connection is listening again: whatever was sent meanwhile is
|
|
621
|
+
* lost. `channel` must be a plain identifier.
|
|
622
|
+
*/
|
|
623
|
+
listen?(channel: string, onNotify: (payload: string) => void, options?: {
|
|
624
|
+
onReconnect?: () => void;
|
|
625
|
+
}): Promise<() => Promise<void>>;
|
|
523
626
|
}
|
|
524
627
|
/**
|
|
525
628
|
* Administrative operations for document-based databases (MongoDB, Firestore, etc.).
|
|
@@ -799,6 +902,17 @@ export interface RealtimeSocketOptions {
|
|
|
799
902
|
* identity that has since signed out, been revoked, demoted or deleted.
|
|
800
903
|
*/
|
|
801
904
|
identityRecheckIntervalMs?: number;
|
|
905
|
+
/**
|
|
906
|
+
* How often a socket that asked for heartbeats (`?rebase_caps=heartbeat`)
|
|
907
|
+
* is sent one, in ms (default 30 000). The SDK redials after two missed.
|
|
908
|
+
*/
|
|
909
|
+
heartbeatIntervalMs?: number;
|
|
910
|
+
/**
|
|
911
|
+
* How many sockets one identity may hold open on this server; the next
|
|
912
|
+
* one's `AUTHENTICATE` is refused with `TOO_MANY_SOCKETS` and the socket
|
|
913
|
+
* closed (4002). Unset for no ceiling. See `realtime.maxSocketsPerUser`.
|
|
914
|
+
*/
|
|
915
|
+
maxSocketsPerUser?: number;
|
|
802
916
|
}
|
|
803
917
|
/**
|
|
804
918
|
* A `BackendBootstrapper` encapsulates all driver-specific initialization logic.
|
package/dist/types/backup.d.ts
CHANGED
|
@@ -87,6 +87,25 @@ export interface BackupScheduleStatus {
|
|
|
87
87
|
*/
|
|
88
88
|
historyError?: string;
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* A short-lived link to one backup's bytes, from `POST /admin/backups/download-url`.
|
|
92
|
+
*
|
|
93
|
+
* The URL needs no `Authorization` header — the token in it stands in for one,
|
|
94
|
+
* for this backup only — so a browser can be sent to it and stream the dump to
|
|
95
|
+
* disk instead of holding it in the page.
|
|
96
|
+
*/
|
|
97
|
+
export interface BackupDownloadLink {
|
|
98
|
+
/**
|
|
99
|
+
* `…/admin/backups/download?key=…&token=…`. The server answers it as a
|
|
100
|
+
* path on itself; the SDK's `downloadUrl()` answers it absolute, on the
|
|
101
|
+
* client's own base URL.
|
|
102
|
+
*/
|
|
103
|
+
url: string;
|
|
104
|
+
/** The token in {@link url}, on its own. */
|
|
105
|
+
token: string;
|
|
106
|
+
/** Seconds until the link stops working (60). */
|
|
107
|
+
expiresIn: number;
|
|
108
|
+
}
|
|
90
109
|
/** What the backup listing (`GET /admin/backups`) answers. */
|
|
91
110
|
export interface BackupListing {
|
|
92
111
|
/** Backups at the destination, newest first. */
|
package/dist/types/cron.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RebaseServerClient } from "../controllers/client.js";
|
|
2
|
+
import type { RegisteredDatabase } from "./register.js";
|
|
2
3
|
/**
|
|
3
4
|
* Cron Job type definitions for Rebase.
|
|
4
5
|
*
|
|
@@ -10,7 +11,7 @@ import type { RebaseServerClient } from "../controllers/client.js";
|
|
|
10
11
|
* A cron job definition file exports this shape as its default export.
|
|
11
12
|
* See the example cron files in `app/backend/crons/` for usage.
|
|
12
13
|
*/
|
|
13
|
-
export interface CronJobDefinition {
|
|
14
|
+
export interface CronJobDefinition<DB = RegisteredDatabase> {
|
|
14
15
|
schedule: string;
|
|
15
16
|
/**
|
|
16
17
|
* IANA time zone the schedule is read in, e.g. `"Europe/Madrid"`.
|
|
@@ -84,20 +85,21 @@ export interface CronJobDefinition {
|
|
|
84
85
|
* Receives a context object with the data driver and logger.
|
|
85
86
|
* May return arbitrary JSON-serialisable data stored in the log.
|
|
86
87
|
*/
|
|
87
|
-
handler: (ctx: CronJobContext) => Promise<unknown> | unknown;
|
|
88
|
+
handler: (ctx: CronJobContext<DB>) => Promise<unknown> | unknown;
|
|
88
89
|
}
|
|
89
90
|
/**
|
|
90
91
|
* Context passed to each cron handler invocation.
|
|
91
92
|
*/
|
|
92
|
-
export interface CronJobContext {
|
|
93
|
+
export interface CronJobContext<DB = RegisteredDatabase> {
|
|
93
94
|
/** The job's unique ID (derived from filename). */
|
|
94
95
|
jobId: string;
|
|
95
96
|
/** The current scheduled tick timestamp. */
|
|
96
97
|
scheduledAt: Date;
|
|
97
98
|
/**
|
|
98
|
-
* A simple logger scoped to this job run. Each line is
|
|
99
|
-
*
|
|
100
|
-
*
|
|
99
|
+
* A simple logger scoped to this job run. Each line is written to the
|
|
100
|
+
* server log at `info`, with a `cron` field holding the job ID, and kept
|
|
101
|
+
* with the run's log entry — the first 500 lines or 64 KB of them,
|
|
102
|
+
* whichever comes first, then one line saying how many more there were.
|
|
101
103
|
*/
|
|
102
104
|
log: (...args: unknown[]) => void;
|
|
103
105
|
/**
|
|
@@ -156,7 +158,7 @@ export interface CronJobContext {
|
|
|
156
158
|
* }
|
|
157
159
|
* });
|
|
158
160
|
*/
|
|
159
|
-
rebase: RebaseServerClient
|
|
161
|
+
rebase: RebaseServerClient<DB>;
|
|
160
162
|
}
|
|
161
163
|
export type CronJobRunState = "idle" | "running" | "success" | "error" | "disabled";
|
|
162
164
|
/**
|
|
@@ -244,3 +246,16 @@ export interface CronJobLogEntry {
|
|
|
244
246
|
/** Whether this was a manual trigger. */
|
|
245
247
|
manual?: boolean;
|
|
246
248
|
}
|
|
249
|
+
/**
|
|
250
|
+
* The answer to a manual trigger made with `?async=1`
|
|
251
|
+
* (`client.cron.triggerJob(id, { async: true })`): the run has started and
|
|
252
|
+
* carries on after the answer. Its outcome is in the run history, as the entry
|
|
253
|
+
* with `manual: true` and this `startedAt`.
|
|
254
|
+
*/
|
|
255
|
+
export interface CronJobStarted {
|
|
256
|
+
accepted: true;
|
|
257
|
+
/** ISO timestamp the run started at — the `startedAt` of its log entry. */
|
|
258
|
+
startedAt: string;
|
|
259
|
+
/** The job's status as the run began. */
|
|
260
|
+
job: CronJobStatus;
|
|
261
|
+
}
|
package/dist/types/entities.d.ts
CHANGED
|
@@ -32,6 +32,17 @@ export interface Entity<M extends Record<string, unknown> = Record<string, unkno
|
|
|
32
32
|
* and a record fetched by id never has one.
|
|
33
33
|
*/
|
|
34
34
|
searchMatches?: SearchMatch[];
|
|
35
|
+
/**
|
|
36
|
+
* The version of the row this entity was read at: the `ETag` the read
|
|
37
|
+
* reported, for a `findById`, a `PATCH` answer or a live row frame. A
|
|
38
|
+
* form saves with it as `If-Match`, so a write made against a version
|
|
39
|
+
* someone has since replaced is refused instead of reverting their change.
|
|
40
|
+
*
|
|
41
|
+
* Absent for a row from a list, from a cache, or from a server or data
|
|
42
|
+
* source that does not report versions — a save then is unconditional,
|
|
43
|
+
* as it always was.
|
|
44
|
+
*/
|
|
45
|
+
etag?: string;
|
|
35
46
|
/**
|
|
36
47
|
* Which driver this entity belongs to (e.g., 'postgres', 'firestore').
|
|
37
48
|
* If not specified, the default driver is assumed.
|
package/dist/types/history.d.ts
CHANGED
|
@@ -27,8 +27,20 @@ export interface EntityHistoryEntry {
|
|
|
27
27
|
action: "create" | "update" | "delete";
|
|
28
28
|
/** Which fields changed. `null` for creates and deletes. */
|
|
29
29
|
changed_fields: string[] | null;
|
|
30
|
+
/** The row after the change; for a delete, the row that was deleted. */
|
|
30
31
|
values: Record<string, unknown> | null;
|
|
32
|
+
/**
|
|
33
|
+
* On an update, what the changed fields held before it. `null` for creates
|
|
34
|
+
* and deletes. Which fields it holds depends on {@link format}.
|
|
35
|
+
*/
|
|
31
36
|
previous_values: Record<string, unknown> | null;
|
|
37
|
+
/**
|
|
38
|
+
* How the entry was stored. Absent or `1`: `previous_values` is the whole
|
|
39
|
+
* row before the change. `2`: it holds the `changed_fields` only — a field
|
|
40
|
+
* that is absent from it was absent before — and every other field is
|
|
41
|
+
* unchanged, so it is in `values`.
|
|
42
|
+
*/
|
|
43
|
+
format?: 1 | 2;
|
|
32
44
|
updated_by: string | null;
|
|
33
45
|
/** ISO-8601. A driver storing a native date converts on read. */
|
|
34
46
|
updated_at: string;
|
|
@@ -46,7 +58,9 @@ export interface RecordHistoryParams {
|
|
|
46
58
|
updatedBy?: string | null;
|
|
47
59
|
}
|
|
48
60
|
/**
|
|
49
|
-
* How much history to keep.
|
|
61
|
+
* How much history to keep. Both rules are applied to a row after each write
|
|
62
|
+
* to it; `ttlDays` is also applied to every row by an hourly sweep, so the
|
|
63
|
+
* history of a row nobody writes again (a deleted one) still ages out.
|
|
50
64
|
* @group Backend
|
|
51
65
|
*/
|
|
52
66
|
export interface HistoryRetentionConfig {
|
package/dist/types/index.d.ts
CHANGED
|
@@ -83,6 +83,14 @@ export interface RebaseBackendAppConfig {
|
|
|
83
83
|
* its default export. Default `collections/users`.
|
|
84
84
|
*/
|
|
85
85
|
usersCollection?: string;
|
|
86
|
+
/**
|
|
87
|
+
* Type server-side data from the generated `Database`. When `true`, every
|
|
88
|
+
* `rebase generate-sdk` — `rebase dev`'s included, and the first one on a
|
|
89
|
+
* fresh clone — writes the types and the `Register` declaration to
|
|
90
|
+
* `<config>/generated/`. `rebase generate-sdk --register` sets it.
|
|
91
|
+
* Default `false`.
|
|
92
|
+
*/
|
|
93
|
+
registerDatabase?: boolean;
|
|
86
94
|
/**
|
|
87
95
|
* `runtime: "custom"` only. Dockerfile path relative to the repository root.
|
|
88
96
|
* Default `Dockerfile`.
|
|
@@ -5,6 +5,12 @@ import type { ColorKey, ColorScheme } from "./chips.js";
|
|
|
5
5
|
import type { AuthState } from "../controllers/auth_state.js";
|
|
6
6
|
import type { AfterReadProps, BeforeSaveProps } from "./entity_callbacks.js";
|
|
7
7
|
import type { User } from "../users/index.js";
|
|
8
|
+
/**
|
|
9
|
+
* A SQL expression, written in template form: ``"sql`gen_random_uuid()`"``.
|
|
10
|
+
* The `sql` and backticks are markup; the text between them is the SQL.
|
|
11
|
+
* @group Entity properties
|
|
12
|
+
*/
|
|
13
|
+
export type SqlExpression = `sql\`${string}\``;
|
|
8
14
|
/**
|
|
9
15
|
* Callbacks/Hooks for individual property fields
|
|
10
16
|
* @group Entity properties
|
|
@@ -372,6 +378,25 @@ export interface BaseProperty<CustomProps = unknown> {
|
|
|
372
378
|
* ```
|
|
373
379
|
*/
|
|
374
380
|
access?: FieldAccess;
|
|
381
|
+
/**
|
|
382
|
+
* The database fills this column when a create leaves it empty: a
|
|
383
|
+
* `NOT NULL DEFAULT <expression>` column such as `gen_random_uuid()`,
|
|
384
|
+
* `nextval(…)` or `now()`, whose value no property can state.
|
|
385
|
+
*
|
|
386
|
+
* A `validation.required` field with it may be left out of a create, by
|
|
387
|
+
* the admin form and by the API alike, and a create that sends it as `null`
|
|
388
|
+
* leaves it to the database rather than storing `NULL`. It is still
|
|
389
|
+
* required when a stored record is edited. The generated SDK's `Insert`
|
|
390
|
+
* type makes the field optional. Defaults to `false`.
|
|
391
|
+
*
|
|
392
|
+
* Give the expression, in the template form `isId` takes
|
|
393
|
+
* (``databaseDefault: "sql`gen_random_uuid()`"``), and the schema planner
|
|
394
|
+
* writes it as the column's `DEFAULT`, so `rebase db push` keeps it. `true`
|
|
395
|
+
* declares a DEFAULT someone else wrote and the planner does not write
|
|
396
|
+
* one, so a push plans dropping it. "Import from table" and
|
|
397
|
+
* `rebase schema introspect` set the expression they read.
|
|
398
|
+
*/
|
|
399
|
+
databaseDefault?: boolean | SqlExpression;
|
|
375
400
|
/**
|
|
376
401
|
* Use this to define dynamic properties that change based on certain conditions
|
|
377
402
|
* or on the entity's values. For example, you can make a field read-only if
|
|
@@ -421,8 +446,9 @@ export interface StringProperty extends BaseProperty {
|
|
|
421
446
|
*/
|
|
422
447
|
defaultValue?: string;
|
|
423
448
|
/**
|
|
424
|
-
* Optional database column type. If not set, it
|
|
425
|
-
*
|
|
449
|
+
* Optional database column type. If not set, it is `text` — or `uuid` for an `isId: "uuid"` key.
|
|
450
|
+
* `validation.max` does not bound a `text` column; declare `varchar` (or `char`) to get a
|
|
451
|
+
* width, which is then taken from `validation.max` (255 when it states none).
|
|
426
452
|
*/
|
|
427
453
|
columnType?: "varchar" | "text" | "char" | "uuid";
|
|
428
454
|
/**
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The project-wide registry of generated types, filled by module augmentation.
|
|
3
|
+
*
|
|
4
|
+
* Empty by default, and every server-side data surface is untyped while it is:
|
|
5
|
+
* `context.data`, `rebase.dataAsAdmin`, a cron's `ctx.rebase` and a function's
|
|
6
|
+
* `ctx.rebase` accept any collection by any name and hand back open records.
|
|
7
|
+
*
|
|
8
|
+
* A project that generated its types with `rebase generate-sdk` registers its
|
|
9
|
+
* `Database` once, in any file the TypeScript program includes:
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // src/rebase-env.d.ts — or the top of any module file
|
|
13
|
+
* import type { Database } from "./generated/database.types";
|
|
14
|
+
*
|
|
15
|
+
* declare module "@rebasepro/types" {
|
|
16
|
+
* interface Register {
|
|
17
|
+
* database: Database;
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* From then on every one of those surfaces is typed from it: rows, filters,
|
|
23
|
+
* sorts, `include`, and the inputs `create` and `update` accept. Nothing about
|
|
24
|
+
* the runtime changes; the registration is a declaration.
|
|
25
|
+
*
|
|
26
|
+
* A single function or cron can still name its own type instead —
|
|
27
|
+
* `defineFunction<Database>(…)`, `defineCron<Database>({…})` — which wins over
|
|
28
|
+
* the registration.
|
|
29
|
+
*
|
|
30
|
+
* @group Data
|
|
31
|
+
*/
|
|
32
|
+
export interface Register {
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The `Database` a project registered on {@link Register}, or `unknown` when it
|
|
36
|
+
* registered none — which is what keeps an unregistered project's server-side
|
|
37
|
+
* data untyped, exactly as it was before the registry existed.
|
|
38
|
+
*
|
|
39
|
+
* @group Data
|
|
40
|
+
*/
|
|
41
|
+
export type RegisteredDatabase = Register extends {
|
|
42
|
+
database: infer DB;
|
|
43
|
+
} ? DB : unknown;
|
|
@@ -22,6 +22,23 @@ export type OnAction = "cascade" | "restrict" | "no action" | "set null" | "set
|
|
|
22
22
|
* @group Models
|
|
23
23
|
*/
|
|
24
24
|
export declare const JUNCTION_PIVOT_KEY = "_pivot";
|
|
25
|
+
/**
|
|
26
|
+
* The key a row carries the totals of its counted relations under.
|
|
27
|
+
*
|
|
28
|
+
* An include that asks for a count — `include: { tags: { limit: 4, count: true } }`
|
|
29
|
+
* — serves, beside the (capped) `tags`, how many related rows there are in all:
|
|
30
|
+
* `{ id: 1, tags: [ …4 rows ], _count: { tags: 37 } }`. One object per row,
|
|
31
|
+
* keyed by relation name, so counting a second relation adds a key rather than
|
|
32
|
+
* a second top-level name.
|
|
33
|
+
*
|
|
34
|
+
* Leading underscore, like {@link JUNCTION_PIVOT_KEY} and `_matches`: it
|
|
35
|
+
* describes the read, not the record, and no column or form writes it. A
|
|
36
|
+
* collection with a property of this name cannot ask for a count — the read
|
|
37
|
+
* answers 400 rather than writing over the column.
|
|
38
|
+
*
|
|
39
|
+
* @group Models
|
|
40
|
+
*/
|
|
41
|
+
export declare const INCLUDE_COUNT_KEY = "_count";
|
|
25
42
|
/**
|
|
26
43
|
* What kind of link a relation is.
|
|
27
44
|
*
|
|
@@ -108,6 +125,23 @@ export interface BelongsToRelation extends RelationBase {
|
|
|
108
125
|
* Defaults to `<relationName>_id`.
|
|
109
126
|
*/
|
|
110
127
|
localKey?: string;
|
|
128
|
+
/**
|
|
129
|
+
* Whether Rebase creates a btree index on {@link localKey}. Defaults to
|
|
130
|
+
* `true`.
|
|
131
|
+
*
|
|
132
|
+
* Postgres indexes the key a foreign key *points at* but never the column
|
|
133
|
+
* holding it, so without one every read from the other side seq-scans this
|
|
134
|
+
* table: a `hasMany`/`hasOne` include, a nested listing such as
|
|
135
|
+
* `/authors/5/posts`, and the `ON DELETE` check when a target row is
|
|
136
|
+
* deleted. The derived index is skipped on its own when the column already
|
|
137
|
+
* leads one — the primary key, a `validation.unique`, or an entry in
|
|
138
|
+
* `indexes:` — so it never duplicates an index you declared.
|
|
139
|
+
*
|
|
140
|
+
* Set `false` for a table so write-heavy that the index costs more than
|
|
141
|
+
* the lookups it serves, or when you keep an index of your own on the
|
|
142
|
+
* column outside Rebase's `indexes:` block.
|
|
143
|
+
*/
|
|
144
|
+
index?: boolean;
|
|
111
145
|
}
|
|
112
146
|
/**
|
|
113
147
|
* The target holds the foreign key, and at most one target row points back.
|
|
@@ -234,7 +268,7 @@ export interface ManyToManyRelation extends RelationBase {
|
|
|
234
268
|
* through: {
|
|
235
269
|
* table: "org_members",
|
|
236
270
|
* properties: {
|
|
237
|
-
* role: { type: "string", enum:
|
|
271
|
+
* role: { type: "string", enum: { owner: "Owner", admin: "Admin", member: "Member" },
|
|
238
272
|
* defaultValue: "member", validation: { required: true } },
|
|
239
273
|
* joinedAt: { type: "date", autoValue: "on_create" }
|
|
240
274
|
* }
|
|
@@ -364,6 +398,8 @@ export interface ResolvedBelongsTo extends ResolvedRelationBase {
|
|
|
364
398
|
shared: false;
|
|
365
399
|
/** Column on this collection's table. */
|
|
366
400
|
localKey: string;
|
|
401
|
+
/** As authored, with the default filled in — see {@link BelongsToRelation.index}. */
|
|
402
|
+
index: boolean;
|
|
367
403
|
}
|
|
368
404
|
/** @group Models */
|
|
369
405
|
export interface ResolvedHasOne extends ResolvedRelationBase {
|
|
@@ -167,11 +167,13 @@ export interface TopicOptions extends DeclareOptions {
|
|
|
167
167
|
*
|
|
168
168
|
* `attempt` counts from 1. Worth branching on: the first delivery and the
|
|
169
169
|
* fourth are the same call, but the fourth is where it is worth logging loudly.
|
|
170
|
+
* `signal` is aborted when the attempt runs past the job queue's `timeoutMs`.
|
|
170
171
|
*/
|
|
171
172
|
export type TopicHandler<T> = (event: T, context: {
|
|
172
173
|
attempt: number;
|
|
173
174
|
topic: string;
|
|
174
175
|
subscription: string;
|
|
176
|
+
signal: AbortSignal;
|
|
175
177
|
}) => Promise<void> | void;
|
|
176
178
|
/** A declared subscription, as recorded in the graph and wired at boot. */
|
|
177
179
|
export interface TopicSubscription<T = unknown> {
|
|
@@ -279,11 +281,15 @@ export interface QueueOptions extends DeclareOptions {
|
|
|
279
281
|
/** Attempts before a job is left failed. Default 5. */
|
|
280
282
|
maxAttempts?: number;
|
|
281
283
|
}
|
|
282
|
-
/**
|
|
284
|
+
/**
|
|
285
|
+
* What a queue's handler receives. `attempt` counts from 1; `signal` is
|
|
286
|
+
* aborted when the attempt runs past the job queue's `timeoutMs`.
|
|
287
|
+
*/
|
|
283
288
|
export type QueueHandler<T> = (payload: T, context: {
|
|
284
289
|
attempt: number;
|
|
285
290
|
queue: string;
|
|
286
291
|
jobId: string;
|
|
292
|
+
signal: AbortSignal;
|
|
287
293
|
}) => Promise<void> | void;
|
|
288
294
|
/** Per-job options at enqueue time. */
|
|
289
295
|
export interface QueueEnqueueOptions {
|
|
@@ -18,6 +18,11 @@ export interface StorageAuthorizeUser {
|
|
|
18
18
|
export interface StorageAuthorizeContext {
|
|
19
19
|
/** Object key, bucket prefix stripped and traversal already sanitized. */
|
|
20
20
|
key: string;
|
|
21
|
+
/**
|
|
22
|
+
* The bucket, by one name per bucket. On S3 and GCS, whose source serves a
|
|
23
|
+
* single bucket, this is always `"default"` — also when the request named
|
|
24
|
+
* the provider's bucket (`acme-media`), which reaches the same objects.
|
|
25
|
+
*/
|
|
21
26
|
bucket: string;
|
|
22
27
|
operation: StorageOperation;
|
|
23
28
|
/** Null when the route allows unauthenticated access. */
|
|
@@ -69,6 +69,13 @@ export interface CollectionUpdateMeta {
|
|
|
69
69
|
hasMore: boolean;
|
|
70
70
|
/** The opaque cursor continuing this listing — see `PaginationMeta.nextCursor`. */
|
|
71
71
|
nextCursor?: string;
|
|
72
|
+
/**
|
|
73
|
+
* When `total` was counted, ISO-8601 — present only when it was not
|
|
74
|
+
* counted for this frame. A server with `REALTIME_RECOUNT_INTERVAL_MS` set
|
|
75
|
+
* reuses a big list's total for that long rather than counting it on every
|
|
76
|
+
* write; `hasMore` is then judged against the reused total.
|
|
77
|
+
*/
|
|
78
|
+
totalAsOf?: string;
|
|
72
79
|
/**
|
|
73
80
|
* The count could not be taken, so `total` is missing and `hasMore` is a
|
|
74
81
|
* floor rather than an answer.
|
|
@@ -97,6 +104,14 @@ export interface SingleUpdateMessage extends WebSocketMessage {
|
|
|
97
104
|
type: "single_update";
|
|
98
105
|
subscriptionId: string;
|
|
99
106
|
row: Record<string, unknown> | null;
|
|
107
|
+
/**
|
|
108
|
+
* The row's version: the `ETag` a `GET /:id` of it answers, to send back
|
|
109
|
+
* as `If-Match` on a `PATCH` or `DELETE`. Sent only to a `subscribe_one`
|
|
110
|
+
* that asked with `etag: true`, and never beside a `null` row. A server
|
|
111
|
+
* that predates it ignores the ask and sends no tag; a client that does
|
|
112
|
+
* not ask gets frames without one.
|
|
113
|
+
*/
|
|
114
|
+
etag?: string;
|
|
100
115
|
}
|
|
101
116
|
/**
|
|
102
117
|
* Lightweight patch message sent to collection subscribers when a single
|
|
@@ -151,6 +166,22 @@ export interface BroadcastMessage extends WebSocketMessage {
|
|
|
151
166
|
*/
|
|
152
167
|
seq?: number;
|
|
153
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Server → client: the sequence number the sender's own broadcast took on a
|
|
171
|
+
* retained channel, sent to the sender in place of the message itself (which
|
|
172
|
+
* is never echoed back).
|
|
173
|
+
*
|
|
174
|
+
* Sent only to a socket that opted into `channel_seq` when it opened (see the
|
|
175
|
+
* socket's `hello`). With it, every member of a retained channel hears of
|
|
176
|
+
* every number, one way or the other, so a client that sees a hole in the
|
|
177
|
+
* sequence knows it missed a message and can ask for it — which is what the
|
|
178
|
+
* SDK does.
|
|
179
|
+
*/
|
|
180
|
+
export interface BroadcastAckMessage extends WebSocketMessage {
|
|
181
|
+
type: "broadcast_ack";
|
|
182
|
+
channel: string;
|
|
183
|
+
seq: number;
|
|
184
|
+
}
|
|
154
185
|
/**
|
|
155
186
|
* Server → client: the whole roster of a channel.
|
|
156
187
|
*
|
|
@@ -276,4 +307,4 @@ export interface ChannelHistoryMessage extends WebSocketMessage {
|
|
|
276
307
|
* {@link WebSocketMessage}, whose `type` is a bare `string`, and a `string`
|
|
277
308
|
* discriminates nothing.
|
|
278
309
|
*/
|
|
279
|
-
export type ChannelMessage = BroadcastMessage | PresenceStateMessage | PresenceDiffMessage | ChannelHistoryMessage | ChannelErrorMessage;
|
|
310
|
+
export type ChannelMessage = BroadcastMessage | BroadcastAckMessage | PresenceStateMessage | PresenceDiffMessage | ChannelHistoryMessage | ChannelErrorMessage;
|