@rebasepro/types 0.17.3 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/call_context.d.ts +20 -0
- package/dist/controllers/client.d.ts +36 -4
- package/dist/controllers/data.d.ts +120 -10
- package/dist/errors.d.ts +83 -4
- package/dist/index.es.js +522 -160
- package/dist/index.es.js.map +1 -1
- package/dist/types/admin_block.d.ts +2 -2
- package/dist/types/auth_adapter.d.ts +41 -6
- package/dist/types/backend.d.ts +48 -0
- package/dist/types/collections.d.ts +25 -1
- package/dist/types/cron.d.ts +34 -0
- package/dist/types/database_adapter.d.ts +39 -0
- package/dist/types/entity_callbacks.d.ts +14 -1
- package/dist/types/filter-operators.d.ts +24 -1
- package/dist/types/policy.d.ts +29 -1
- package/dist/types/properties.d.ts +216 -3
- package/dist/types/relations.d.ts +65 -7
- package/dist/types/resource_kinds.d.ts +173 -17
- package/dist/types/resources.d.ts +108 -7
- package/dist/types/rls-functions.d.ts +11 -0
- package/dist/types/storage_source.d.ts +12 -23
- package/package.json +24 -23
- package/src/call_context.ts +0 -120
- package/src/controllers/auth_state.ts +0 -24
- package/src/controllers/client.ts +0 -494
- package/src/controllers/collection_registry.ts +0 -62
- package/src/controllers/data.ts +0 -1012
- package/src/controllers/data_driver.ts +0 -576
- package/src/controllers/effective_role.ts +0 -4
- package/src/controllers/email.ts +0 -91
- package/src/controllers/index.ts +0 -11
- package/src/controllers/storage.ts +0 -252
- package/src/errors.ts +0 -119
- package/src/index.ts +0 -5
- package/src/types/admin_block.ts +0 -209
- package/src/types/api_keys.ts +0 -108
- package/src/types/auth_adapter.ts +0 -580
- package/src/types/backend.ts +0 -987
- package/src/types/backup.ts +0 -26
- package/src/types/channel_bus.ts +0 -202
- package/src/types/chips.ts +0 -34
- package/src/types/collection_contract.ts +0 -278
- package/src/types/collections.ts +0 -763
- package/src/types/component_ref.ts +0 -92
- package/src/types/cron.ts +0 -213
- package/src/types/data_source.ts +0 -357
- package/src/types/database_adapter.ts +0 -267
- package/src/types/entities.ts +0 -226
- package/src/types/entity_callbacks.ts +0 -229
- package/src/types/filter-operators.ts +0 -444
- package/src/types/history.ts +0 -66
- package/src/types/index.ts +0 -36
- package/src/types/indexes.ts +0 -180
- package/src/types/policy.ts +0 -328
- package/src/types/postgres_introspection.ts +0 -101
- package/src/types/project_manifest.ts +0 -598
- package/src/types/properties.ts +0 -1368
- package/src/types/relations.ts +0 -417
- package/src/types/resource_kinds.ts +0 -390
- package/src/types/resources.ts +0 -368
- package/src/types/rls-functions.ts +0 -98
- package/src/types/schema_editing.ts +0 -157
- package/src/types/schema_version.ts +0 -112
- package/src/types/search.ts +0 -247
- package/src/types/security_rules.ts +0 -344
- package/src/types/storage_authorize.ts +0 -77
- package/src/types/storage_source.ts +0 -248
- package/src/types/websockets.ts +0 -117
- package/src/users/index.ts +0 -2
- package/src/users/user.ts +0 -69
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
* A declaration says a resource *exists* and what shape it has. It never says
|
|
20
20
|
* how to reach it — that is a property of the environment, not of the project,
|
|
21
21
|
* and it differs between a laptop, a self-hosted box and a tenant in the cloud.
|
|
22
|
-
* Binding lives in `@rebasepro/server`'s boot path,
|
|
23
|
-
*
|
|
24
|
-
* declared here.
|
|
22
|
+
* Binding lives in `@rebasepro/server`'s boot path, where each kind registers
|
|
23
|
+
* the resolver that reads its environment variables, keyed off the logical
|
|
24
|
+
* name declared here.
|
|
25
25
|
*
|
|
26
26
|
* This split is the whole point. Before it, storage topology was hand-written
|
|
27
27
|
* into `rebase.json` while database topology lived in TypeScript, and the
|
|
@@ -54,6 +54,26 @@ export type ResourceTransport =
|
|
|
54
54
|
export interface ResourceKindSpec {
|
|
55
55
|
/** The kind's name, as it appears in a declaration and in the graph. */
|
|
56
56
|
kind: string;
|
|
57
|
+
/**
|
|
58
|
+
* Which definition of this kind this is. Bump it whenever anything else in
|
|
59
|
+
* the spec changes.
|
|
60
|
+
*
|
|
61
|
+
* Two copies of this package can meet in one process — a published driver
|
|
62
|
+
* inlines it into its dist, and the runtime image ships its own — and the
|
|
63
|
+
* registry is shared between them on purpose. Without a revision the only
|
|
64
|
+
* thing the registry can do with two specs that differ is refuse, and a
|
|
65
|
+
* refusal at driver load is a pod that never boots: every bundle built with
|
|
66
|
+
* a driver older than the change dies on the first image that carries it.
|
|
67
|
+
* With one, the higher revision wins whichever copy loads first, and the
|
|
68
|
+
* older copy is told so. Missing means 0, which is what every copy shipped
|
|
69
|
+
* before revisions existed reports.
|
|
70
|
+
*
|
|
71
|
+
* Only copies that know about revisions honour them. A copy published
|
|
72
|
+
* BEFORE they existed still compares the whole literal and throws, so a
|
|
73
|
+
* kind that has shipped in such a copy cannot change its literal at all —
|
|
74
|
+
* not even to add this field. Correct those kinds with `amendResourceKind`.
|
|
75
|
+
*/
|
|
76
|
+
revision?: number;
|
|
57
77
|
/** Engines this kind ships with. `custom:<id>` is always additionally valid. */
|
|
58
78
|
engines: readonly string[];
|
|
59
79
|
/** Used when a declaration names none. */
|
|
@@ -100,6 +120,19 @@ export interface ResourceDeclaration {
|
|
|
100
120
|
label?: string;
|
|
101
121
|
/** Kind-specific options, validated against the kind's `optionKeys`. */
|
|
102
122
|
options: Readonly<Record<string, unknown>>;
|
|
123
|
+
/**
|
|
124
|
+
* What in the project reaches this resource, as `<what>:<name>` — a
|
|
125
|
+
* `collection:posts` routed to a database, a `property:posts.cover` stored
|
|
126
|
+
* in a bucket, a `function:report` importing a handle.
|
|
127
|
+
*
|
|
128
|
+
* Recorded by the derive step, never by a constructor: a declaration says
|
|
129
|
+
* a resource exists, and only a reader that has evaluated the rest of the
|
|
130
|
+
* project can say who uses it. It is the map a host needs to split a
|
|
131
|
+
* monolith into units later, and the map a console needs to answer "what
|
|
132
|
+
* breaks if I remove this". Absent when nothing was recorded, which is
|
|
133
|
+
* different from an empty list.
|
|
134
|
+
*/
|
|
135
|
+
usedBy?: readonly string[];
|
|
103
136
|
}
|
|
104
137
|
/**
|
|
105
138
|
* The value a constructor returns.
|
|
@@ -115,9 +148,68 @@ export interface ResourceHandle extends ResourceDeclaration {
|
|
|
115
148
|
}
|
|
116
149
|
/** Whether a value is a resource handle rather than a plain string key. */
|
|
117
150
|
export declare function isResourceHandle(value: unknown): value is ResourceHandle;
|
|
151
|
+
/**
|
|
152
|
+
* A reference to a resource where a key is expected: the handle a constructor
|
|
153
|
+
* returned, or the key spelled as a string.
|
|
154
|
+
*
|
|
155
|
+
* The handle is the point. `dataSource: analytics` is the same name spelled
|
|
156
|
+
* once — rename the export and every use follows, jump-to-definition lands on
|
|
157
|
+
* the declaration, and the derive step can record who uses what. The string
|
|
158
|
+
* form stays because a key has to survive serialisation: the runtime and the
|
|
159
|
+
* admin UI read collections as plain data, where a handle cannot travel.
|
|
160
|
+
*/
|
|
161
|
+
export type ResourceRef = string | ResourceHandle;
|
|
118
162
|
/** The key a resource reference names, whether it is a handle or already a key. */
|
|
119
|
-
export declare function resourceKeyOf(ref:
|
|
120
|
-
/**
|
|
163
|
+
export declare function resourceKeyOf(ref: ResourceRef): string;
|
|
164
|
+
/**
|
|
165
|
+
* Replace every resource handle inside a value with its key, deeply.
|
|
166
|
+
*
|
|
167
|
+
* Applied where authored config becomes data: `defineCollection`, the
|
|
168
|
+
* collection loaders, the derive step. Past that point a collection is plain
|
|
169
|
+
* data that serialises, compares with `===` and reaches the admin UI over the
|
|
170
|
+
* wire, so a handle must not survive into it. Plain objects and arrays are
|
|
171
|
+
* walked; anything else — a function, a Date, a class instance — is a leaf and
|
|
172
|
+
* is returned as it is, which is what keeps callbacks and validators intact.
|
|
173
|
+
*/
|
|
174
|
+
export declare function resolveResourceRefs<T>(value: T): T;
|
|
175
|
+
/**
|
|
176
|
+
* Corrections this copy applies on top of a registered kind.
|
|
177
|
+
*
|
|
178
|
+
* Deliberately a module local — per COPY of this package — where the registry
|
|
179
|
+
* above is deliberately shared. A published driver inlines this package into
|
|
180
|
+
* its dist, and the copy it carries compares the shared registry's entry for a
|
|
181
|
+
* kind against its own literal by `JSON.stringify` and throws if they differ
|
|
182
|
+
* (see `registerResourceKind` before revisions existed). That code is in the
|
|
183
|
+
* field and cannot be changed, so the registered literal of any kind that has
|
|
184
|
+
* ever shipped is frozen: change one enumerable key and every bundle built with
|
|
185
|
+
* an older driver dies at driver load on the next image. What a kind actually
|
|
186
|
+
* binds can still be corrected — here, read through `resourceKind()` and
|
|
187
|
+
* everything built on it, invisible to the older copy, which keeps binding the
|
|
188
|
+
* way it did when it was published.
|
|
189
|
+
*/
|
|
190
|
+
type KindAmendment = Partial<Pick<ResourceKindSpec, "envBases" | "envBasesByEngine" | "optionKeys">>;
|
|
191
|
+
/**
|
|
192
|
+
* Correct a registered kind without touching its registered literal.
|
|
193
|
+
*
|
|
194
|
+
* Use this, never an edit to the literal, for a kind that has shipped in a
|
|
195
|
+
* published package. The amendment applies to reads through this copy only.
|
|
196
|
+
*/
|
|
197
|
+
export declare function amendResourceKind(kind: string, amendment: KindAmendment): void;
|
|
198
|
+
/**
|
|
199
|
+
* Register a resource kind.
|
|
200
|
+
*
|
|
201
|
+
* Idempotent for an identical spec. When a spec for the same kind is already
|
|
202
|
+
* registered and differs, the `revision` decides: the higher one is kept and
|
|
203
|
+
* the other copy is warned about, in either load order. Two different specs at
|
|
204
|
+
* the SAME revision are a genuine conflict — two packages defining one kind, or
|
|
205
|
+
* a change that forgot to bump — and still throw.
|
|
206
|
+
*
|
|
207
|
+
* Both copies in that comparison are peers on `KINDS_KEY`, which is what makes
|
|
208
|
+
* the rule enforceable: a copy old enough not to know `revision` writes to the
|
|
209
|
+
* legacy map instead and never reaches this function's arithmetic. Registering
|
|
210
|
+
* a kind an older copy already put in the legacy map is therefore not a
|
|
211
|
+
* conflict — it is the ordinary case, and `visibleKinds` prefers this one.
|
|
212
|
+
*/
|
|
121
213
|
export declare function registerResourceKind(spec: ResourceKindSpec): void;
|
|
122
214
|
/** Every registered kind, for validators and for `rebase doctor`. */
|
|
123
215
|
export declare function resourceKinds(): ResourceKindSpec[];
|
|
@@ -185,8 +277,16 @@ export interface ResourceGraph {
|
|
|
185
277
|
}
|
|
186
278
|
/** The current graph format version. */
|
|
187
279
|
export declare const RESOURCE_GRAPH_VERSION: 1;
|
|
188
|
-
/**
|
|
189
|
-
|
|
280
|
+
/**
|
|
281
|
+
* Build a graph from the current declarations, sorted for a stable diff.
|
|
282
|
+
*
|
|
283
|
+
* `usedBy` maps a `kind:key` id to the things that reach it. The derive step
|
|
284
|
+
* supplies it after evaluating collections; a runtime building the graph at
|
|
285
|
+
* boot passes nothing and gets declarations alone, which is all it binds from.
|
|
286
|
+
*/
|
|
287
|
+
export declare function buildResourceGraph(usedBy?: ReadonlyMap<string, readonly string[]>): ResourceGraph;
|
|
288
|
+
/** `kind:key`, the id `usedBy` maps are keyed by. Exported for the derive step. */
|
|
289
|
+
export declare function resourceId(kind: string, key: string): string;
|
|
190
290
|
/**
|
|
191
291
|
* The environment variables worth writing for a resource, given its engine.
|
|
192
292
|
*
|
|
@@ -195,3 +295,4 @@ export declare function buildResourceGraph(): ResourceGraph;
|
|
|
195
295
|
* guessing narrow would silently omit the variable it actually needs.
|
|
196
296
|
*/
|
|
197
297
|
export declare function envBasesForResource(declaration: ResourceDeclaration): readonly string[];
|
|
298
|
+
export {};
|
|
@@ -56,6 +56,17 @@ export declare const REBASE_SCHEMA = "rebase";
|
|
|
56
56
|
export declare const RLS_UID_SQL = "rebase.uid()";
|
|
57
57
|
/** The request's roles as a comma-separated string, for `string_to_array`. */
|
|
58
58
|
export declare const RLS_ROLES_SQL = "rebase.roles()";
|
|
59
|
+
/**
|
|
60
|
+
* Whether the caller is a GUEST — signed in through anonymous sign-in rather
|
|
61
|
+
* than with an account.
|
|
62
|
+
*
|
|
63
|
+
* A different question from {@link ANONYMOUS_USER_ID}, and the two are easy to
|
|
64
|
+
* confuse: that sentinel means "no session at all", while this means "a session
|
|
65
|
+
* with nobody behind it". Anonymous sign-in mints a real user row with a real
|
|
66
|
+
* uid, so before this reached the database the two kinds of caller were one
|
|
67
|
+
* principal inside every policy.
|
|
68
|
+
*/
|
|
69
|
+
export declare const RLS_IS_ANONYMOUS_SQL = "rebase.is_anonymous()";
|
|
59
70
|
/** The request's JWT claims as `jsonb`, or `{}`. */
|
|
60
71
|
export declare const RLS_JWT_SQL = "rebase.jwt()";
|
|
61
72
|
/**
|
|
@@ -89,6 +89,18 @@ export interface StorageSourceDefinition {
|
|
|
89
89
|
* upload/download traffic for this source.
|
|
90
90
|
*/
|
|
91
91
|
transport: StorageSourceTransport;
|
|
92
|
+
/**
|
|
93
|
+
* Serve unqualified uploads — a storage property naming no `storageSource`
|
|
94
|
+
* — from this source.
|
|
95
|
+
*
|
|
96
|
+
* Declared, never inferred. A project with named buckets and no default
|
|
97
|
+
* used to have one chosen for it by declaration order, with a warning, and
|
|
98
|
+
* the choice differed between development and production because the
|
|
99
|
+
* synthesized local default is dropped in production and the promotion was
|
|
100
|
+
* not. Where the files land is the author's decision; boot now fails
|
|
101
|
+
* without one.
|
|
102
|
+
*/
|
|
103
|
+
default?: boolean;
|
|
92
104
|
/** Human-readable label for the UI (e.g. "Firebase Storage", "S3 Media"). */
|
|
93
105
|
label?: string;
|
|
94
106
|
}
|
|
@@ -145,26 +157,3 @@ export declare function findStorageSuffixCollision(keys: string[], defaultKey?:
|
|
|
145
157
|
b: string;
|
|
146
158
|
suffix: string;
|
|
147
159
|
} | null;
|
|
148
|
-
/** The `storage` block of `rebase.json`, structurally. */
|
|
149
|
-
export type DeclaredStorageSources = Record<string, {
|
|
150
|
-
engine: string;
|
|
151
|
-
transport?: StorageSourceTransport;
|
|
152
|
-
label?: string;
|
|
153
|
-
}>;
|
|
154
|
-
/**
|
|
155
|
-
* Merge the two places a project may declare storage sources into one list.
|
|
156
|
-
*
|
|
157
|
-
* `rebase.json` is authoritative for every field it states. Config code may add
|
|
158
|
-
* sources it does not mention and fill in fields it left out, but may not
|
|
159
|
-
* contradict it: the manifest is what a host reads to decide which buckets need
|
|
160
|
-
* configuring, and a runtime that quietly disagreed with it would put the
|
|
161
|
-
* console back to describing a topology the tenant does not have — the exact
|
|
162
|
-
* failure this whole mechanism exists to end.
|
|
163
|
-
*
|
|
164
|
-
* Note what is *not* here: no default source is invented when both inputs are
|
|
165
|
-
* empty. That decision belongs to the resolver, which knows whether declaring
|
|
166
|
-
* nothing means "one plain bucket" (it does) or "no storage at all".
|
|
167
|
-
*
|
|
168
|
-
* @group Models
|
|
169
|
-
*/
|
|
170
|
-
export declare function normalizeStorageSources(declared: DeclaredStorageSources | StorageSourceDefinition[] | undefined, exported: StorageSourceDefinition[] | undefined): StorageSourceDefinition[];
|
package/package.json
CHANGED
|
@@ -1,39 +1,43 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/types",
|
|
3
|
-
"
|
|
4
|
-
"version": "0.17.3",
|
|
3
|
+
"version": "0.18.1",
|
|
5
4
|
"description": "Rebase type definitions — shared interfaces and controller types",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
5
|
+
"keywords": [
|
|
6
|
+
"rebase",
|
|
7
|
+
"cms",
|
|
8
|
+
"admin",
|
|
9
|
+
"typescript",
|
|
10
|
+
"headless",
|
|
11
|
+
"headless cms",
|
|
12
|
+
"content manager"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://rebase.pro",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/rebasepro/rebase/issues"
|
|
8
17
|
},
|
|
9
|
-
"author": "Rebase",
|
|
10
|
-
"license": "MIT",
|
|
11
18
|
"repository": {
|
|
12
19
|
"type": "git",
|
|
13
20
|
"url": "https://github.com/rebasepro/rebase.git",
|
|
14
21
|
"directory": "packages/types"
|
|
15
22
|
},
|
|
23
|
+
"funding": {
|
|
24
|
+
"url": "https://github.com/sponsors/rebasepro"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"author": "Rebase",
|
|
28
|
+
"type": "module",
|
|
16
29
|
"main": "./dist/index.es.js",
|
|
17
30
|
"module": "./dist/index.es.js",
|
|
18
31
|
"types": "./dist/index.d.ts",
|
|
19
|
-
"source": "src/index.ts",
|
|
20
32
|
"engines": {
|
|
21
|
-
"node": ">=
|
|
33
|
+
"node": ">=22.22.0"
|
|
22
34
|
},
|
|
23
|
-
"keywords": [
|
|
24
|
-
"rebase",
|
|
25
|
-
"cms",
|
|
26
|
-
"admin",
|
|
27
|
-
"typescript",
|
|
28
|
-
"headless",
|
|
29
|
-
"headless cms",
|
|
30
|
-
"content manager"
|
|
31
|
-
],
|
|
32
35
|
"exports": {
|
|
33
36
|
".": {
|
|
34
37
|
"types": "./dist/index.d.ts",
|
|
35
38
|
"development": "./dist/index.es.js",
|
|
36
|
-
"import": "./dist/index.es.js"
|
|
39
|
+
"import": "./dist/index.es.js",
|
|
40
|
+
"default": "./dist/index.es.js"
|
|
37
41
|
},
|
|
38
42
|
"./package.json": "./package.json"
|
|
39
43
|
},
|
|
@@ -48,12 +52,8 @@
|
|
|
48
52
|
"typescript": "^6.0.3",
|
|
49
53
|
"vite": "^8.1.5"
|
|
50
54
|
},
|
|
51
|
-
"peerDependencies": {
|
|
52
|
-
"hono": "^4.12.27"
|
|
53
|
-
},
|
|
54
55
|
"files": [
|
|
55
|
-
"dist"
|
|
56
|
-
"src"
|
|
56
|
+
"dist"
|
|
57
57
|
],
|
|
58
58
|
"gitHead": "d935eefa5aa8d1009a2398cfac2c1e4ee9aeb6b6",
|
|
59
59
|
"publishConfig": {
|
|
@@ -91,6 +91,7 @@
|
|
|
91
91
|
"build": "vite build && tsc --emitDeclarationOnly -p tsconfig.prod.json && node ../../tooling/scripts/add-dts-extensions.mjs dist && node ../../tooling/scripts/assert-build-output.mjs",
|
|
92
92
|
"test:lint": "eslint \"src/**\" --quiet",
|
|
93
93
|
"test": "jest --passWithNoTests",
|
|
94
|
+
"test:watch": "jest --watch",
|
|
94
95
|
"clean": "rm -rf dist && find ./src -name '*.js' -type f | xargs rm -f"
|
|
95
96
|
}
|
|
96
97
|
}
|
package/src/call_context.ts
DELETED
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
import type { DataDriver } from "./controllers/data_driver";
|
|
2
|
-
import type { StorageSource } from "./controllers/storage";
|
|
3
|
-
import type { RebaseClient } from "./controllers/client";
|
|
4
|
-
import type { RebaseSdkData } from "./controllers/data";
|
|
5
|
-
import type { User } from "./users";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Context that is provided to entity callbacks (hooks).
|
|
9
|
-
* It contains only the dependencies that are available in both the frontend and the backend.
|
|
10
|
-
*
|
|
11
|
-
* This is the *whole* context a collection callback gets, and it lives apart from
|
|
12
|
-
* {@link RebaseContext} on purpose. `RebaseContext` widens it with nine admin-panel
|
|
13
|
-
* controllers — navigation, side dialogs, snackbars — none of which exist in a
|
|
14
|
-
* backend process. Keeping them in one type meant every backend module that
|
|
15
|
-
* touched a callback signature transitively named the admin UI.
|
|
16
|
-
*
|
|
17
|
-
* @group Hooks and utilities
|
|
18
|
-
*/
|
|
19
|
-
export type RebaseCallContext<USER extends User = User> = {
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* The Rebase client instance.
|
|
23
|
-
* Available in all entity callbacks (beforeSave, afterSave, afterRead,
|
|
24
|
-
* beforeDelete, afterDelete) and in CollectionActionsProps via context.
|
|
25
|
-
* Use it to call backend functions, access storage, send email, etc.
|
|
26
|
-
*
|
|
27
|
-
* ⚠️ **Not the same trust level as {@link data}.** Server-side this is the
|
|
28
|
-
* app singleton, so `client.dataAsAdmin` is **always** the admin-scoped
|
|
29
|
-
* plane — scoped as `{ uid: "service", roles: ["admin"] }`, so policies are
|
|
30
|
-
* evaluated against that identity rather than skipped — while {@link data},
|
|
31
|
-
* one property over, follows whoever triggered the callback. On a user
|
|
32
|
-
* request, reaching for `context.client.dataAsAdmin` silently escalates a
|
|
33
|
-
* user-scoped operation to admin. For queries in a callback use
|
|
34
|
-
* {@link data}; come here for functions, storage and email.
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* // In a beforeSave callback:
|
|
38
|
-
* const result = await context.client.functions.invoke('my-function', { ... });
|
|
39
|
-
*
|
|
40
|
-
* @example
|
|
41
|
-
* // In a CollectionAction component:
|
|
42
|
-
* const { client } = props.context;
|
|
43
|
-
* const result = await client.functions.invoke('extract-job', { url });
|
|
44
|
-
*/
|
|
45
|
-
client: RebaseClient;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Unified data access — `context.data.products.create(...)`.
|
|
49
|
-
* Access any collection as a dynamic property.
|
|
50
|
-
*
|
|
51
|
-
* **Inherits the privilege of whatever triggered the callback.** This is not
|
|
52
|
-
* a fixed trust level, and it is the one thing to know about this accessor:
|
|
53
|
-
*
|
|
54
|
-
* - Triggered by a **user request** (REST, realtime, an admin-panel edit):
|
|
55
|
-
* user-scoped. The callback runs on the RLS-bound transaction opened for
|
|
56
|
-
* that request, so policies apply to reads *and* writes — a callback
|
|
57
|
-
* cannot see a row its caller could not.
|
|
58
|
-
* - Triggered by **`rebase.dataAsAdmin` or a cron** (the same singleton):
|
|
59
|
-
* admin-scoped, not unscoped. That driver is scoped as
|
|
60
|
-
* `{ uid: "service", roles: ["admin"] }`, so the callback still runs on an
|
|
61
|
-
* RLS-bound transaction — policies are evaluated against that identity.
|
|
62
|
-
* - Triggered by **the base driver** (auth flows, migrations): unscoped, on
|
|
63
|
-
* the owner connection, bypassing RLS.
|
|
64
|
-
*
|
|
65
|
-
* So a callback that reads a sibling row will find it when an admin task
|
|
66
|
-
* saves and may find nothing when an end user saves — without an error,
|
|
67
|
-
* because RLS filters rather than raises. Write callbacks that tolerate
|
|
68
|
-
* that, or reach for {@link client}`.dataAsAdmin` deliberately when the
|
|
69
|
-
* callback genuinely has to see what an admin may see. Note what that does
|
|
70
|
-
* *not* buy you: `policy.serverContext()` (`rebase.uid() IS NULL`) is false
|
|
71
|
-
* for the service identity, so a collection whose only rule is
|
|
72
|
-
* `serverContext()` stays closed to it.
|
|
73
|
-
*
|
|
74
|
-
* Verified end-to-end against Postgres rather than asserted — see
|
|
75
|
-
* `"scopes context.data to the caller when a callback runs on a user
|
|
76
|
-
* request"` in `server-postgres`' `rls-enforcement` e2e suite. The
|
|
77
|
-
* documentation previously claimed the opposite (that callbacks always have
|
|
78
|
-
* full access), which is the unsafe direction to be wrong in.
|
|
79
|
-
*
|
|
80
|
-
* Returns flat rows (`{ id, ...columns }`), identical in *shape* to the
|
|
81
|
-
* frontend SDK client — so `context.data` in a backend callback and
|
|
82
|
-
* `client.data` in the frontend are accessed the same way (`row.title`,
|
|
83
|
-
* never `row.values.title`). Shape only: privilege differs as above.
|
|
84
|
-
*/
|
|
85
|
-
data: RebaseSdkData;
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* The driver executing the operation this callback is attached to.
|
|
89
|
-
*
|
|
90
|
-
* Present server-side only. Declared here because it is already public in
|
|
91
|
-
* practice — the backend has always passed it, and the callbacks guide
|
|
92
|
-
* documented `context.driver.withAuth(user)` in all six locales. The
|
|
93
|
-
* contract simply did not name it, so `buildCallContext` was cast through
|
|
94
|
-
* `as unknown as RebaseCallContext` and nothing about the object was
|
|
95
|
-
* type-checked at all.
|
|
96
|
-
*
|
|
97
|
-
* The guide no longer recommends `withAuth` — {@link data} is already
|
|
98
|
-
* user-scoped on a user request, so the manual re-scoping it described was
|
|
99
|
-
* answering a problem that did not exist. The field stays declared rather
|
|
100
|
-
* than removed: it is on the runtime object, dropping it would break anyone
|
|
101
|
-
* who found it, and a named optional is better than a silent extra.
|
|
102
|
-
*
|
|
103
|
-
* `withAuth` is not on {@link DataDriver} because not every engine supports
|
|
104
|
-
* RLS scoping; it is narrowed here, and left optional so a driver without it
|
|
105
|
-
* is a compile-time absence rather than a runtime surprise.
|
|
106
|
-
*/
|
|
107
|
-
driver?: DataDriver & {
|
|
108
|
-
withAuth?(user: { uid: string; roles?: string[] }): Promise<DataDriver>;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Used storage implementation
|
|
113
|
-
*/
|
|
114
|
-
storageSource: StorageSource;
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Set by the backend when callbacks are executed on the server.
|
|
118
|
-
*/
|
|
119
|
-
user?: USER;
|
|
120
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { User } from "../users";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* The read-only slice of authentication state that property resolution needs.
|
|
5
|
-
*
|
|
6
|
-
* `dynamicProps`, `conditions` and the JSON-Logic condition context all want the
|
|
7
|
-
* same one thing: who is asking. They used to be handed the entire
|
|
8
|
-
* {@link AuthController} — `signOut`, `googleLogin`, `authLoading`, thirty-odd
|
|
9
|
-
* members of frontend machinery — which meant `properties.ts` named a frontend
|
|
10
|
-
* controller, and so did `resolveProperty` in `@rebasepro/common`, which the
|
|
11
|
-
* Postgres schema generator calls at build time. A backend generating DDL had to
|
|
12
|
-
* satisfy a type with a login method in it.
|
|
13
|
-
*
|
|
14
|
-
* `User` already carries `roles` and `metadata`, so this is not a reduction in
|
|
15
|
-
* what a dynamic property can decide on — only in what it has to be given.
|
|
16
|
-
* An `AuthController` satisfies this structurally, so the frontend passes the
|
|
17
|
-
* controller it already has.
|
|
18
|
-
*
|
|
19
|
-
* @group Hooks and utilities
|
|
20
|
-
*/
|
|
21
|
-
export interface AuthState<USER extends User = User> {
|
|
22
|
-
/** The signed-in user, or `null` when nobody is. */
|
|
23
|
-
user: USER | null;
|
|
24
|
-
}
|