@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
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* How a collection points at a UI component without the backend learning about React.
|
|
3
|
-
*
|
|
4
|
-
* This file is the hinge the BaaS/admin split turns on. `ComponentRef` is named
|
|
5
|
-
* by a property's `admin` block (`admin.Field`, `admin.Preview`, `admin.Filter`)
|
|
6
|
-
* and imported by `properties.ts`, which must stay in the React-free core
|
|
7
|
-
* because every backend subsystem — validation,
|
|
8
|
-
* the drizzle schema generator, the OpenAPI generator, the SDK codegen — reads
|
|
9
|
-
* property definitions. If `ComponentRef` needed `React.ComponentType`, the whole
|
|
10
|
-
* property model would have to move to the admin layer with it.
|
|
11
|
-
*
|
|
12
|
-
* So the React types are described structurally instead of imported. Every form
|
|
13
|
-
* a React component takes is assignable to {@link ComponentLike}:
|
|
14
|
-
*
|
|
15
|
-
* - a function component is `(props: P) => ReactNode`
|
|
16
|
-
* - a class component satisfies the construct signature (`Component` has `render`)
|
|
17
|
-
* - `memo` and `forwardRef` return exotic components, which are callable
|
|
18
|
-
*
|
|
19
|
-
* The cost is that the return type is `unknown` rather than `ReactNode`, so a
|
|
20
|
-
* function that returns something React could not render is accepted here.
|
|
21
|
-
* `@rebasepro/cms-types` re-exports a `ReactComponentRef<P>` narrowed against
|
|
22
|
-
* the real `React.ComponentType` for authoring and for the admin's internals,
|
|
23
|
-
* which restores that check where it can be enforced.
|
|
24
|
-
*/
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Structural stand-in for `React.ComponentType<P>`.
|
|
28
|
-
*
|
|
29
|
-
* Deliberately not `Function` or `unknown`: those would accept anything and the
|
|
30
|
-
* resolver's runtime heuristics ({@link ComponentRef} form 3) would be all that
|
|
31
|
-
* stood between a typo and a blank screen.
|
|
32
|
-
*/
|
|
33
|
-
export type ComponentLike<P = any> =
|
|
34
|
-
| ((props: P) => unknown)
|
|
35
|
-
| (new (props: P, context?: unknown) => { render(): unknown });
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Internal marker for a lazily-loaded component reference.
|
|
39
|
-
* Created by the Vite transform plugin when converting string paths
|
|
40
|
-
* to deferred `import()` calls. Users should NOT create these manually.
|
|
41
|
-
*
|
|
42
|
-
* @internal
|
|
43
|
-
*/
|
|
44
|
-
export interface LazyComponentRef<P = unknown> {
|
|
45
|
-
readonly __rebaseLazy: true;
|
|
46
|
-
readonly load: () => Promise<{ default: ComponentLike<P> }>;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* A reference to a UI component that can be provided in three forms:
|
|
51
|
-
*
|
|
52
|
-
* 1. **String path** (recommended for collection configs):
|
|
53
|
-
* ```ts
|
|
54
|
-
* Field: "../../frontend/src/components/MyField"
|
|
55
|
-
* ```
|
|
56
|
-
* The Vite plugin transforms this into a `LazyComponentRef` at build time.
|
|
57
|
-
* On the backend, the string stays inert and is never evaluated.
|
|
58
|
-
*
|
|
59
|
-
* 2. **Lazy import function**:
|
|
60
|
-
* ```ts
|
|
61
|
-
* Field: () => import("../../frontend/src/components/MyField")
|
|
62
|
-
* ```
|
|
63
|
-
* Standard ES dynamic import. Backend never calls the function.
|
|
64
|
-
*
|
|
65
|
-
* 3. **Direct component reference** (use only in frontend-only code):
|
|
66
|
-
* ```ts
|
|
67
|
-
* Field: MyFieldComponent
|
|
68
|
-
* ```
|
|
69
|
-
* Importing a component at the top level will pull React into the
|
|
70
|
-
* backend runtime — only safe in code that the backend never imports.
|
|
71
|
-
* `pnpm check:headless` fails on a collection file that does this.
|
|
72
|
-
*
|
|
73
|
-
* @group Types
|
|
74
|
-
*/
|
|
75
|
-
export type ComponentRef<P = any> =
|
|
76
|
-
| string
|
|
77
|
-
| LazyComponentRef<P>
|
|
78
|
-
| (() => Promise<{ default: ComponentLike<P> }>)
|
|
79
|
-
| ComponentLike<P>;
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Type guard: checks if a value is a `LazyComponentRef` produced by the
|
|
83
|
-
* Vite transform plugin.
|
|
84
|
-
*/
|
|
85
|
-
export function isLazyComponentRef<P = unknown>(ref: unknown): ref is LazyComponentRef<P> {
|
|
86
|
-
return (
|
|
87
|
-
typeof ref === "object" &&
|
|
88
|
-
ref !== null &&
|
|
89
|
-
"__rebaseLazy" in ref &&
|
|
90
|
-
(ref as Record<string, unknown>).__rebaseLazy === true
|
|
91
|
-
);
|
|
92
|
-
}
|
package/src/types/cron.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import type { RebaseServerClient } from "../controllers/client";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Cron Job type definitions for Rebase.
|
|
5
|
-
*
|
|
6
|
-
* These types define the shape of cron job definitions, their runtime
|
|
7
|
-
* status, and execution log entries — used across server, client,
|
|
8
|
-
* and studio packages.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
// =============================================================================
|
|
12
|
-
// CRON JOB DEFINITION (static, declared by the developer)
|
|
13
|
-
// =============================================================================
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* A cron job definition file exports this shape as its default export.
|
|
17
|
-
* See the example cron files in `app/backend/crons/` for usage.
|
|
18
|
-
*/
|
|
19
|
-
export interface CronJobDefinition {
|
|
20
|
-
// Cron schedule expression, e.g. "0 3 * * *" for daily at 3 AM.
|
|
21
|
-
schedule: string;
|
|
22
|
-
|
|
23
|
-
/** Human-readable name shown in the Studio UI. */
|
|
24
|
-
name: string;
|
|
25
|
-
|
|
26
|
-
/** Optional description shown in the Studio UI. */
|
|
27
|
-
description?: string;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Whether the job is enabled at startup. Defaults to `true`.
|
|
31
|
-
* Can be toggled at runtime via the Admin API.
|
|
32
|
-
*/
|
|
33
|
-
enabled?: boolean;
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Maximum number of seconds the handler may run before being
|
|
37
|
-
* considered timed-out. Default: 300 (5 min).
|
|
38
|
-
*/
|
|
39
|
-
timeoutSeconds?: number;
|
|
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
|
-
|
|
73
|
-
/**
|
|
74
|
-
* The handler function executed on each tick.
|
|
75
|
-
* Receives a context object with the data driver and logger.
|
|
76
|
-
* May return arbitrary JSON-serialisable data stored in the log.
|
|
77
|
-
*/
|
|
78
|
-
handler: (ctx: CronJobContext) => Promise<unknown> | unknown;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Context passed to each cron handler invocation.
|
|
84
|
-
*/
|
|
85
|
-
export interface CronJobContext {
|
|
86
|
-
/** The job's unique ID (derived from filename). */
|
|
87
|
-
jobId: string;
|
|
88
|
-
|
|
89
|
-
/** The current scheduled tick timestamp. */
|
|
90
|
-
scheduledAt: Date;
|
|
91
|
-
|
|
92
|
-
/** A simple logger scoped to this job run. */
|
|
93
|
-
log: (...args: unknown[]) => void;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* The server-side Rebase singleton — the **same object** `import { rebase }
|
|
97
|
-
* from "@rebasepro/server"` returns, and the same one `defineFunction`
|
|
98
|
-
* hands its callback. Spelled the same way here so that one thing has one
|
|
99
|
-
* name across every server-side authoring surface.
|
|
100
|
-
*
|
|
101
|
-
* Its data plane is {@link RebaseServerClient.dataAsAdmin}, scoped as
|
|
102
|
-
* `{ uid: "service", roles: ["admin"] }` — **admin-scoped, not
|
|
103
|
-
* RLS-bypassing**. Statements still run as `rebase_user` with the policies
|
|
104
|
-
* evaluated against that identity; the admin role merely clears the default
|
|
105
|
-
* policies through their `rolesOverlap(['admin'])` arm, and
|
|
106
|
-
* `policy.serverContext()` (`rebase.uid() IS NULL`) is *false* for it.
|
|
107
|
-
* `rebase.sql()` is the unconditional bypass. A cron has no per-request
|
|
108
|
-
* user, so there is no user-scoped alternative here: scope every query's
|
|
109
|
-
* filters yourself.
|
|
110
|
-
*
|
|
111
|
-
* @example
|
|
112
|
-
* export default defineCron({
|
|
113
|
-
* name: "Nightly cleanup",
|
|
114
|
-
* schedule: "0 3 * * *",
|
|
115
|
-
* async handler({ rebase, log }) {
|
|
116
|
-
* const expired = await rebase.dataAsAdmin.sessions.findAll({
|
|
117
|
-
* where: { expired: ["==", true] }
|
|
118
|
-
* });
|
|
119
|
-
* for (const session of expired) {
|
|
120
|
-
* await rebase.dataAsAdmin.sessions.delete(session.id as string);
|
|
121
|
-
* }
|
|
122
|
-
* log(`Deleted ${expired.length} expired sessions`);
|
|
123
|
-
* }
|
|
124
|
-
* });
|
|
125
|
-
*/
|
|
126
|
-
rebase: RebaseServerClient;
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// =============================================================================
|
|
131
|
-
// CRON JOB RUNTIME STATUS (what the API returns)
|
|
132
|
-
// =============================================================================
|
|
133
|
-
|
|
134
|
-
export type CronJobRunState = "idle" | "running" | "success" | "error" | "disabled";
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Full runtime information about a registered cron job.
|
|
138
|
-
*/
|
|
139
|
-
export interface CronJobStatus {
|
|
140
|
-
/** Unique identifier (derived from filename, e.g. "cleanup-sessions"). */
|
|
141
|
-
id: string;
|
|
142
|
-
|
|
143
|
-
/** Human-readable name from the definition. */
|
|
144
|
-
name: string;
|
|
145
|
-
|
|
146
|
-
/** Description from the definition. */
|
|
147
|
-
description?: string;
|
|
148
|
-
|
|
149
|
-
/** The cron schedule expression. */
|
|
150
|
-
schedule: string;
|
|
151
|
-
|
|
152
|
-
/** Whether the job is currently enabled. */
|
|
153
|
-
enabled: boolean;
|
|
154
|
-
|
|
155
|
-
/** Current run state. */
|
|
156
|
-
state: CronJobRunState;
|
|
157
|
-
|
|
158
|
-
/** ISO timestamp of the last execution start. */
|
|
159
|
-
lastRunAt?: string;
|
|
160
|
-
|
|
161
|
-
/** ISO timestamp of the next scheduled execution. */
|
|
162
|
-
nextRunAt?: string;
|
|
163
|
-
|
|
164
|
-
/** Duration of the last run in milliseconds. */
|
|
165
|
-
lastDurationMs?: number;
|
|
166
|
-
|
|
167
|
-
/** Error message from the last failed run. */
|
|
168
|
-
lastError?: string;
|
|
169
|
-
|
|
170
|
-
/** Total number of executions since server start. */
|
|
171
|
-
totalRuns: number;
|
|
172
|
-
|
|
173
|
-
/** Total number of failed executions since server start. */
|
|
174
|
-
totalFailures: number;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// =============================================================================
|
|
178
|
-
// CRON JOB LOG ENTRY
|
|
179
|
-
// =============================================================================
|
|
180
|
-
|
|
181
|
-
export type CronLogLevel = "info" | "error" | "warn";
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* A single execution log entry stored in the in-memory ring buffer.
|
|
185
|
-
*/
|
|
186
|
-
export interface CronJobLogEntry {
|
|
187
|
-
/** The job ID this log belongs to. */
|
|
188
|
-
jobId: string;
|
|
189
|
-
|
|
190
|
-
/** ISO timestamp when execution started. */
|
|
191
|
-
startedAt: string;
|
|
192
|
-
|
|
193
|
-
/** ISO timestamp when execution finished. */
|
|
194
|
-
finishedAt: string;
|
|
195
|
-
|
|
196
|
-
/** Duration in milliseconds. */
|
|
197
|
-
durationMs: number;
|
|
198
|
-
|
|
199
|
-
/** Whether this run succeeded. */
|
|
200
|
-
success: boolean;
|
|
201
|
-
|
|
202
|
-
/** Error message if the run failed. */
|
|
203
|
-
error?: string;
|
|
204
|
-
|
|
205
|
-
/** Arbitrary result data returned by the handler. */
|
|
206
|
-
result?: unknown;
|
|
207
|
-
|
|
208
|
-
/** Captured log lines. */
|
|
209
|
-
logs: string[];
|
|
210
|
-
|
|
211
|
-
/** Whether this was a manual trigger. */
|
|
212
|
-
manual?: boolean;
|
|
213
|
-
}
|
package/src/types/data_source.ts
DELETED
|
@@ -1,357 +0,0 @@
|
|
|
1
|
-
import { ALL_WHERE_FILTER_OPS, WhereFilterOp } from "./filter-operators";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Describes the capabilities and features supported by a data source (driver).
|
|
5
|
-
*
|
|
6
|
-
* Each driver (Postgres, Firebase, MongoDB, etc.) declares which features it
|
|
7
|
-
* supports. The admin uses this descriptor to:
|
|
8
|
-
* - Show/hide editor tabs (e.g. Relations for SQL, Subcollections for Firebase)
|
|
9
|
-
* - Filter the property type picker (e.g. `relation` for SQL, `reference` for Firebase)
|
|
10
|
-
* - Toggle driver-specific form controls (e.g. `columnType` for SQL)
|
|
11
|
-
*
|
|
12
|
-
* @group Models
|
|
13
|
-
*/
|
|
14
|
-
export interface DataSourceCapabilities {
|
|
15
|
-
/** Unique driver key (e.g. "postgres", "firestore", "mongodb") */
|
|
16
|
-
key: string;
|
|
17
|
-
|
|
18
|
-
/** Human-readable label for the UI (e.g. "PostgreSQL", "Firebase / Firestore") */
|
|
19
|
-
label: string;
|
|
20
|
-
|
|
21
|
-
// ── Feature flags ─────────────────────────────────────────────────
|
|
22
|
-
/** Does this source support SQL-style relations (JOINs)? */
|
|
23
|
-
supportsRelations: boolean;
|
|
24
|
-
|
|
25
|
-
/** Does this source support nested subcollections? */
|
|
26
|
-
supportsSubcollections: boolean;
|
|
27
|
-
|
|
28
|
-
/** Does this source support Row Level Security policies? */
|
|
29
|
-
supportsRLS: boolean;
|
|
30
|
-
|
|
31
|
-
/** Does this source support document references (Firebase-style)? */
|
|
32
|
-
supportsReferences: boolean;
|
|
33
|
-
|
|
34
|
-
/** Does this source support SQL column type annotations? */
|
|
35
|
-
supportsColumnTypes: boolean;
|
|
36
|
-
|
|
37
|
-
/** Does this source support real-time listeners? */
|
|
38
|
-
supportsRealtime: boolean;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Does this source store vectors natively?
|
|
42
|
-
*
|
|
43
|
-
* `VectorProperty` carries a `dimensions` and is pgvector-shaped. It was
|
|
44
|
-
* the one driver-specific property kind with no flag to gate it, so unlike
|
|
45
|
-
* every other field in this descriptor there was not even a runtime answer
|
|
46
|
-
* to appeal to — a Firestore collection could declare an embedding column
|
|
47
|
-
* and no driver would do anything with it.
|
|
48
|
-
*/
|
|
49
|
-
supportsVectors: boolean;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Canonical filter operators this engine can execute.
|
|
53
|
-
*
|
|
54
|
-
* The admin UI intersects this set with the property-type defaults and
|
|
55
|
-
* any per-property narrowing (`property.ui.filterOperators`) to decide
|
|
56
|
-
* which operators to offer in filter fields — so an engine that cannot
|
|
57
|
-
* run `ilike` (e.g. Firestore) never shows a "Contains" filter that
|
|
58
|
-
* would throw at query time.
|
|
59
|
-
*/
|
|
60
|
-
filterOperators: readonly WhereFilterOp[];
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Relation kinds this engine's driver can compile into a filter.
|
|
64
|
-
*
|
|
65
|
-
* Only `belongsTo` puts a column on the row being filtered; the others are
|
|
66
|
-
* answered with a correlated subquery over the junction or the target
|
|
67
|
-
* table, which not every driver can build. An engine with no relations at
|
|
68
|
-
* all declares none.
|
|
69
|
-
*
|
|
70
|
-
* The admin uses this to decide whether a relation column offers a filter
|
|
71
|
-
* control. Offering one an engine cannot answer is not cosmetic: a driver
|
|
72
|
-
* that drops the key it cannot resolve *widens* the read to every row, and
|
|
73
|
-
* one that fails closed answers a control the admin itself put on screen
|
|
74
|
-
* with a 400.
|
|
75
|
-
*
|
|
76
|
-
* Optional, so a third-party driver registered before this existed still
|
|
77
|
-
* compiles. Omitted means {@link DEFAULT_FILTERABLE_RELATION_KINDS} — the
|
|
78
|
-
* one kind that is a plain column comparison, which every relational
|
|
79
|
-
* driver can do. The subquery kinds are a real capability and have to be
|
|
80
|
-
* claimed rather than assumed: assuming them wrongly is the widening.
|
|
81
|
-
*/
|
|
82
|
-
filterableRelationKinds?: readonly string[];
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Can a filter address a *column of the related row* — `applications.status`
|
|
86
|
-
* — rather than only the related row's id?
|
|
87
|
-
*
|
|
88
|
-
* A separate capability from {@link filterableRelationKinds} because it is
|
|
89
|
-
* a separate subquery: the id filter stops at the junction, one of these
|
|
90
|
-
* reaches the target table and compares one of its columns. A driver can
|
|
91
|
-
* do the first and not the second.
|
|
92
|
-
*
|
|
93
|
-
* Optional and defaulting to **false**, for the reason the relation kinds
|
|
94
|
-
* default narrow: an unclaimed capability that the admin assumes is there
|
|
95
|
-
* produces a control whose query the driver answers by dropping the key —
|
|
96
|
-
* and a dropped filter key widens the read to every row.
|
|
97
|
-
*
|
|
98
|
-
* Meaningless without {@link supportsRelations}; a driver with no relations
|
|
99
|
-
* has nothing to reach through.
|
|
100
|
-
*/
|
|
101
|
-
supportsRelationFieldFilters?: boolean;
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Can a sort key be an aggregate over a to-many relation — "oldest waiting
|
|
105
|
-
* first", "busiest first"?
|
|
106
|
-
*
|
|
107
|
-
* Compiled as a correlated scalar subquery in `ORDER BY`, which a document
|
|
108
|
-
* store cannot express at all. Optional and defaulting to **false**.
|
|
109
|
-
*
|
|
110
|
-
* A wrongly claimed sort capability fails differently from a wrongly
|
|
111
|
-
* claimed filter one, and worse in one respect: a driver that cannot
|
|
112
|
-
* resolve the key drops the `ORDER BY` and answers 200 with rows in
|
|
113
|
-
* whatever order the database pleased, which reads as a sorted list. Paging
|
|
114
|
-
* over that repeats and skips rows.
|
|
115
|
-
*/
|
|
116
|
-
relationAggregateSorts?: boolean;
|
|
117
|
-
|
|
118
|
-
// ── Admin capability flags ───────────────────────────────────────
|
|
119
|
-
/** Does this source support SQL admin operations (SQL editor, EXPLAIN, etc.)? */
|
|
120
|
-
supportsSQLAdmin: boolean;
|
|
121
|
-
|
|
122
|
-
/** Does this source support document admin operations (aggregation, stats)? */
|
|
123
|
-
supportsDocumentAdmin: boolean;
|
|
124
|
-
|
|
125
|
-
/** Does this source support schema admin (unmapped tables, table metadata)? */
|
|
126
|
-
supportsSchemaAdmin: boolean;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Subset of DataSourceCapabilities containing only feature flags.
|
|
131
|
-
* Useful when you only need to check capabilities without UI metadata.
|
|
132
|
-
* @group Models
|
|
133
|
-
*/
|
|
134
|
-
export type DataSourceFeatures = Omit<DataSourceCapabilities, "key" | "label">;
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* The default data-source key, used when a collection does not name a
|
|
138
|
-
* `dataSource`. Shared by the frontend router and the backend driver
|
|
139
|
-
* registry so both agree on "the default database".
|
|
140
|
-
* @group Models
|
|
141
|
-
*/
|
|
142
|
-
export const DEFAULT_DATA_SOURCE_KEY = "(default)";
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* How the *frontend* reaches a data source.
|
|
146
|
-
*
|
|
147
|
-
* - `"server"` — through the Rebase backend (the `RebaseClient`). The backend
|
|
148
|
-
* holds the actual database adapter and routes by data-source key. This is
|
|
149
|
-
* the default and covers Postgres, MongoDB, and any other server-mediated
|
|
150
|
-
* engine.
|
|
151
|
-
* - `"direct"` — straight from the client to the external backend via its own
|
|
152
|
-
* SDK driver (e.g. Firestore). The Rebase backend is not in the data path.
|
|
153
|
-
* - `"custom"` — a developer-supplied {@link DataDriver}, transport unspecified.
|
|
154
|
-
*
|
|
155
|
-
* @group Models
|
|
156
|
-
*/
|
|
157
|
-
export type DataSourceTransport = "server" | "direct" | "custom";
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Declarative definition of a data source — a named place data lives.
|
|
161
|
-
*
|
|
162
|
-
* Declared once and shared front and back: the frontend uses it to decide
|
|
163
|
-
* transport (client vs direct driver), the backend uses the same `key` to
|
|
164
|
-
* resolve a database adapter, and the editor derives capabilities from
|
|
165
|
-
* `engine`. Collections reference a definition by its `key` via
|
|
166
|
-
* `collection.dataSource`.
|
|
167
|
-
*
|
|
168
|
-
* @group Models
|
|
169
|
-
*/
|
|
170
|
-
export interface DataSourceDefinition {
|
|
171
|
-
/**
|
|
172
|
-
* Unique identifier for this data source. Collections point at it via
|
|
173
|
-
* `dataSource`. Defaults to {@link DEFAULT_DATA_SOURCE_KEY}.
|
|
174
|
-
*/
|
|
175
|
-
key: string;
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* The engine backing this data source (e.g. `"postgres"`, `"mongodb"`,
|
|
179
|
-
* `"firestore"`, or a custom id). Determines the
|
|
180
|
-
* {@link DataSourceCapabilities} surfaced in the editor.
|
|
181
|
-
*/
|
|
182
|
-
engine: string;
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* How the frontend reaches this source. Optional — when omitted it is
|
|
186
|
-
* inferred: `"direct"` if the definition carries a client-side driver,
|
|
187
|
-
* `"server"` otherwise.
|
|
188
|
-
*/
|
|
189
|
-
transport?: DataSourceTransport;
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* The physical database/schema/Firestore-database within the engine.
|
|
193
|
-
* Threaded to drivers/adapters as the existing `databaseId` runtime
|
|
194
|
-
* parameter. Defaults to the engine's own default.
|
|
195
|
-
*/
|
|
196
|
-
databaseId?: string;
|
|
197
|
-
|
|
198
|
-
/** Human-readable label for the UI. */
|
|
199
|
-
label?: string;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
/**
|
|
203
|
-
* The resolved data source for a collection: the single source of truth that
|
|
204
|
-
* the frontend router, backend registry, and editor all derive from.
|
|
205
|
-
* Produced by `resolveDataSource(collection, registry)`.
|
|
206
|
-
*
|
|
207
|
-
* @group Models
|
|
208
|
-
*/
|
|
209
|
-
export interface ResolvedDataSource {
|
|
210
|
-
/** Data-source key (routing key, shared front + back). */
|
|
211
|
-
key: string;
|
|
212
|
-
/** Engine backing the source (drives capabilities). */
|
|
213
|
-
engine: string;
|
|
214
|
-
/** Frontend transport. */
|
|
215
|
-
transport: DataSourceTransport;
|
|
216
|
-
/** Within-engine instance, if any (the `databaseId` runtime param). */
|
|
217
|
-
databaseId?: string;
|
|
218
|
-
/** Capabilities derived from {@link engine}. */
|
|
219
|
-
capabilities: DataSourceCapabilities;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Relation kinds assumed filterable when a driver does not say.
|
|
224
|
-
*
|
|
225
|
-
* `belongsTo` alone: its filter is a comparison on a column of the row being
|
|
226
|
-
* filtered, the one shape that needs no query construction a driver might not
|
|
227
|
-
* have. Everything else is a correlated subquery over another table.
|
|
228
|
-
*
|
|
229
|
-
* @group Models
|
|
230
|
-
*/
|
|
231
|
-
export const DEFAULT_FILTERABLE_RELATION_KINDS: readonly string[] = ["belongsTo"];
|
|
232
|
-
|
|
233
|
-
// ── Built-in driver capabilities ─────────────────────────────────────
|
|
234
|
-
|
|
235
|
-
/** @group Models */
|
|
236
|
-
export const POSTGRES_CAPABILITIES: DataSourceCapabilities = {
|
|
237
|
-
key: "postgres",
|
|
238
|
-
label: "PostgreSQL",
|
|
239
|
-
supportsRelations: true,
|
|
240
|
-
supportsSubcollections: false,
|
|
241
|
-
supportsRLS: true,
|
|
242
|
-
supportsReferences: false,
|
|
243
|
-
supportsColumnTypes: true,
|
|
244
|
-
supportsRealtime: true,
|
|
245
|
-
supportsVectors: true,
|
|
246
|
-
filterOperators: ALL_WHERE_FILTER_OPS,
|
|
247
|
-
// `via` is absent: its join path is authored source → target with no
|
|
248
|
-
// stated inverse, so the driver has nothing to reverse into a filter.
|
|
249
|
-
filterableRelationKinds: ["belongsTo", "manyToMany", "hasMany", "hasOne"],
|
|
250
|
-
supportsRelationFieldFilters: true,
|
|
251
|
-
relationAggregateSorts: true,
|
|
252
|
-
supportsSQLAdmin: true,
|
|
253
|
-
supportsDocumentAdmin: false,
|
|
254
|
-
supportsSchemaAdmin: true
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
/** @group Models */
|
|
258
|
-
export const FIREBASE_CAPABILITIES: DataSourceCapabilities = {
|
|
259
|
-
key: "firestore",
|
|
260
|
-
label: "Firebase / Firestore",
|
|
261
|
-
supportsRelations: false,
|
|
262
|
-
supportsSubcollections: true,
|
|
263
|
-
supportsRLS: false,
|
|
264
|
-
supportsReferences: true,
|
|
265
|
-
supportsColumnTypes: false,
|
|
266
|
-
supportsRealtime: true,
|
|
267
|
-
supportsVectors: false,
|
|
268
|
-
// Firestore has no SQL pattern matching — the driver throws on the LIKE
|
|
269
|
-
// family, so the UI must never offer it.
|
|
270
|
-
filterOperators: ALL_WHERE_FILTER_OPS.filter(op =>
|
|
271
|
-
op !== "like" && op !== "ilike" && op !== "not-like" && op !== "not-ilike"),
|
|
272
|
-
// No relations at all — a document store links by reference. Nothing to
|
|
273
|
-
// reach through, so neither of the two relation-reaching features either.
|
|
274
|
-
filterableRelationKinds: [],
|
|
275
|
-
supportsRelationFieldFilters: false,
|
|
276
|
-
relationAggregateSorts: false,
|
|
277
|
-
supportsSQLAdmin: false,
|
|
278
|
-
supportsDocumentAdmin: false,
|
|
279
|
-
supportsSchemaAdmin: false
|
|
280
|
-
};
|
|
281
|
-
|
|
282
|
-
/** @group Models */
|
|
283
|
-
export const MONGODB_CAPABILITIES: DataSourceCapabilities = {
|
|
284
|
-
key: "mongodb",
|
|
285
|
-
label: "MongoDB",
|
|
286
|
-
supportsRelations: false,
|
|
287
|
-
supportsSubcollections: true,
|
|
288
|
-
supportsRLS: false,
|
|
289
|
-
supportsReferences: true,
|
|
290
|
-
supportsColumnTypes: false,
|
|
291
|
-
supportsRealtime: false,
|
|
292
|
-
supportsVectors: false,
|
|
293
|
-
filterOperators: ALL_WHERE_FILTER_OPS,
|
|
294
|
-
filterableRelationKinds: [],
|
|
295
|
-
supportsRelationFieldFilters: false,
|
|
296
|
-
relationAggregateSorts: false,
|
|
297
|
-
supportsSQLAdmin: false,
|
|
298
|
-
supportsDocumentAdmin: true,
|
|
299
|
-
supportsSchemaAdmin: true
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
/**
|
|
303
|
-
* Fallback capabilities when the driver is unknown.
|
|
304
|
-
* Enables everything so nothing is hidden unexpectedly.
|
|
305
|
-
* @group Models
|
|
306
|
-
*/
|
|
307
|
-
export const DEFAULT_CAPABILITIES: DataSourceCapabilities = {
|
|
308
|
-
key: "(default)",
|
|
309
|
-
label: "Default",
|
|
310
|
-
supportsRelations: true,
|
|
311
|
-
supportsSubcollections: true,
|
|
312
|
-
supportsRLS: true,
|
|
313
|
-
supportsReferences: true,
|
|
314
|
-
supportsColumnTypes: true,
|
|
315
|
-
supportsRealtime: true,
|
|
316
|
-
supportsVectors: true,
|
|
317
|
-
filterOperators: ALL_WHERE_FILTER_OPS,
|
|
318
|
-
// The exception to this descriptor's "enable everything" rule. The other
|
|
319
|
-
// flags hide a tab or a picker when they are wrong; this one decides
|
|
320
|
-
// whether a query is sent that an unknown driver may answer by dropping
|
|
321
|
-
// the condition — which returns every row rather than none.
|
|
322
|
-
filterableRelationKinds: DEFAULT_FILTERABLE_RELATION_KINDS,
|
|
323
|
-
// Narrow for the same reason, and more sharply. An unknown driver that is
|
|
324
|
-
// assumed to compile these answers by dropping the key: the filter widens
|
|
325
|
-
// the read to every row, and the sort comes back unordered while looking
|
|
326
|
-
// sorted. Both have to be claimed.
|
|
327
|
-
supportsRelationFieldFilters: false,
|
|
328
|
-
relationAggregateSorts: false,
|
|
329
|
-
supportsSQLAdmin: true,
|
|
330
|
-
supportsDocumentAdmin: true,
|
|
331
|
-
supportsSchemaAdmin: true
|
|
332
|
-
};
|
|
333
|
-
|
|
334
|
-
const CAPABILITIES_REGISTRY: Record<string, DataSourceCapabilities> = {
|
|
335
|
-
postgres: POSTGRES_CAPABILITIES,
|
|
336
|
-
firestore: FIREBASE_CAPABILITIES,
|
|
337
|
-
mongodb: MONGODB_CAPABILITIES,
|
|
338
|
-
"(default)": DEFAULT_CAPABILITIES
|
|
339
|
-
};
|
|
340
|
-
|
|
341
|
-
/**
|
|
342
|
-
* Look up capabilities for a given engine key.
|
|
343
|
-
* If `engine` is undefined or not found, returns `DEFAULT_CAPABILITIES`.
|
|
344
|
-
* @group Models
|
|
345
|
-
*/
|
|
346
|
-
export function getDataSourceCapabilities(engine?: string): DataSourceCapabilities {
|
|
347
|
-
if (!engine) return POSTGRES_CAPABILITIES; // postgres is the default engine
|
|
348
|
-
return CAPABILITIES_REGISTRY[engine] ?? DEFAULT_CAPABILITIES;
|
|
349
|
-
}
|
|
350
|
-
|
|
351
|
-
/**
|
|
352
|
-
* Register custom capabilities for a third-party driver.
|
|
353
|
-
* @group Models
|
|
354
|
-
*/
|
|
355
|
-
export function registerDataSourceCapabilities(capabilities: DataSourceCapabilities): void {
|
|
356
|
-
CAPABILITIES_REGISTRY[capabilities.key] = capabilities;
|
|
357
|
-
}
|