@rebasepro/types 0.12.1-canary.gf5f1d39 → 0.13.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/controllers/client.d.ts +41 -12
- package/dist/controllers/data_driver.d.ts +11 -0
- package/dist/index.es.js +41 -6
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +2 -2
- package/dist/types/backend.d.ts +27 -0
- package/dist/types/chips.d.ts +12 -1
- package/dist/types/cron.d.ts +31 -0
- package/dist/types/database_adapter.d.ts +35 -1
- package/dist/types/policy.d.ts +34 -2
- package/dist/types/project_manifest.d.ts +15 -0
- package/dist/types/relations.d.ts +51 -0
- package/dist/users/user.d.ts +2 -2
- package/package.json +4 -4
- package/src/controllers/client.ts +44 -13
- package/src/controllers/data_driver.ts +11 -0
- package/src/types/admin_block.ts +2 -1
- package/src/types/backend.ts +30 -0
- package/src/types/chips.ts +14 -1
- package/src/types/cron.ts +32 -0
- package/src/types/database_adapter.ts +41 -0
- package/src/types/policy.ts +38 -2
- package/src/types/project_manifest.ts +15 -0
- package/src/types/relations.ts +51 -0
- package/src/users/user.ts +2 -2
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
*
|
|
30
30
|
* @group Models
|
|
31
31
|
*/
|
|
32
|
-
export declare const ADMIN_COLLECTION_KEYS: readonly ["Actions", "additionalFields", "alwaysApplyDefaultValues", "components", "defaultEntityAction", "defaultFilter", "defaultSelectedView", "defaultSize", "defaultViewMode", "disableDefaultActions", "enabledViews", "entityActions", "entityViews", "exportable", "filterPresets", "fixedFilter", "formAutoSave", "formView", "group", "hideFromNavigation", "hideIdFromCollection", "hideIdFromForm", "icon", "includeJsonView", "inlineEditing", "kanban", "listProperties", "localChangesBackup", "openEntityMode", "orderProperty", "pagination", "previewProperties", "propertiesOrder", "selectionController", "selectionEnabled", "sideDialogWidth", "sort", "titleProperty"];
|
|
32
|
+
export declare const ADMIN_COLLECTION_KEYS: readonly ["Actions", "additionalFields", "alwaysApplyDefaultValues", "components", "defaultEntityAction", "defaultFilter", "defaultSelectedView", "defaultSize", "defaultViewMode", "disableDefaultActions", "enabledViews", "entityActions", "entityViews", "exportable", "filterPresets", "fixedFilter", "form", "formAutoSave", "formView", "group", "hideFromNavigation", "hideIdFromCollection", "hideIdFromForm", "icon", "includeJsonView", "inlineEditing", "kanban", "listProperties", "localChangesBackup", "openEntityMode", "orderProperty", "pagination", "previewProperties", "propertiesOrder", "selectionController", "selectionEnabled", "sideDialogWidth", "sort", "titleProperty"];
|
|
33
33
|
/** A key of a collection's `admin` block. @group Models */
|
|
34
34
|
export type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];
|
|
35
35
|
/**
|
|
@@ -49,6 +49,6 @@ export type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];
|
|
|
49
49
|
*
|
|
50
50
|
* @group Models
|
|
51
51
|
*/
|
|
52
|
-
export declare const ADMIN_PROPERTY_KEYS: readonly ["canAddElements", "clearable", "columnWidth", "customProps", "disabled", "expanded", "Field", "Filter", "filterOperators", "fixedFilter", "hideFromCollection", "includeEntityLink", "includeId", "markdown", "minimalistView", "multiline", "Preview", "previewAsTag", "previewProperties", "readOnly", "sortable", "
|
|
52
|
+
export declare const ADMIN_PROPERTY_KEYS: readonly ["canAddElements", "clearable", "columnWidth", "customProps", "disabled", "expanded", "Field", "Filter", "filterOperators", "fixedFilter", "hideFromCollection", "includeEntityLink", "includeId", "markdown", "minimalistView", "multiline", "Preview", "previewAsTag", "previewProperties", "readOnly", "sortable", "span", "spreadChildren", "urlPreview", "widget"];
|
|
53
53
|
/** A key of a property's `admin` block. @group Models */
|
|
54
54
|
export type AdminPropertyKey = typeof ADMIN_PROPERTY_KEYS[number];
|
package/dist/types/backend.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CollectionConfig, FilterValues, WhereFilterOp } from "./collections";
|
|
2
|
+
import type { LogicalCondition } from "../controllers/data";
|
|
2
3
|
import type { AuthAdapter } from "./auth_adapter";
|
|
3
4
|
import type { HistoryConfig } from "../controllers/client";
|
|
4
5
|
import type { ChannelBusSetting } from "./channel_bus";
|
|
@@ -58,6 +59,13 @@ export interface SearchOptions<M extends Record<string, unknown> = Record<string
|
|
|
58
59
|
*/
|
|
59
60
|
export interface CountOptions<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
60
61
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
62
|
+
/**
|
|
63
|
+
* An `or(...)`/`and(...)` group, alongside `filter`.
|
|
64
|
+
*
|
|
65
|
+
* Counted as well as fetched, or `total` describes a different set of rows
|
|
66
|
+
* from the one that was served — the same reason `filter` is here.
|
|
67
|
+
*/
|
|
68
|
+
logical?: LogicalCondition;
|
|
61
69
|
searchString?: string;
|
|
62
70
|
databaseId?: string;
|
|
63
71
|
}
|
|
@@ -611,6 +619,25 @@ export interface BackendBootstrapper {
|
|
|
611
619
|
ensureCollectionSchema?(collections: unknown[], driverResult: InitializedDriver, log?: (message: string) => void): Promise<{
|
|
612
620
|
applied: number;
|
|
613
621
|
}>;
|
|
622
|
+
/**
|
|
623
|
+
* Apply the collections' row-level-security policies, additively and
|
|
624
|
+
* idempotently — the companion to {@link ensureCollectionSchema}.
|
|
625
|
+
*
|
|
626
|
+
* That method creates the tables; a table with RLS disabled and no policies
|
|
627
|
+
* is not servable, because authenticated requests run as a restricted role:
|
|
628
|
+
* a read with no `SELECT` policy returns nothing (a public collection
|
|
629
|
+
* answers 401) and a write with no `INSERT`/`UPDATE` policy is denied. The
|
|
630
|
+
* `db push` CLI applies these from the same collections, but it cannot reach
|
|
631
|
+
* a managed tenant's in-cluster database — the runtime, already connected,
|
|
632
|
+
* is the only thing that can.
|
|
633
|
+
*
|
|
634
|
+
* MUST be idempotent (re-run on every boot) and MUST NOT be destructive.
|
|
635
|
+
* Runs after auth initialization, because the generated policies call the
|
|
636
|
+
* `auth.*` helper functions and `CREATE POLICY` validates they exist.
|
|
637
|
+
*/
|
|
638
|
+
ensureCollectionPolicies?(collections: unknown[], driverResult: InitializedDriver, log?: (message: string) => void): Promise<{
|
|
639
|
+
applied: number;
|
|
640
|
+
}>;
|
|
614
641
|
/**
|
|
615
642
|
* Initialize WebSocket server for realtime operations.
|
|
616
643
|
*/
|
package/dist/types/chips.d.ts
CHANGED
|
@@ -2,4 +2,15 @@ export type ColorScheme = {
|
|
|
2
2
|
color: string;
|
|
3
3
|
text: string;
|
|
4
4
|
};
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* The hues a chip can take.
|
|
7
|
+
*
|
|
8
|
+
* Kept in step with `CHIP_HUES` in `@rebasepro/ui` by hand: config types cannot
|
|
9
|
+
* depend on the component library. A hue added there needs adding here too.
|
|
10
|
+
*/
|
|
11
|
+
export type ColorHue = "blue" | "cyan" | "teal" | "green" | "yellow" | "orange" | "red" | "pink" | "purple" | "gray" | "indigo" | "violet" | "fuchsia" | "rose" | "emerald";
|
|
12
|
+
/**
|
|
13
|
+
* How light or saturated a chip is. A hue on its own means `Lighter`.
|
|
14
|
+
*/
|
|
15
|
+
export type ColorTone = "Lighter" | "Light" | "Dark" | "Darker";
|
|
16
|
+
export type ColorKey = ColorHue | `${ColorHue}${ColorTone}`;
|
package/dist/types/cron.d.ts
CHANGED
|
@@ -26,6 +26,37 @@ export interface CronJobDefinition {
|
|
|
26
26
|
* considered timed-out. Default: 300 (5 min).
|
|
27
27
|
*/
|
|
28
28
|
timeoutSeconds?: number;
|
|
29
|
+
/**
|
|
30
|
+
* How far back to look, on startup, for a slot that elapsed while no
|
|
31
|
+
* instance was holding a timer for it. Off by default.
|
|
32
|
+
*
|
|
33
|
+
* The scheduler drives jobs with in-process `setTimeout` and computes the
|
|
34
|
+
* next slot from *now* on every boot, so a slot only fires if some instance
|
|
35
|
+
* happened to be alive and ticking when it came round. That is not a
|
|
36
|
+
* scale-to-zero problem: a platform that recycles containers — Cloud Run
|
|
37
|
+
* rotating an instance under `--min-instances 1`, a rolling deploy, a crash
|
|
38
|
+
* loop — drops any slot that falls inside the changeover, and the
|
|
39
|
+
* replacement schedules the slot *after* it. The run is skipped in silence.
|
|
40
|
+
*
|
|
41
|
+
* Set this to a window comfortably wider than a restart (a few minutes for
|
|
42
|
+
* a frequent job; an hour or more for a daily one) and startup will run a
|
|
43
|
+
* slot it finds unclaimed inside that window.
|
|
44
|
+
*
|
|
45
|
+
* Two deliberate limits:
|
|
46
|
+
*
|
|
47
|
+
* - **Only the most recent missed slot runs.** Booting after a six-hour
|
|
48
|
+
* outage catches an hourly job up once, not six times. Catch-up exists to
|
|
49
|
+
* stop a run going missing, not to replay history.
|
|
50
|
+
* - **A claims-capable store is required.** Catch-up is skipped entirely
|
|
51
|
+
* when the store has no `tryClaimRun` (or no store is attached), because
|
|
52
|
+
* the claim is the only thing that distinguishes "this slot never ran"
|
|
53
|
+
* from "this slot already ran on the instance I am replacing". Without
|
|
54
|
+
* it, an instance recycled every 30 minutes would re-run the same hourly
|
|
55
|
+
* job every time it booted.
|
|
56
|
+
*
|
|
57
|
+
* @example catchUpWindowSeconds: 3600 // daily job: tolerate an hour of downtime
|
|
58
|
+
*/
|
|
59
|
+
catchUpWindowSeconds?: number;
|
|
29
60
|
/**
|
|
30
61
|
* The handler function executed on each tick.
|
|
31
62
|
* Receives a context object with the data driver and logger.
|
|
@@ -65,8 +65,42 @@ export interface DatabaseAdapter {
|
|
|
65
65
|
} | undefined>;
|
|
66
66
|
/**
|
|
67
67
|
* Initialize WebSocket server for realtime operations.
|
|
68
|
+
*
|
|
69
|
+
* `adapter` is the configured AuthAdapter, if any. It is what makes the
|
|
70
|
+
* socket secure by default: an implementation that receives one requires
|
|
71
|
+
* authentication regardless of whether a local `jwtSecret` exists. The
|
|
72
|
+
* parameter was missing from this signature while the caller in `init.ts`
|
|
73
|
+
* already passed it, so it was dropped at every adapter that routed through
|
|
74
|
+
* here — turning an adapter-authenticated server's socket into one that
|
|
75
|
+
* accepted every client as already authenticated.
|
|
76
|
+
*/
|
|
77
|
+
initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, adapter?: import("./auth_adapter").AuthAdapter): Promise<void> | void;
|
|
78
|
+
/**
|
|
79
|
+
* Bring the database's collection tables up to date, additively — the boot
|
|
80
|
+
* companion to `db push`. See `BackendBootstrapper.ensureCollectionSchema`
|
|
81
|
+
* for the contract (create-only; never drop, narrow, or rewrite).
|
|
82
|
+
*
|
|
83
|
+
* Optional, and MUST be forwarded by any wrapper that turns this adapter
|
|
84
|
+
* into a `BackendBootstrapper`: the runtime calls it through the bootstrapper
|
|
85
|
+
* at boot, and a wrapper that silently omits it leaves a managed tenant
|
|
86
|
+
* 500ing every data route with no create step ever having run.
|
|
87
|
+
*/
|
|
88
|
+
ensureCollectionSchema?(collections: unknown[], driverResult: InitializedDriver, log?: (message: string) => void): Promise<{
|
|
89
|
+
applied: number;
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* Apply the collections' RLS policies (ENABLE ROW LEVEL SECURITY + the
|
|
93
|
+
* `securityRules` compiled to `CREATE POLICY`) — the boot companion to the
|
|
94
|
+
* policy half of `db push`. Idempotent; see
|
|
95
|
+
* `BackendBootstrapper.ensureCollectionPolicies`.
|
|
96
|
+
*
|
|
97
|
+
* Same forwarding requirement as `ensureCollectionSchema`: without the
|
|
98
|
+
* policies, tables exist but every user-context read is denied (a public
|
|
99
|
+
* collection answers 401).
|
|
68
100
|
*/
|
|
69
|
-
|
|
101
|
+
ensureCollectionPolicies?(collections: unknown[], driverResult: InitializedDriver, log?: (message: string) => void): Promise<{
|
|
102
|
+
applied: number;
|
|
103
|
+
}>;
|
|
70
104
|
/**
|
|
71
105
|
* Return admin capabilities for this database (SQL editor, schema browser, branching).
|
|
72
106
|
*/
|
package/dist/types/policy.d.ts
CHANGED
|
@@ -29,12 +29,44 @@ export type PolicyExpression = TruePolicyExpression | FalsePolicyExpression | An
|
|
|
29
29
|
*
|
|
30
30
|
* The consequence for policy authors is that **`auth.uid() IS NOT NULL` is a
|
|
31
31
|
* tautology on the user path** — it is true for anonymous visitors too. Use
|
|
32
|
-
* {@link policy.authenticated}
|
|
33
|
-
*
|
|
32
|
+
* {@link policy.authenticated} to mean "signed in", and
|
|
33
|
+
* {@link policy.serverContext} to mean "the trusted server context". Do not
|
|
34
|
+
* hand-write the comparison: see {@link ANONYMOUS_USER_IDS} for why one
|
|
35
|
+
* literal is not enough.
|
|
34
36
|
*
|
|
35
37
|
* @group Models
|
|
36
38
|
*/
|
|
37
39
|
export declare const ANONYMOUS_USER_ID = "anonymous";
|
|
40
|
+
/**
|
|
41
|
+
* Every uid that has ever meant "nobody is signed in" — newest first.
|
|
42
|
+
*
|
|
43
|
+
* There are two because there were two. The types, the policy compiler, the
|
|
44
|
+
* JavaScript evaluator and the linter were all built on
|
|
45
|
+
* {@link ANONYMOUS_USER_ID}, while the request path scoped unauthenticated
|
|
46
|
+
* callers as `'anon'` — so `policy.authenticated()`, which compiled to
|
|
47
|
+
* `auth.uid() <> 'anonymous'`, was *true* for an anonymous visitor. The
|
|
48
|
+
* sanctioned way to write "signed in" granted to everyone, and the linter
|
|
49
|
+
* flagged the spelling that actually worked as a foreign convention.
|
|
50
|
+
*
|
|
51
|
+
* The request path now reports {@link ANONYMOUS_USER_ID}. `'anon'` stays here
|
|
52
|
+
* because policies outlive the server that generated them: a database still
|
|
53
|
+
* holding policies from before the fix, or a project whose server has not been
|
|
54
|
+
* upgraded yet, must not become a grant in either direction. Compile against
|
|
55
|
+
* this list, not against a single literal.
|
|
56
|
+
*
|
|
57
|
+
* No real user id is ever one of these, so a match is always "not signed in".
|
|
58
|
+
*
|
|
59
|
+
* @group Models
|
|
60
|
+
*/
|
|
61
|
+
export declare const ANONYMOUS_USER_IDS: readonly string[];
|
|
62
|
+
/**
|
|
63
|
+
* Whether a uid stands for "no one is signed in", in any spelling rebase has
|
|
64
|
+
* used. `null`/`undefined` is the trusted server context, not an anonymous
|
|
65
|
+
* caller, and is therefore **not** anonymous — see {@link ANONYMOUS_USER_ID}.
|
|
66
|
+
*
|
|
67
|
+
* @group Models
|
|
68
|
+
*/
|
|
69
|
+
export declare function isAnonymousUid(uid: string | null | undefined): boolean;
|
|
38
70
|
/** Always allows. Compiles to `true`. @group Models */
|
|
39
71
|
export interface TruePolicyExpression {
|
|
40
72
|
kind: "true";
|
|
@@ -192,6 +192,21 @@ export interface RebaseProjectManifest {
|
|
|
192
192
|
* common project and must not be required to say so.
|
|
193
193
|
*/
|
|
194
194
|
storage?: Record<string, RebaseStorageSourceConfig>;
|
|
195
|
+
/**
|
|
196
|
+
* Repository-wide opt-out from anonymous CLI usage sharing.
|
|
197
|
+
*
|
|
198
|
+
* **Only `false` does anything.** It suppresses sharing for everyone who
|
|
199
|
+
* clones this repository, overriding each developer's own opt-in — an
|
|
200
|
+
* organisation setting policy for work done on its behalf, the same shape
|
|
201
|
+
* as a committed `.npmrc`.
|
|
202
|
+
*
|
|
203
|
+
* `true` is deliberately ignored, and the CLI says so rather than obeying
|
|
204
|
+
* quietly. This file is committed, so a `true` here would be one developer
|
|
205
|
+
* answering a privacy question for every colleague who later clones the
|
|
206
|
+
* repo — consent by proxy, which is the exact thing opt-in exists to
|
|
207
|
+
* prevent. Individuals opt in with `rebase telemetry enable`.
|
|
208
|
+
*/
|
|
209
|
+
telemetry?: boolean;
|
|
195
210
|
}
|
|
196
211
|
/**
|
|
197
212
|
* The per-checkout project link.
|
|
@@ -73,6 +73,15 @@ export interface HasOneRelation extends RelationBase {
|
|
|
73
73
|
* Defaults to `<thisCollection>_id`.
|
|
74
74
|
*/
|
|
75
75
|
foreignKeyOnTarget?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Column on **this** collection's table whose value `foreignKeyOnTarget`
|
|
78
|
+
* holds. Defaults to this collection's primary key.
|
|
79
|
+
*
|
|
80
|
+
* Set it when the two sides are joined on a natural key rather than on the
|
|
81
|
+
* row id — an external identity id, a SKU, a tenant slug. See
|
|
82
|
+
* {@link HasManyRelation.sourceKey}, which this mirrors.
|
|
83
|
+
*/
|
|
84
|
+
sourceKey?: string;
|
|
76
85
|
}
|
|
77
86
|
/**
|
|
78
87
|
* The target holds the foreign key, and many target rows point back. The
|
|
@@ -90,6 +99,30 @@ export interface HasManyRelation extends RelationBase {
|
|
|
90
99
|
* Defaults to `<thisCollection>_id`.
|
|
91
100
|
*/
|
|
92
101
|
foreignKeyOnTarget?: string;
|
|
102
|
+
/**
|
|
103
|
+
* Column on **this** collection's table whose value `foreignKeyOnTarget`
|
|
104
|
+
* holds. Defaults to this collection's primary key.
|
|
105
|
+
*
|
|
106
|
+
* The mirror of `localKey` on {@link BelongsToRelation}: that one names the
|
|
107
|
+
* column this side reads from, this one names the column the other side
|
|
108
|
+
* points at. Without it the pair can only be joined on the row id, which
|
|
109
|
+
* makes a natural-key link — `auth_user_id ↔ auth_user_id`, a SKU, a tenant
|
|
110
|
+
* slug — inexpressible as `hasMany`, and it has to drop to the read-only
|
|
111
|
+
* `via`.
|
|
112
|
+
*
|
|
113
|
+
* The column must be unique: the link addresses one source row per value,
|
|
114
|
+
* and Postgres will not accept a foreign key against a non-unique column.
|
|
115
|
+
*
|
|
116
|
+
* ```ts
|
|
117
|
+
* applications: {
|
|
118
|
+
* kind: "hasMany",
|
|
119
|
+
* target: () => talentApplications,
|
|
120
|
+
* sourceKey: "auth_user_id",
|
|
121
|
+
* foreignKeyOnTarget: "auth_user_id"
|
|
122
|
+
* }
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
sourceKey?: string;
|
|
93
126
|
}
|
|
94
127
|
/**
|
|
95
128
|
* Both sides hold many, through a junction table. The target rows are shared,
|
|
@@ -223,6 +256,8 @@ export interface ResolvedHasOne extends ResolvedRelationBase {
|
|
|
223
256
|
shared: false;
|
|
224
257
|
/** Column on the target's table. */
|
|
225
258
|
foreignKeyOnTarget: string;
|
|
259
|
+
/** @see ResolvedHasMany.sourceKey */
|
|
260
|
+
sourceKey?: string;
|
|
226
261
|
}
|
|
227
262
|
/** @group Models */
|
|
228
263
|
export interface ResolvedHasMany extends ResolvedRelationBase {
|
|
@@ -232,6 +267,22 @@ export interface ResolvedHasMany extends ResolvedRelationBase {
|
|
|
232
267
|
shared: false;
|
|
233
268
|
/** Column on the target's table. */
|
|
234
269
|
foreignKeyOnTarget: string;
|
|
270
|
+
/**
|
|
271
|
+
* Column on the source's table that `foreignKeyOnTarget` points at, or
|
|
272
|
+
* `undefined` for the source's primary key.
|
|
273
|
+
*
|
|
274
|
+
* The one optional field on a resolved relation, and deliberately so. Every
|
|
275
|
+
* other default is filled in here because it can be: a table name and a
|
|
276
|
+
* column name are derivable from the relation and its two endpoints alone.
|
|
277
|
+
* The primary key is not — this driver resolves it from `isId`, then the
|
|
278
|
+
* Drizzle schema, then a column named `id`, and the middle tier does not
|
|
279
|
+
* exist at resolution time.
|
|
280
|
+
*
|
|
281
|
+
* So `undefined` is a sentinel with exactly one meaning, not a field a
|
|
282
|
+
* consumer is invited to guess at. Read it through `sourceKeyField()`,
|
|
283
|
+
* which is the only place that turns it into a column name.
|
|
284
|
+
*/
|
|
285
|
+
sourceKey?: string;
|
|
235
286
|
}
|
|
236
287
|
/** @group Models */
|
|
237
288
|
export interface ResolvedManyToMany extends ResolvedRelationBase {
|
package/dist/users/user.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* The canonical representation of an authenticated user in the Rebase ecosystem.
|
|
3
3
|
*
|
|
4
4
|
* Used by {@link AuthController}, collections, callbacks, and both the
|
|
5
|
-
* `@rebasepro/client` and `@rebasepro/app` packages.
|
|
6
|
-
*
|
|
5
|
+
* `@rebasepro/client` and `@rebasepro/app` packages. It is the only user type
|
|
6
|
+
* those packages export — the `RebaseUser` / `UserInfo` aliases are gone.
|
|
7
7
|
*
|
|
8
8
|
* **Backend-managed fields** (`uid`, `email`, `roles`, `metadata`, `createdAt`)
|
|
9
9
|
* are populated by the server. **Client-visible fields** (`displayName`,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/types",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"description": "Rebase type definitions — shared interfaces and controller types",
|
|
6
6
|
"funding": {
|
|
7
7
|
"url": "https://github.com/sponsors/rebaseco"
|
|
@@ -39,14 +39,14 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/jest": "^30.0.0",
|
|
42
|
-
"@types/node": "^
|
|
42
|
+
"@types/node": "^26.1.2",
|
|
43
43
|
"@types/object-hash": "^3.0.6",
|
|
44
44
|
"@types/react-measure": "^2.0.12",
|
|
45
45
|
"hono": "^4.12.27",
|
|
46
46
|
"jest": "^30.4.2",
|
|
47
|
-
"ts-jest": "^29.4.
|
|
47
|
+
"ts-jest": "^29.4.12",
|
|
48
48
|
"typescript": "^6.0.3",
|
|
49
|
-
"vite": "^8.
|
|
49
|
+
"vite": "^8.1.5"
|
|
50
50
|
},
|
|
51
51
|
"peerDependencies": {
|
|
52
52
|
"hono": "^4.12.27"
|
|
@@ -94,6 +94,21 @@ export interface AuthClient {
|
|
|
94
94
|
* Manually refresh the session token
|
|
95
95
|
*/
|
|
96
96
|
refreshSession(): Promise<RebaseSession>;
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Whether a session could exist that this client has not loaded yet.
|
|
100
|
+
*
|
|
101
|
+
* `false` means the only way this client can hold a session is an explicit
|
|
102
|
+
* sign-in during this page's lifetime: it neither persists sessions nor
|
|
103
|
+
* carries an httpOnly auth cookie, so there is nothing on disk or in the
|
|
104
|
+
* browser to restore from. A caller that would otherwise probe the server
|
|
105
|
+
* — `getUser()` on mount, say — can skip it, because the answer is already
|
|
106
|
+
* known and the request can only ever fail.
|
|
107
|
+
*
|
|
108
|
+
* Optional so that alternative {@link AuthClient} implementations need not
|
|
109
|
+
* supply it; treat a missing implementation as "unknown, go ahead and ask".
|
|
110
|
+
*/
|
|
111
|
+
canRestoreSession?: () => boolean;
|
|
97
112
|
}
|
|
98
113
|
|
|
99
114
|
// ─── Admin API ───────────────────────────────────────────────────────────────
|
|
@@ -321,6 +336,17 @@ export interface RebaseClient<DB = unknown> {
|
|
|
321
336
|
/** Base HTTP URL of the backend server */
|
|
322
337
|
baseUrl?: string;
|
|
323
338
|
|
|
339
|
+
/**
|
|
340
|
+
* The path every API route is mounted under, appended to {@link baseUrl}.
|
|
341
|
+
*
|
|
342
|
+
* `"/api"` unless the backend was configured with a different `basePath`
|
|
343
|
+
* and the client told to match. Exposed because code that builds a URL by
|
|
344
|
+
* hand — rather than going through the client's own methods — otherwise has
|
|
345
|
+
* to guess, and guessing `/api` is wrong for exactly the projects that set
|
|
346
|
+
* the option.
|
|
347
|
+
*/
|
|
348
|
+
apiPath?: string;
|
|
349
|
+
|
|
324
350
|
/** WebSocket client for realtime subscriptions */
|
|
325
351
|
ws?: RebaseWebSocket;
|
|
326
352
|
|
|
@@ -356,20 +382,14 @@ export interface RebaseClient<DB = unknown> {
|
|
|
356
382
|
* the admin-scoped {@link dataAsAdmin} accessor, raw {@link sql}, and the
|
|
357
383
|
* {@link email} service are all present (non-optional).
|
|
358
384
|
*
|
|
359
|
-
* **Trust levels.**
|
|
360
|
-
*
|
|
361
|
-
*
|
|
362
|
-
*
|
|
363
|
-
* (`c.var.driver`)
|
|
385
|
+
* **Trust levels.** {@link dataAsAdmin} is the admin-scoped, **RLS-bypassing**
|
|
386
|
+
* driver, and it is the only name for it here — the `data` alias that used to
|
|
387
|
+
* sit beside it is deliberately `Omit`ted from {@link RebaseClient} so the
|
|
388
|
+
* privilege has to be spelled out at every call site. For user-scoped queries
|
|
389
|
+
* inside a request handler use the request-scoped driver (`c.var.driver`)
|
|
390
|
+
* instead — never `dataAsAdmin`.
|
|
364
391
|
*/
|
|
365
|
-
export interface RebaseServerClient<DB = unknown> extends RebaseClient<DB> {
|
|
366
|
-
/**
|
|
367
|
-
* @deprecated On the server, prefer {@link dataAsAdmin} for admin scope or
|
|
368
|
-
* the request-scoped driver (`c.var.driver`) for user scope. This alias
|
|
369
|
-
* points at the same admin-scoped, RLS-bypassing accessor as `dataAsAdmin`.
|
|
370
|
-
*/
|
|
371
|
-
data: RebaseSdkData<DB>;
|
|
372
|
-
|
|
392
|
+
export interface RebaseServerClient<DB = unknown> extends Omit<RebaseClient<DB>, "data"> {
|
|
373
393
|
/**
|
|
374
394
|
* Admin-scoped, **RLS-bypassing** data accessor. Always present server-side.
|
|
375
395
|
* See {@link RebaseClient.dataAsAdmin} for the full safety contract.
|
|
@@ -439,6 +459,17 @@ export interface RebaseBrowserClient<DB = unknown> {
|
|
|
439
459
|
/** Base HTTP URL of the backend server */
|
|
440
460
|
baseUrl?: string;
|
|
441
461
|
|
|
462
|
+
/**
|
|
463
|
+
* The path every API route is mounted under, appended to {@link baseUrl}.
|
|
464
|
+
*
|
|
465
|
+
* `"/api"` unless the backend was configured with a different `basePath`
|
|
466
|
+
* and the client told to match. Exposed because code that builds a URL by
|
|
467
|
+
* hand — rather than going through the client's own methods — otherwise has
|
|
468
|
+
* to guess, and guessing `/api` is wrong for exactly the projects that set
|
|
469
|
+
* the option.
|
|
470
|
+
*/
|
|
471
|
+
apiPath?: string;
|
|
472
|
+
|
|
442
473
|
/** WebSocket client for realtime subscriptions */
|
|
443
474
|
ws?: RebaseWebSocket;
|
|
444
475
|
|
|
@@ -2,6 +2,7 @@ import type { CollectionRegistryController } from "./collection_registry";
|
|
|
2
2
|
import type { EntityStatus, EntityValues } from "../types/entities";
|
|
3
3
|
import type { CollectionConfig, FilterValues } from "../types/collections";
|
|
4
4
|
import type { RebaseCallContext } from "../call_context";
|
|
5
|
+
import type { LogicalCondition } from "./data";
|
|
5
6
|
|
|
6
7
|
|
|
7
8
|
/**
|
|
@@ -103,6 +104,14 @@ export interface FetchCollectionProps<M extends Record<string, unknown> = Record
|
|
|
103
104
|
path: string;
|
|
104
105
|
collection?: CollectionConfig<M>;
|
|
105
106
|
filter?: FilterValues<Extract<keyof M, string>>,
|
|
107
|
+
/**
|
|
108
|
+
* An `or(...)`/`and(...)` group, applied alongside `filter`.
|
|
109
|
+
*
|
|
110
|
+
* The REST layer parsed `?or=` into this and then had nowhere to put it, so
|
|
111
|
+
* the group was dropped and the read ran unfiltered — returning every row
|
|
112
|
+
* the caller's policies allowed rather than the ones they asked for.
|
|
113
|
+
*/
|
|
114
|
+
logical?: LogicalCondition;
|
|
106
115
|
limit?: number;
|
|
107
116
|
offset?: number;
|
|
108
117
|
startAfter?: unknown;
|
|
@@ -366,6 +375,8 @@ export interface RestFetchService {
|
|
|
366
375
|
collectionPath: string,
|
|
367
376
|
options?: {
|
|
368
377
|
filter?: FilterValues<string>;
|
|
378
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
379
|
+
logical?: LogicalCondition;
|
|
369
380
|
orderBy?: string;
|
|
370
381
|
order?: "desc" | "asc";
|
|
371
382
|
limit?: number;
|
package/src/types/admin_block.ts
CHANGED
|
@@ -47,6 +47,7 @@ export const ADMIN_COLLECTION_KEYS = [
|
|
|
47
47
|
"exportable",
|
|
48
48
|
"filterPresets",
|
|
49
49
|
"fixedFilter",
|
|
50
|
+
"form",
|
|
50
51
|
"formAutoSave",
|
|
51
52
|
"formView",
|
|
52
53
|
"group",
|
|
@@ -113,10 +114,10 @@ export const ADMIN_PROPERTY_KEYS = [
|
|
|
113
114
|
"previewProperties",
|
|
114
115
|
"readOnly",
|
|
115
116
|
"sortable",
|
|
117
|
+
"span",
|
|
116
118
|
"spreadChildren",
|
|
117
119
|
"urlPreview",
|
|
118
120
|
"widget",
|
|
119
|
-
"widthPercentage"
|
|
120
121
|
] as const;
|
|
121
122
|
|
|
122
123
|
/** A key of a property's `admin` block. @group Models */
|
package/src/types/backend.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CollectionConfig, FilterValues, WhereFilterOp } from "./collections";
|
|
2
|
+
import type { LogicalCondition } from "../controllers/data";
|
|
2
3
|
import type { AuthAdapter } from "./auth_adapter";
|
|
3
4
|
import type { HistoryConfig } from "../controllers/client";
|
|
4
5
|
import type { ChannelBusSetting } from "./channel_bus";
|
|
@@ -73,6 +74,13 @@ export interface SearchOptions<M extends Record<string, unknown> = Record<string
|
|
|
73
74
|
*/
|
|
74
75
|
export interface CountOptions<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
75
76
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
77
|
+
/**
|
|
78
|
+
* An `or(...)`/`and(...)` group, alongside `filter`.
|
|
79
|
+
*
|
|
80
|
+
* Counted as well as fetched, or `total` describes a different set of rows
|
|
81
|
+
* from the one that was served — the same reason `filter` is here.
|
|
82
|
+
*/
|
|
83
|
+
logical?: LogicalCondition;
|
|
76
84
|
searchString?: string;
|
|
77
85
|
databaseId?: string;
|
|
78
86
|
}
|
|
@@ -798,6 +806,28 @@ export interface BackendBootstrapper {
|
|
|
798
806
|
log?: (message: string) => void
|
|
799
807
|
): Promise<{ applied: number }>;
|
|
800
808
|
|
|
809
|
+
/**
|
|
810
|
+
* Apply the collections' row-level-security policies, additively and
|
|
811
|
+
* idempotently — the companion to {@link ensureCollectionSchema}.
|
|
812
|
+
*
|
|
813
|
+
* That method creates the tables; a table with RLS disabled and no policies
|
|
814
|
+
* is not servable, because authenticated requests run as a restricted role:
|
|
815
|
+
* a read with no `SELECT` policy returns nothing (a public collection
|
|
816
|
+
* answers 401) and a write with no `INSERT`/`UPDATE` policy is denied. The
|
|
817
|
+
* `db push` CLI applies these from the same collections, but it cannot reach
|
|
818
|
+
* a managed tenant's in-cluster database — the runtime, already connected,
|
|
819
|
+
* is the only thing that can.
|
|
820
|
+
*
|
|
821
|
+
* MUST be idempotent (re-run on every boot) and MUST NOT be destructive.
|
|
822
|
+
* Runs after auth initialization, because the generated policies call the
|
|
823
|
+
* `auth.*` helper functions and `CREATE POLICY` validates they exist.
|
|
824
|
+
*/
|
|
825
|
+
ensureCollectionPolicies?(
|
|
826
|
+
collections: unknown[],
|
|
827
|
+
driverResult: InitializedDriver,
|
|
828
|
+
log?: (message: string) => void
|
|
829
|
+
): Promise<{ applied: number }>;
|
|
830
|
+
|
|
801
831
|
/**
|
|
802
832
|
* Initialize WebSocket server for realtime operations.
|
|
803
833
|
*/
|
package/src/types/chips.ts
CHANGED
|
@@ -3,7 +3,13 @@ export type ColorScheme = {
|
|
|
3
3
|
text: string;
|
|
4
4
|
}
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* The hues a chip can take.
|
|
8
|
+
*
|
|
9
|
+
* Kept in step with `CHIP_HUES` in `@rebasepro/ui` by hand: config types cannot
|
|
10
|
+
* depend on the component library. A hue added there needs adding here too.
|
|
11
|
+
*/
|
|
12
|
+
export type ColorHue =
|
|
7
13
|
| "blue"
|
|
8
14
|
| "cyan"
|
|
9
15
|
| "teal"
|
|
@@ -19,3 +25,10 @@ export type ColorKey =
|
|
|
19
25
|
| "fuchsia"
|
|
20
26
|
| "rose"
|
|
21
27
|
| "emerald";
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* How light or saturated a chip is. A hue on its own means `Lighter`.
|
|
31
|
+
*/
|
|
32
|
+
export type ColorTone = "Lighter" | "Light" | "Dark" | "Darker";
|
|
33
|
+
|
|
34
|
+
export type ColorKey = ColorHue | `${ColorHue}${ColorTone}`;
|
package/src/types/cron.ts
CHANGED
|
@@ -38,6 +38,38 @@ export interface CronJobDefinition {
|
|
|
38
38
|
*/
|
|
39
39
|
timeoutSeconds?: number;
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* How far back to look, on startup, for a slot that elapsed while no
|
|
43
|
+
* instance was holding a timer for it. Off by default.
|
|
44
|
+
*
|
|
45
|
+
* The scheduler drives jobs with in-process `setTimeout` and computes the
|
|
46
|
+
* next slot from *now* on every boot, so a slot only fires if some instance
|
|
47
|
+
* happened to be alive and ticking when it came round. That is not a
|
|
48
|
+
* scale-to-zero problem: a platform that recycles containers — Cloud Run
|
|
49
|
+
* rotating an instance under `--min-instances 1`, a rolling deploy, a crash
|
|
50
|
+
* loop — drops any slot that falls inside the changeover, and the
|
|
51
|
+
* replacement schedules the slot *after* it. The run is skipped in silence.
|
|
52
|
+
*
|
|
53
|
+
* Set this to a window comfortably wider than a restart (a few minutes for
|
|
54
|
+
* a frequent job; an hour or more for a daily one) and startup will run a
|
|
55
|
+
* slot it finds unclaimed inside that window.
|
|
56
|
+
*
|
|
57
|
+
* Two deliberate limits:
|
|
58
|
+
*
|
|
59
|
+
* - **Only the most recent missed slot runs.** Booting after a six-hour
|
|
60
|
+
* outage catches an hourly job up once, not six times. Catch-up exists to
|
|
61
|
+
* stop a run going missing, not to replay history.
|
|
62
|
+
* - **A claims-capable store is required.** Catch-up is skipped entirely
|
|
63
|
+
* when the store has no `tryClaimRun` (or no store is attached), because
|
|
64
|
+
* the claim is the only thing that distinguishes "this slot never ran"
|
|
65
|
+
* from "this slot already ran on the instance I am replacing". Without
|
|
66
|
+
* it, an instance recycled every 30 minutes would re-run the same hourly
|
|
67
|
+
* job every time it booted.
|
|
68
|
+
*
|
|
69
|
+
* @example catchUpWindowSeconds: 3600 // daily job: tolerate an hour of downtime
|
|
70
|
+
*/
|
|
71
|
+
catchUpWindowSeconds?: number;
|
|
72
|
+
|
|
41
73
|
/**
|
|
42
74
|
* The handler function executed on each tick.
|
|
43
75
|
* Receives a context object with the data driver and logger.
|