@rebasepro/types 0.23.0 → 0.24.1-canary.g19596ef
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 +113 -24
- package/dist/controllers/collection_registry.d.ts +17 -0
- package/dist/controllers/data.d.ts +570 -122
- 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 +73 -13
- package/dist/errors.d.ts +15 -1
- package/dist/index.es.js +1228 -43
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +1 -1
- package/dist/types/api_keys.d.ts +68 -36
- package/dist/types/auth_adapter.d.ts +97 -2
- package/dist/types/backend.d.ts +356 -9
- package/dist/types/backup.d.ts +102 -0
- package/dist/types/collection_patch.d.ts +74 -0
- package/dist/types/collections.d.ts +23 -0
- package/dist/types/cron.d.ts +58 -7
- package/dist/types/database_adapter.d.ts +34 -8
- package/dist/types/entities.d.ts +11 -0
- package/dist/types/history.d.ts +15 -1
- package/dist/types/index.d.ts +3 -0
- package/dist/types/project_manifest.d.ts +73 -1
- package/dist/types/properties.d.ts +39 -2
- package/dist/types/register.d.ts +43 -0
- package/dist/types/relations.d.ts +37 -1
- package/dist/types/resource_kinds.d.ts +20 -10
- package/dist/types/schema_editing.d.ts +39 -13
- package/dist/types/scopes.d.ts +272 -0
- package/dist/types/storage_authorize.d.ts +10 -1
- package/dist/types/storage_source.d.ts +2 -6
- package/dist/types/websockets.d.ts +32 -1
- package/dist/users/user.d.ts +7 -0
- package/package.json +1 -1
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,17 +85,22 @@ 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
|
+
/**
|
|
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.
|
|
103
|
+
*/
|
|
98
104
|
log: (...args: unknown[]) => void;
|
|
99
105
|
/**
|
|
100
106
|
* Aborted when the run exceeds `timeoutSeconds`, or when the server
|
|
@@ -152,7 +158,7 @@ export interface CronJobContext {
|
|
|
152
158
|
* }
|
|
153
159
|
* });
|
|
154
160
|
*/
|
|
155
|
-
rebase: RebaseServerClient
|
|
161
|
+
rebase: RebaseServerClient<DB>;
|
|
156
162
|
}
|
|
157
163
|
export type CronJobRunState = "idle" | "running" | "success" | "error" | "disabled";
|
|
158
164
|
/**
|
|
@@ -179,11 +185,43 @@ export interface CronJobStatus {
|
|
|
179
185
|
lastDurationMs?: number;
|
|
180
186
|
/** Error message from the last failed run. */
|
|
181
187
|
lastError?: string;
|
|
182
|
-
/**
|
|
188
|
+
/**
|
|
189
|
+
* Executions in the run history: every process's, read from `cron_logs`
|
|
190
|
+
* where runs are persisted, and this process's own otherwise.
|
|
191
|
+
*/
|
|
183
192
|
totalRuns: number;
|
|
184
|
-
/**
|
|
193
|
+
/** How many of {@link totalRuns} failed. */
|
|
185
194
|
totalFailures: number;
|
|
186
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* A job the scheduler refused, and why.
|
|
198
|
+
*
|
|
199
|
+
* It is not a `CronJobStatus`: it has no state, no next run and no counters,
|
|
200
|
+
* because it was never registered. Reporting it as a job with `state: "error"`
|
|
201
|
+
* would be a lie in the other direction — nothing is going to run it.
|
|
202
|
+
*/
|
|
203
|
+
export interface RejectedCronJob {
|
|
204
|
+
id: string;
|
|
205
|
+
name: string;
|
|
206
|
+
schedule: string;
|
|
207
|
+
/** Why it was refused — most often `Expected 5 fields, got 6`. */
|
|
208
|
+
reason: string;
|
|
209
|
+
}
|
|
210
|
+
/** What the cron listing (`GET /admin/cron`) answers. */
|
|
211
|
+
export interface CronJobListing {
|
|
212
|
+
/** The registered jobs. */
|
|
213
|
+
jobs: CronJobStatus[];
|
|
214
|
+
/**
|
|
215
|
+
* Cron files that did not become jobs: the files that failed to load,
|
|
216
|
+
* plus the jobs in {@link rejected}. Absent when there are none. None of
|
|
217
|
+
* them is scheduled.
|
|
218
|
+
*/
|
|
219
|
+
skipped?: number;
|
|
220
|
+
/** The jobs the scheduler refused, by name, with the reason. */
|
|
221
|
+
rejected?: RejectedCronJob[];
|
|
222
|
+
/** {@link skipped} and {@link rejected}, in a sentence. */
|
|
223
|
+
note?: string;
|
|
224
|
+
}
|
|
187
225
|
export type CronLogLevel = "info" | "error" | "warn";
|
|
188
226
|
/**
|
|
189
227
|
* A single execution log entry stored in the in-memory ring buffer.
|
|
@@ -208,3 +246,16 @@ export interface CronJobLogEntry {
|
|
|
208
246
|
/** Whether this was a manual trigger. */
|
|
209
247
|
manual?: boolean;
|
|
210
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
|
+
}
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
import type { DataDriver } from "../controllers/data_driver.js";
|
|
23
23
|
import type { CollectionConfig } from "./collections.js";
|
|
24
|
-
import type { CollectionRegistryInterface, DatabaseAdmin, InitializedDriver, RealtimeProvider,
|
|
24
|
+
import type { CollectionRegistryInterface, DatabaseAdmin, InitializedDriver, RealtimeProvider, RealtimeSocketOptions, BootstrappedAuth } from "./backend.js";
|
|
25
25
|
import type { HistoryConfig } from "../controllers/client.js";
|
|
26
26
|
/**
|
|
27
27
|
* A `DatabaseAdapter` provides data persistence for Rebase.
|
|
@@ -78,7 +78,7 @@ export interface DatabaseAdapter {
|
|
|
78
78
|
* socket's frames: it serves the same rows, and without them a frame had
|
|
79
79
|
* neither. Forwarded for the same reason `adapter` is.
|
|
80
80
|
*/
|
|
81
|
-
initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, adapter?: import("./auth_adapter.js").AuthAdapter, limits?:
|
|
81
|
+
initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, adapter?: import("./auth_adapter.js").AuthAdapter, limits?: RealtimeSocketOptions): Promise<void> | void;
|
|
82
82
|
/**
|
|
83
83
|
* Ask the database whether it is there, before boot's first real query. See
|
|
84
84
|
* `BackendBootstrapper.verifyConnection` for the contract.
|
|
@@ -171,9 +171,11 @@ export interface DatabaseAdapter {
|
|
|
171
171
|
*/
|
|
172
172
|
getAdmin?(driverResult: InitializedDriver): DatabaseAdmin | undefined;
|
|
173
173
|
/**
|
|
174
|
-
*
|
|
174
|
+
* Never called: the server mounts no route on an adapter's behalf. A
|
|
175
|
+
* driver that needs HTTP routes exports a route factory, and the app
|
|
176
|
+
* mounts it where it chooses.
|
|
175
177
|
*
|
|
176
|
-
*
|
|
178
|
+
* @deprecated Never called. Will be removed in a future release.
|
|
177
179
|
*/
|
|
178
180
|
mountRoutes?(app: unknown, basePath: string, driverResult: InitializedDriver): void;
|
|
179
181
|
/**
|
|
@@ -196,8 +198,29 @@ export interface DatabaseAdapterInitConfig {
|
|
|
196
198
|
* name none when this is the default.
|
|
197
199
|
*/
|
|
198
200
|
dataSourceKey?: string;
|
|
199
|
-
/**
|
|
200
|
-
|
|
201
|
+
/**
|
|
202
|
+
* The backend's shared collection registry. No bundled driver reads it —
|
|
203
|
+
* each builds and returns its own on `InitializedDriver.collectionRegistry`
|
|
204
|
+
* — so a caller opening a driver on its own (a script, a test) leaves it out.
|
|
205
|
+
*/
|
|
206
|
+
collectionRegistry?: CollectionRegistryInterface;
|
|
207
|
+
/**
|
|
208
|
+
* Whether opening the driver may change the database (default `true`).
|
|
209
|
+
*
|
|
210
|
+
* The server's boot owns its schema, so the driver provisions what it needs
|
|
211
|
+
* while it opens: the restricted role requests run as under RLS, the
|
|
212
|
+
* branch-metadata table, channel-history tables, change-capture triggers.
|
|
213
|
+
*
|
|
214
|
+
* `false` opens it with no DDL of any kind — what a script, a REPL or a
|
|
215
|
+
* test that only reads and writes rows wants, and what a process that does
|
|
216
|
+
* not own the schema must ask for. A connection that would bypass
|
|
217
|
+
* row-level security still sends a user's requests through the restricted
|
|
218
|
+
* role; when an earlier boot never created that role, those requests fail
|
|
219
|
+
* rather than run unfiltered. Change capture is not installed
|
|
220
|
+
* (`realtime.provision` reads as `false`); whether change events are
|
|
221
|
+
* consumed is still `realtime.subscribe`.
|
|
222
|
+
*/
|
|
223
|
+
provision?: boolean;
|
|
201
224
|
/**
|
|
202
225
|
* Whether this driver should describe its own schema.
|
|
203
226
|
*
|
|
@@ -251,9 +274,12 @@ export interface DatabaseAdapterInitConfig {
|
|
|
251
274
|
* runtime otherwise refuses to boot without.
|
|
252
275
|
*
|
|
253
276
|
* Absent means both — every caller that predates this field is a
|
|
254
|
-
* single-process deployment, where both are true.
|
|
277
|
+
* single-process deployment, where both are true. `false` is a process
|
|
278
|
+
* that takes no part in realtime at all: nothing installed, nothing
|
|
279
|
+
* consumed, and its writes not announced to the processes that serve
|
|
280
|
+
* realtime (a script, a REPL).
|
|
255
281
|
*/
|
|
256
|
-
realtime?: {
|
|
282
|
+
realtime?: false | {
|
|
257
283
|
/**
|
|
258
284
|
* Consume change events: open the `LISTEN` connection, start CDC or the
|
|
259
285
|
* app-level fallback. False for a process that serves no websockets.
|
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
|
@@ -15,6 +15,7 @@ export * from "./entity_callbacks.js";
|
|
|
15
15
|
export * from "./websockets.js";
|
|
16
16
|
export * from "./backend.js";
|
|
17
17
|
export * from "./schema_editing.js";
|
|
18
|
+
export * from "./collection_patch.js";
|
|
18
19
|
export * from "./channel_bus.js";
|
|
19
20
|
export * from "./data_source.js";
|
|
20
21
|
export * from "./resources.js";
|
|
@@ -27,9 +28,11 @@ export * from "./component_ref.js";
|
|
|
27
28
|
export * from "./auth_adapter.js";
|
|
28
29
|
export * from "./database_adapter.js";
|
|
29
30
|
export * from "./api_keys.js";
|
|
31
|
+
export * from "./scopes.js";
|
|
30
32
|
export * from "./history.js";
|
|
31
33
|
export * from "./postgres_introspection.js";
|
|
32
34
|
export * from "./project_manifest.js";
|
|
33
35
|
export * from "./collection_contract.js";
|
|
34
36
|
export * from "./schema_version.js";
|
|
35
37
|
export * from "./storage_authorize.js";
|
|
38
|
+
export * from "./register.js";
|
|
@@ -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`.
|
|
@@ -132,6 +140,20 @@ export interface RebaseStaticAppConfig {
|
|
|
132
140
|
* loads and every asset 404s: a blank page with no server error. `rebase
|
|
133
141
|
* build` passes it as `REBASE_APP_BASE` and asserts the emitted HTML honours
|
|
134
142
|
* it. Changing this value requires rebuilding the app.
|
|
143
|
+
*
|
|
144
|
+
* **Or a full URL, to give the app a hostname of its own:**
|
|
145
|
+
* `"https://admin.example.com"`, or `"https://admin.example.com/cms"` for a
|
|
146
|
+
* path under it. The app then answers only on that hostname, and every app
|
|
147
|
+
* without one keeps answering on all the others. The URL form is split by
|
|
148
|
+
* {@link parseAppAddress}: the hostname travels as the bundle's
|
|
149
|
+
* {@link RebaseBundleStatic.host}, and the path part is what the rules above
|
|
150
|
+
* (and `REBASE_APP_BASE`) apply to. HTTPS only, with no port, query or
|
|
151
|
+
* fragment — a hostname is served over TLS by the platform, not chosen per
|
|
152
|
+
* request.
|
|
153
|
+
*
|
|
154
|
+
* On Rebase Cloud the deploy registers the hostname on the project. It is
|
|
155
|
+
* live once its DNS records verify, which the deploy prints when they don't
|
|
156
|
+
* yet.
|
|
135
157
|
*/
|
|
136
158
|
path?: string;
|
|
137
159
|
/**
|
|
@@ -191,6 +213,42 @@ export declare const RESERVED_BACKEND_PREFIXES: readonly ["/api", "/health", "/h
|
|
|
191
213
|
* that is looser admits paths that will not.
|
|
192
214
|
*/
|
|
193
215
|
export declare function reservedPrefixFor(path: string): string | undefined;
|
|
216
|
+
/**
|
|
217
|
+
* Where a static app answers: on one hostname or all of them, at a path.
|
|
218
|
+
*
|
|
219
|
+
* `host` absent is every hostname the project is served on.
|
|
220
|
+
*/
|
|
221
|
+
export interface AppAddress {
|
|
222
|
+
host?: string;
|
|
223
|
+
path: string;
|
|
224
|
+
}
|
|
225
|
+
export type AppAddressResult = {
|
|
226
|
+
ok: true;
|
|
227
|
+
address: AppAddress;
|
|
228
|
+
} | {
|
|
229
|
+
ok: false;
|
|
230
|
+
reason: string;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Split a static app's declared `path` into the hostname it claims and the path
|
|
234
|
+
* under it.
|
|
235
|
+
*
|
|
236
|
+
* `"/admin"` is `{ path: "/admin" }`, returned as written: the path rules
|
|
237
|
+
* (leading slash, no `..`, reserved prefixes) belong to the caller, which
|
|
238
|
+
* applies them to the path part of either form. `"https://admin.example.com/cms"`
|
|
239
|
+
* is `{ host: "admin.example.com", path: "/cms" }`.
|
|
240
|
+
*
|
|
241
|
+
* Shared because three parties must read the same value the same way: the CLI
|
|
242
|
+
* while `rebase.json` is edited, the control plane at deploy intake (a bundle can
|
|
243
|
+
* come from any CLI, or none), and the runtime choosing which app answers.
|
|
244
|
+
*/
|
|
245
|
+
export declare function parseAppAddress(value: string): AppAddressResult;
|
|
246
|
+
/**
|
|
247
|
+
* Whether `host` is a public DNS name an app can be given: lowercase labels of
|
|
248
|
+
* letters, digits and hyphens, at least two of them, not an IP address and not
|
|
249
|
+
* `localhost`. Internationalised names arrive here already in punycode.
|
|
250
|
+
*/
|
|
251
|
+
export declare function isValidAppHost(host: string): boolean;
|
|
194
252
|
/**
|
|
195
253
|
* One declared storage source, as authored in `rebase.json`.
|
|
196
254
|
*
|
|
@@ -375,8 +433,22 @@ export interface RebaseBundleEntrypoints {
|
|
|
375
433
|
}
|
|
376
434
|
/** One built static app inside a bundle. */
|
|
377
435
|
export interface RebaseBundleStatic {
|
|
378
|
-
/** Public base path, e.g. `/` or `/admin`. */
|
|
436
|
+
/** Public base path, e.g. `/` or `/admin`. Always a path, never a URL. */
|
|
379
437
|
path: string;
|
|
438
|
+
/**
|
|
439
|
+
* The one hostname this app answers on, lowercase, e.g. `admin.example.com`.
|
|
440
|
+
*
|
|
441
|
+
* Absent means every hostname the project is served on, which is what every
|
|
442
|
+
* app was before this field existed. Present, the app answers only for
|
|
443
|
+
* requests whose `Host` is exactly this — the path part of its address is
|
|
444
|
+
* still `path`. At an equal `path`, an app naming the request's hostname
|
|
445
|
+
* wins over one that does not.
|
|
446
|
+
*
|
|
447
|
+
* A runtime that predates this field ignores it and would mount two apps at
|
|
448
|
+
* the same `path`, so a bundle carrying it must only ever reach a runtime
|
|
449
|
+
* that reads it. The control plane enforces that at intake.
|
|
450
|
+
*/
|
|
451
|
+
host?: string;
|
|
380
452
|
/** Bundle-relative directory holding the built assets. */
|
|
381
453
|
dir: string;
|
|
382
454
|
/** Serve `index.html` for unmatched paths under `path`. */
|
|
@@ -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
|
/**
|
|
@@ -435,6 +461,13 @@ export interface StringProperty extends BaseProperty {
|
|
|
435
461
|
* Drizzle append: `.primaryKey()`
|
|
436
462
|
* UI behavior: Field value cannot be changed after creation.
|
|
437
463
|
*
|
|
464
|
+
* Marking several properties declares one composite key: the table gets a
|
|
465
|
+
* single `PRIMARY KEY (a, b)` constraint (Drizzle: `primaryKey({ columns })`)
|
|
466
|
+
* and a row is addressed by all of its key values, joined in declaration
|
|
467
|
+
* order — `1:::en_US`. A foreign key cannot point at a composite key, so a
|
|
468
|
+
* relation or reference into such a collection is refused by the schema
|
|
469
|
+
* generators.
|
|
470
|
+
*
|
|
438
471
|
* You can set this to `"manual"` for a user-defined ID, or specify a generation strategy:
|
|
439
472
|
* 'uuid' -> Drizzle `.defaultRandom()` (Postgres gen_random_uuid())
|
|
440
473
|
* Or any other string to act as a raw SQL default expression, written either
|
|
@@ -565,6 +598,10 @@ export interface NumberProperty extends BaseProperty {
|
|
|
565
598
|
* Drizzle append: `.primaryKey()`
|
|
566
599
|
* UI behavior: Field value cannot be changed after creation.
|
|
567
600
|
*
|
|
601
|
+
* Marking several properties declares one composite key — see
|
|
602
|
+
* {@link StringProperty.isId}. A column whose value the database does not
|
|
603
|
+
* generate, such as a foreign key that is part of the key, takes `true`.
|
|
604
|
+
*
|
|
568
605
|
* You can set this to `"manual"` for a user-defined ID, or specify a generation strategy:
|
|
569
606
|
* 'increment' -> PostgreSQL `INTEGER GENERATED BY DEFAULT AS IDENTITY`.
|
|
570
607
|
* Or any other string to act as a raw SQL default expression, bare or in
|
|
@@ -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 {
|
|
@@ -94,20 +94,24 @@ export interface BucketOptions extends DeclareOptions {
|
|
|
94
94
|
* is a data leak that nothing errors on.
|
|
95
95
|
*/
|
|
96
96
|
publicRead?: boolean;
|
|
97
|
-
/**
|
|
97
|
+
/**
|
|
98
|
+
* @deprecated Never applied: every object lands at the root of its bucket,
|
|
99
|
+
* whatever this says, so two sources declaring one bucket share one
|
|
100
|
+
* namespace. Give each source a bucket of its own (`S3_BUCKET__<KEY>`,
|
|
101
|
+
* `GCS_BUCKET__<KEY>`, or `STORAGE_PATH__<KEY>` for a local one), or put
|
|
102
|
+
* the folder in each storage property's `storagePath`. The backend warns
|
|
103
|
+
* once at boot when it is set. Will be removed in a future release.
|
|
104
|
+
*/
|
|
98
105
|
prefix?: string;
|
|
99
106
|
/**
|
|
100
107
|
* Serve unqualified uploads — a storage property with no `storageSource` —
|
|
101
108
|
* from this bucket.
|
|
102
109
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* default is dropped in production and the promotion is not. So it is now
|
|
109
|
-
* a boot error, and this is one of the two ways to answer it — the other
|
|
110
|
-
* being `bucket()`, which declares the default bucket itself.
|
|
110
|
+
* Without it, declaring `bucket("media")` leaves the default source where
|
|
111
|
+
* it was: on its own `STORAGE_PATH` / `./uploads`, or the bucket the
|
|
112
|
+
* unsuffixed `S3_BUCKET` / `GCS_BUCKET` names. Mark a named bucket to make
|
|
113
|
+
* it the default instead; the files the default held before stay where
|
|
114
|
+
* they are and are no longer served through it.
|
|
111
115
|
*/
|
|
112
116
|
default?: boolean;
|
|
113
117
|
/**
|
|
@@ -167,11 +171,13 @@ export interface TopicOptions extends DeclareOptions {
|
|
|
167
171
|
*
|
|
168
172
|
* `attempt` counts from 1. Worth branching on: the first delivery and the
|
|
169
173
|
* fourth are the same call, but the fourth is where it is worth logging loudly.
|
|
174
|
+
* `signal` is aborted when the attempt runs past the job queue's `timeoutMs`.
|
|
170
175
|
*/
|
|
171
176
|
export type TopicHandler<T> = (event: T, context: {
|
|
172
177
|
attempt: number;
|
|
173
178
|
topic: string;
|
|
174
179
|
subscription: string;
|
|
180
|
+
signal: AbortSignal;
|
|
175
181
|
}) => Promise<void> | void;
|
|
176
182
|
/** A declared subscription, as recorded in the graph and wired at boot. */
|
|
177
183
|
export interface TopicSubscription<T = unknown> {
|
|
@@ -279,11 +285,15 @@ export interface QueueOptions extends DeclareOptions {
|
|
|
279
285
|
/** Attempts before a job is left failed. Default 5. */
|
|
280
286
|
maxAttempts?: number;
|
|
281
287
|
}
|
|
282
|
-
/**
|
|
288
|
+
/**
|
|
289
|
+
* What a queue's handler receives. `attempt` counts from 1; `signal` is
|
|
290
|
+
* aborted when the attempt runs past the job queue's `timeoutMs`.
|
|
291
|
+
*/
|
|
283
292
|
export type QueueHandler<T> = (payload: T, context: {
|
|
284
293
|
attempt: number;
|
|
285
294
|
queue: string;
|
|
286
295
|
jobId: string;
|
|
296
|
+
signal: AbortSignal;
|
|
287
297
|
}) => Promise<void> | void;
|
|
288
298
|
/** Per-job options at enqueue time. */
|
|
289
299
|
export interface QueueEnqueueOptions {
|