@rebasepro/types 0.8.0 → 0.9.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/README.md +10 -10
- package/dist/controllers/auth.d.ts +1 -1
- package/dist/controllers/client.d.ts +184 -6
- package/dist/controllers/collection_registry.d.ts +3 -3
- package/dist/controllers/customization_controller.d.ts +1 -1
- package/dist/controllers/data.d.ts +267 -35
- package/dist/controllers/data_driver.d.ts +50 -37
- package/dist/controllers/index.d.ts +1 -1
- package/dist/controllers/local_config_persistence.d.ts +4 -4
- package/dist/controllers/navigation.d.ts +2 -2
- package/dist/controllers/registry.d.ts +17 -4
- package/dist/controllers/{side_entity_controller.d.ts → side_panel_controller.d.ts} +7 -7
- package/dist/controllers/storage.d.ts +39 -0
- package/dist/errors.d.ts +64 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.es.js +142 -15
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +150 -17
- package/dist/index.umd.js.map +1 -1
- package/dist/rebase_context.d.ts +8 -4
- package/dist/types/auth_adapter.d.ts +4 -1
- package/dist/types/backend.d.ts +26 -26
- package/dist/types/builders.d.ts +2 -2
- package/dist/types/collections.d.ts +62 -63
- package/dist/types/component_overrides.d.ts +61 -3
- package/dist/types/cron.d.ts +10 -1
- package/dist/types/data_source.d.ts +15 -2
- package/dist/types/database_adapter.d.ts +4 -3
- package/dist/types/entities.d.ts +7 -7
- package/dist/types/entity_actions.d.ts +7 -7
- package/dist/types/entity_callbacks.d.ts +39 -39
- package/dist/types/entity_views.d.ts +3 -3
- package/dist/types/filter-operators.d.ts +62 -17
- package/dist/types/modify_collections.d.ts +2 -2
- package/dist/types/plugins.d.ts +9 -9
- package/dist/types/policy.d.ts +64 -10
- package/dist/types/properties.d.ts +41 -9
- package/dist/types/relations.d.ts +3 -3
- package/dist/types/slots.d.ts +14 -14
- package/dist/types/websockets.d.ts +12 -13
- package/dist/users/user.d.ts +21 -9
- package/package.json +1 -1
- package/src/controllers/auth.tsx +1 -1
- package/src/controllers/client.ts +214 -6
- package/src/controllers/collection_registry.ts +3 -3
- package/src/controllers/customization_controller.tsx +1 -1
- package/src/controllers/data.ts +290 -39
- package/src/controllers/data_driver.ts +49 -40
- package/src/controllers/index.ts +1 -1
- package/src/controllers/local_config_persistence.tsx +4 -4
- package/src/controllers/navigation.ts +2 -2
- package/src/controllers/registry.ts +18 -4
- package/src/controllers/{side_entity_controller.tsx → side_panel_controller.tsx} +7 -7
- package/src/controllers/storage.ts +60 -1
- package/src/errors.ts +80 -0
- package/src/index.ts +1 -0
- package/src/rebase_context.tsx +8 -4
- package/src/types/auth_adapter.ts +4 -1
- package/src/types/backend.ts +36 -36
- package/src/types/builders.ts +2 -2
- package/src/types/collections.ts +78 -79
- package/src/types/component_overrides.ts +72 -5
- package/src/types/cron.ts +10 -1
- package/src/types/data_source.ts +24 -2
- package/src/types/database_adapter.ts +4 -3
- package/src/types/entities.ts +7 -7
- package/src/types/entity_actions.tsx +7 -7
- package/src/types/entity_callbacks.ts +42 -42
- package/src/types/entity_views.tsx +3 -3
- package/src/types/filter-operators.ts +96 -23
- package/src/types/modify_collections.tsx +2 -2
- package/src/types/plugins.tsx +9 -9
- package/src/types/policy.ts +64 -8
- package/src/types/properties.ts +44 -9
- package/src/types/relations.ts +3 -3
- package/src/types/slots.tsx +14 -14
- package/src/types/websockets.ts +12 -14
- package/src/users/user.ts +22 -9
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Entity, EntityValues } from "../types/entities";
|
|
2
|
-
import { WhereFilterOp, FilterValues } from "../types/filter-operators";
|
|
2
|
+
import { WhereFilterOp, FilterValues, OrderByTuple } from "../types/filter-operators";
|
|
3
3
|
export type WhereValue<T> = T | T[] | null;
|
|
4
4
|
export interface LogicalCondition {
|
|
5
5
|
type: "and" | "or";
|
|
@@ -13,19 +13,51 @@ export interface FilterCondition {
|
|
|
13
13
|
/**
|
|
14
14
|
* Parameters for querying a collection.
|
|
15
15
|
*
|
|
16
|
+
* ## How the filter parameters combine
|
|
17
|
+
*
|
|
18
|
+
* `where`, `logical`, and `searchString` are **independent** and, when more
|
|
19
|
+
* than one is present, are combined with **AND** — every clause must match.
|
|
20
|
+
* Concretely the backend builds:
|
|
21
|
+
*
|
|
22
|
+
* ```text
|
|
23
|
+
* (where filters, AND-ed together)
|
|
24
|
+
* AND (logical group)
|
|
25
|
+
* AND (searchString matches, OR-ed across searchable columns)
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* So `where` does **not** conflict with or override `logical` — they stack.
|
|
29
|
+
* If you need `where` fields OR-ed with each other, move them into `logical`
|
|
30
|
+
* instead. There is no way to OR `where` against `logical`; express anything
|
|
31
|
+
* that isn't a plain AND of the three groups inside a single `logical` tree.
|
|
32
|
+
*
|
|
33
|
+
* ## Pagination precedence
|
|
34
|
+
*
|
|
35
|
+
* `limit`/`offset` and `page` describe the same window two ways. If **both
|
|
36
|
+
* `offset` and `page` are provided, `page` wins** — the backend computes
|
|
37
|
+
* `offset = (page - 1) * (limit ?? 20)` and ignores the explicit `offset`.
|
|
38
|
+
* Pick one style per query.
|
|
39
|
+
*
|
|
16
40
|
* @group Data
|
|
17
41
|
*/
|
|
18
42
|
export interface FindParams {
|
|
19
|
-
/** Maximum number of items to return (default: 20) */
|
|
43
|
+
/** Maximum number of items to return (default: 20). */
|
|
20
44
|
limit?: number;
|
|
21
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* Number of items to skip. Ignored when {@link FindParams.page} is also
|
|
47
|
+
* set — `page` takes precedence.
|
|
48
|
+
*/
|
|
22
49
|
offset?: number;
|
|
23
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* Page number (1-indexed), alternative to {@link FindParams.offset}.
|
|
52
|
+
* When set, overrides `offset` as `(page - 1) * (limit ?? 20)`.
|
|
53
|
+
*/
|
|
24
54
|
page?: number;
|
|
25
55
|
/**
|
|
26
56
|
* Filter conditions keyed by field name.
|
|
27
57
|
* Each value is a `[WhereFilterOp, value]` tuple or an array of tuples
|
|
28
|
-
* for multiple conditions on the same field.
|
|
58
|
+
* for multiple conditions on the same field. Multiple fields, and multiple
|
|
59
|
+
* tuples on one field, are **AND-ed**; also AND-ed with `logical` and
|
|
60
|
+
* `searchString` when present (see the interface docs).
|
|
29
61
|
*
|
|
30
62
|
* @example
|
|
31
63
|
* { status: ["==", "active"] }
|
|
@@ -34,16 +66,24 @@ export interface FindParams {
|
|
|
34
66
|
* { age: [[">=", 18], ["<", 65]] }
|
|
35
67
|
*/
|
|
36
68
|
where?: FilterValues<string>;
|
|
37
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Logical grouping conditions (AND/OR). Use this for anything `where`
|
|
71
|
+
* can't express — notably OR-ing conditions. AND-ed with `where` and
|
|
72
|
+
* `searchString` when present (see the interface docs).
|
|
73
|
+
*/
|
|
38
74
|
logical?: LogicalCondition;
|
|
39
75
|
/**
|
|
40
|
-
* Sort order
|
|
41
|
-
* @example "created_at
|
|
76
|
+
* Sort order as a `[field, direction]` tuple.
|
|
77
|
+
* @example orderBy: ["created_at", "desc"]
|
|
42
78
|
*/
|
|
43
|
-
orderBy?:
|
|
79
|
+
orderBy?: OrderByTuple;
|
|
44
80
|
/** Relations to include in the response */
|
|
45
81
|
include?: string[];
|
|
46
|
-
/**
|
|
82
|
+
/**
|
|
83
|
+
* Full-text search string. Matched (OR-ed) across the collection's
|
|
84
|
+
* searchable columns, then AND-ed with `where`/`logical`. This is the
|
|
85
|
+
* value behind the query builder's `.search()` method.
|
|
86
|
+
*/
|
|
47
87
|
searchString?: string;
|
|
48
88
|
}
|
|
49
89
|
/**
|
|
@@ -62,7 +102,13 @@ export interface FindResponse<M extends Record<string, unknown> = Record<string,
|
|
|
62
102
|
};
|
|
63
103
|
}
|
|
64
104
|
/**
|
|
65
|
-
* Fluent
|
|
105
|
+
* Fluent query builder for the **admin CMS** — resolves to `FindResponse<M>`
|
|
106
|
+
* (Snapshot-wrapped rows).
|
|
107
|
+
*
|
|
108
|
+
* @internal App developers should use {@link SDKQueryBuilderInterface}
|
|
109
|
+
* (flat rows, returned by `client.data.*` / `context.data.*`). This
|
|
110
|
+
* Snapshot-flavored variant backs the admin CMS internals only.
|
|
111
|
+
*
|
|
66
112
|
* @group Data
|
|
67
113
|
*/
|
|
68
114
|
export interface QueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
@@ -77,11 +123,13 @@ export interface QueryBuilderInterface<M extends Record<string, unknown> = Recor
|
|
|
77
123
|
listen(onUpdate: (data: FindResponse<M>) => void, onError?: (error: Error) => void): () => void;
|
|
78
124
|
}
|
|
79
125
|
/**
|
|
80
|
-
* A single collection's CRUD accessor
|
|
126
|
+
* A single collection's CRUD accessor for the **admin CMS** — every method
|
|
127
|
+
* resolves to `Snapshot`-wrapped rows (`FindResponse<M>` / `Snapshot<M>`).
|
|
81
128
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
129
|
+
* @internal App developers do **not** use this. The public, symmetric surface
|
|
130
|
+
* is {@link SDKCollectionClient} (flat rows), exposed as `client.data.products`
|
|
131
|
+
* in the SDK and `context.data.products` in framework callbacks. This
|
|
132
|
+
* Snapshot-flavored accessor backs the admin CMS view-model only.
|
|
85
133
|
*
|
|
86
134
|
* @group Data
|
|
87
135
|
*/
|
|
@@ -110,10 +158,6 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
|
|
|
110
158
|
* Delete a record by ID.
|
|
111
159
|
*/
|
|
112
160
|
delete(id: string | number): Promise<void>;
|
|
113
|
-
/**
|
|
114
|
-
* Delete all records in this collection.
|
|
115
|
-
*/
|
|
116
|
-
deleteAll?(): Promise<void>;
|
|
117
161
|
/**
|
|
118
162
|
* Subscribe to a collection for real-time updates.
|
|
119
163
|
* Optional method, may not be supported by all implementations (like stateless HTTP clients).
|
|
@@ -137,27 +181,138 @@ export interface CollectionAccessor<M extends Record<string, unknown> = Record<s
|
|
|
137
181
|
include(...relations: string[]): QueryBuilderInterface<M>;
|
|
138
182
|
}
|
|
139
183
|
/**
|
|
140
|
-
*
|
|
184
|
+
* Pagination metadata returned with collection queries.
|
|
185
|
+
* @group Data
|
|
186
|
+
*/
|
|
187
|
+
export interface PaginationMeta {
|
|
188
|
+
total: number;
|
|
189
|
+
limit: number;
|
|
190
|
+
offset: number;
|
|
191
|
+
hasMore: boolean;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Paginated response from a collection query (SDK-facing).
|
|
195
|
+
* Returns flat rows instead of Entity-wrapped objects.
|
|
141
196
|
*
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
197
|
+
* @example
|
|
198
|
+
* const { data, meta } = await rebase.data.posts.find();
|
|
199
|
+
* console.log(data[0].title); // direct access — no .values
|
|
200
|
+
* console.log(meta.total);
|
|
201
|
+
*
|
|
202
|
+
* @group Data
|
|
203
|
+
*/
|
|
204
|
+
export interface FindResult<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
205
|
+
/** Array of flat rows matching the query */
|
|
206
|
+
data: M[];
|
|
207
|
+
/** Pagination metadata */
|
|
208
|
+
meta: PaginationMeta;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Fluent Query Builder Interface for the SDK client.
|
|
212
|
+
* Returns `FindResult<M>` (flat rows) instead of `FindResponse<M>` (Entity-wrapped).
|
|
213
|
+
*
|
|
214
|
+
* @group Data
|
|
215
|
+
*/
|
|
216
|
+
export interface SDKQueryBuilderInterface<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
217
|
+
where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): this;
|
|
218
|
+
where(logicalCondition: LogicalCondition): this;
|
|
219
|
+
orderBy(column: keyof M & string, direction?: "asc" | "desc"): this;
|
|
220
|
+
limit(count: number): this;
|
|
221
|
+
offset(count: number): this;
|
|
222
|
+
search(searchString: string): this;
|
|
223
|
+
include(...relations: string[]): this;
|
|
224
|
+
find(): Promise<FindResult<M>>;
|
|
225
|
+
count(): Promise<number>;
|
|
226
|
+
listen(onUpdate: (data: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* SDK collection client — returns flat rows, no Entity wrapper.
|
|
230
|
+
*
|
|
231
|
+
* This is the public API surface for app developers using
|
|
232
|
+
* `createRebaseClient()`. CMS internals use `CollectionAccessor` instead.
|
|
233
|
+
*
|
|
234
|
+
* Type parameters:
|
|
235
|
+
* - `M` — the **Row** shape returned by reads (`find`, `findById`, `listen`).
|
|
236
|
+
* - `I` — the **Insert** shape accepted by {@link create}. Defaults to
|
|
237
|
+
* `Partial<M>`; the generated SDK supplies a dedicated `Insert` type where
|
|
238
|
+
* required columns are required and auto-generated / read-only columns are
|
|
239
|
+
* omitted, so `create({})` on a table with required fields is a compile error.
|
|
240
|
+
* - `U` — the **Update** shape accepted by {@link update}. Defaults to
|
|
241
|
+
* `Partial<M>`; the generated SDK supplies a dedicated `Update` type.
|
|
145
242
|
*
|
|
146
243
|
* @example
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
244
|
+
* const { data: posts } = await rebase.data.posts.find();
|
|
245
|
+
* console.log(posts[0].title); // flat access
|
|
246
|
+
* console.log(posts[0].id); // id at top level
|
|
150
247
|
*
|
|
151
|
-
*
|
|
152
|
-
*
|
|
153
|
-
* afterSave({ context }) {
|
|
154
|
-
* await context.data.logs.create({ action: "saved", timestamp: new Date() });
|
|
155
|
-
* }
|
|
156
|
-
* }
|
|
248
|
+
* const post = await rebase.data.posts.findById(1);
|
|
249
|
+
* console.log(post?.title); // no .values needed
|
|
157
250
|
*
|
|
158
251
|
* @group Data
|
|
159
252
|
*/
|
|
160
|
-
export interface
|
|
253
|
+
export interface SDKCollectionClient<M extends Record<string, unknown> = Record<string, unknown>, I = Partial<M>, U = Partial<M>> {
|
|
254
|
+
/**
|
|
255
|
+
* Find multiple records with optional filtering, pagination, and sorting.
|
|
256
|
+
*/
|
|
257
|
+
find(params?: FindParams): Promise<FindResult<M>>;
|
|
258
|
+
/**
|
|
259
|
+
* Find a single record by its ID.
|
|
260
|
+
*/
|
|
261
|
+
findById(id: string | number): Promise<M | undefined>;
|
|
262
|
+
/**
|
|
263
|
+
* Create a new record.
|
|
264
|
+
* @param data The record data to create (the collection's `Insert` shape).
|
|
265
|
+
* @param id Optional specific ID to use for the new record.
|
|
266
|
+
* @returns The created row
|
|
267
|
+
*/
|
|
268
|
+
create(data: I, id?: string | number): Promise<M>;
|
|
269
|
+
/**
|
|
270
|
+
* Update an existing record by ID.
|
|
271
|
+
* @param data The fields to update (the collection's `Update` shape).
|
|
272
|
+
* @returns The updated row.
|
|
273
|
+
* @throws {RebaseApiError} with status 404 when the record does not exist.
|
|
274
|
+
*/
|
|
275
|
+
update(id: string | number, data: U): Promise<M>;
|
|
276
|
+
/**
|
|
277
|
+
* Delete a record by ID.
|
|
278
|
+
* @throws {RebaseApiError} with status 404 when the record does not exist.
|
|
279
|
+
*/
|
|
280
|
+
delete(id: string | number): Promise<void>;
|
|
281
|
+
/**
|
|
282
|
+
* Subscribe to a collection for real-time updates.
|
|
283
|
+
*/
|
|
284
|
+
listen?(params: FindParams | undefined, onUpdate: (response: FindResult<M>) => void, onError?: (error: Error) => void): () => void;
|
|
285
|
+
/**
|
|
286
|
+
* Subscribe to a single record for real-time updates.
|
|
287
|
+
*/
|
|
288
|
+
listenById?(id: string | number, onUpdate: (row: M | undefined) => void, onError?: (error: Error) => void): () => void;
|
|
289
|
+
/**
|
|
290
|
+
* Count the number of records matching the given filter.
|
|
291
|
+
*/
|
|
292
|
+
count?(params?: FindParams): Promise<number>;
|
|
293
|
+
where<K extends keyof M & string>(column: K, operator: WhereFilterOp, value: WhereValue<M[K]>): SDKQueryBuilderInterface<M>;
|
|
294
|
+
where(logicalCondition: LogicalCondition): SDKQueryBuilderInterface<M>;
|
|
295
|
+
orderBy(column: keyof M & string, direction?: "asc" | "desc"): SDKQueryBuilderInterface<M>;
|
|
296
|
+
limit(count: number): SDKQueryBuilderInterface<M>;
|
|
297
|
+
offset(count: number): SDKQueryBuilderInterface<M>;
|
|
298
|
+
search(searchString: string): SDKQueryBuilderInterface<M>;
|
|
299
|
+
include(...relations: string[]): SDKQueryBuilderInterface<M>;
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The unified data access object for the **admin CMS** (Entity-shaped).
|
|
303
|
+
*
|
|
304
|
+
* Access collections as dynamic properties: `data.products.find(...)`. Each
|
|
305
|
+
* accessor returns `Entity`-wrapped records (`{ id, path, values }`) — the
|
|
306
|
+
* view-model the CMS renders. This is what `useData()` / the admin
|
|
307
|
+
* `RebaseContext.data` are backed by.
|
|
308
|
+
*
|
|
309
|
+
* @internal App developers do **not** use this — they use
|
|
310
|
+
* {@link RebaseSdkData} (flat rows), which is what the SDK client and backend
|
|
311
|
+
* `context.data` expose. This Entity-shaped map backs the admin CMS only.
|
|
312
|
+
*
|
|
313
|
+
* @group Data
|
|
314
|
+
*/
|
|
315
|
+
export type RebaseData<DB = unknown> = {
|
|
161
316
|
/**
|
|
162
317
|
* Get a collection accessor by slug.
|
|
163
318
|
* Alternative to dynamic property access for cases where
|
|
@@ -168,12 +323,89 @@ export interface RebaseData {
|
|
|
168
323
|
* await accessor.find({ limit: 10 });
|
|
169
324
|
*/
|
|
170
325
|
collection<M extends Record<string, unknown> = Record<string, unknown>>(slug: string): CollectionAccessor<M>;
|
|
326
|
+
} & (DB extends Record<string, unknown> ? {
|
|
327
|
+
[K in keyof DB]: CollectionAccessor<DB[K] extends {
|
|
328
|
+
Row: infer R extends Record<string, unknown>;
|
|
329
|
+
} ? R : Record<string, unknown>>;
|
|
330
|
+
} : {
|
|
171
331
|
/**
|
|
172
332
|
* Dynamic collection accessor.
|
|
173
333
|
* Access any collection by its slug as a property.
|
|
174
334
|
*
|
|
175
335
|
* @example
|
|
176
|
-
* data.products.find({ where: { status: "
|
|
336
|
+
* data.products.find({ where: { status: ["==", "published"] } })
|
|
177
337
|
*/
|
|
178
338
|
[collectionSlug: string]: CollectionAccessor | ((slug: string) => CollectionAccessor);
|
|
179
|
-
}
|
|
339
|
+
});
|
|
340
|
+
/**
|
|
341
|
+
* The unified data access object for the **SDK** — flat rows, no Entity wrapper.
|
|
342
|
+
*
|
|
343
|
+
* This is the symmetric developer-facing data API, identical in shape on both
|
|
344
|
+
* sides of the stack:
|
|
345
|
+
* - The frontend SDK client (`client.data.products.find()`)
|
|
346
|
+
* - Backend framework callbacks & scripts (`context.data.products.find()`)
|
|
347
|
+
*
|
|
348
|
+
* Every accessor returns flat rows (`{ id, ...columns }`) via
|
|
349
|
+
* {@link SDKCollectionClient} — access fields directly (`row.title`), never
|
|
350
|
+
* `row.values.title`. The admin CMS uses {@link RebaseData} (Entity) instead.
|
|
351
|
+
*
|
|
352
|
+
* @example
|
|
353
|
+
* // Frontend SDK
|
|
354
|
+
* const { data: posts } = await client.data.posts.find();
|
|
355
|
+
* console.log(posts[0].title); // flat — no .values
|
|
356
|
+
*
|
|
357
|
+
* // Backend callback — identical shape
|
|
358
|
+
* callbacks: {
|
|
359
|
+
* beforeSave: async ({ context }) => {
|
|
360
|
+
* const product = await context.data.products.findById(id);
|
|
361
|
+
* console.log(product?.price); // flat — no .values
|
|
362
|
+
* }
|
|
363
|
+
* }
|
|
364
|
+
*
|
|
365
|
+
* @group Data
|
|
366
|
+
*/
|
|
367
|
+
/**
|
|
368
|
+
* Extract the `Row` shape from a generated `Database[slug]` entry, falling
|
|
369
|
+
* back to an open record when the entry is untyped.
|
|
370
|
+
* @group Data
|
|
371
|
+
*/
|
|
372
|
+
export type RowOf<T> = T extends {
|
|
373
|
+
Row: infer R extends Record<string, unknown>;
|
|
374
|
+
} ? R : Record<string, unknown>;
|
|
375
|
+
/**
|
|
376
|
+
* Extract the `Insert` shape from a generated `Database[slug]` entry (the
|
|
377
|
+
* input accepted by `create`), falling back to `Partial<Row>`.
|
|
378
|
+
* @group Data
|
|
379
|
+
*/
|
|
380
|
+
export type InsertOf<T> = T extends {
|
|
381
|
+
Insert: infer I extends Record<string, unknown>;
|
|
382
|
+
} ? I : Partial<RowOf<T>>;
|
|
383
|
+
/**
|
|
384
|
+
* Extract the `Update` shape from a generated `Database[slug]` entry (the
|
|
385
|
+
* input accepted by `update`), falling back to `Partial<Row>`.
|
|
386
|
+
* @group Data
|
|
387
|
+
*/
|
|
388
|
+
export type UpdateOf<T> = T extends {
|
|
389
|
+
Update: infer U extends Record<string, unknown>;
|
|
390
|
+
} ? U : Partial<RowOf<T>>;
|
|
391
|
+
export type RebaseSdkData<DB = unknown> = {
|
|
392
|
+
/**
|
|
393
|
+
* Get a flat collection accessor by slug.
|
|
394
|
+
*
|
|
395
|
+
* @example
|
|
396
|
+
* const accessor = data.collection("products");
|
|
397
|
+
* await accessor.find({ limit: 10 });
|
|
398
|
+
*/
|
|
399
|
+
collection<M extends Record<string, unknown> = Record<string, unknown>>(slug: string): SDKCollectionClient<M>;
|
|
400
|
+
} & (DB extends Record<string, unknown> ? {
|
|
401
|
+
[K in keyof DB]: SDKCollectionClient<RowOf<DB[K]>, InsertOf<DB[K]>, UpdateOf<DB[K]>>;
|
|
402
|
+
} : {
|
|
403
|
+
/**
|
|
404
|
+
* Dynamic flat collection accessor.
|
|
405
|
+
* Access any collection by its slug as a property.
|
|
406
|
+
*
|
|
407
|
+
* @example
|
|
408
|
+
* data.products.find({ where: { status: ["==", "published"] } })
|
|
409
|
+
*/
|
|
410
|
+
[collectionSlug: string]: SDKCollectionClient | ((slug: string) => SDKCollectionClient);
|
|
411
|
+
});
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
1
|
+
import type { EntityStatus, EntityValues } from "../types/entities";
|
|
2
|
+
import type { CollectionConfig, FilterValues } from "../types/collections";
|
|
3
3
|
import type { RebaseContext } from "../rebase_context";
|
|
4
4
|
/**
|
|
5
5
|
* @internal
|
|
6
6
|
*/
|
|
7
|
-
export interface
|
|
7
|
+
export interface FetchOneProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
8
8
|
path: string;
|
|
9
|
-
|
|
9
|
+
id: string | number;
|
|
10
10
|
databaseId?: string;
|
|
11
|
-
collection?:
|
|
11
|
+
collection?: CollectionConfig<M>;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
14
|
* @internal
|
|
15
15
|
*/
|
|
16
|
-
export type
|
|
17
|
-
onUpdate: (
|
|
16
|
+
export type ListenOneProps<M extends Record<string, unknown> = Record<string, unknown>> = FetchOneProps<M> & {
|
|
17
|
+
onUpdate: (row: Record<string, unknown> | null) => void;
|
|
18
18
|
onError?: (error: Error) => void;
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
@@ -37,7 +37,7 @@ export interface VectorSearchParams {
|
|
|
37
37
|
*/
|
|
38
38
|
export interface FetchCollectionProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
39
39
|
path: string;
|
|
40
|
-
collection?:
|
|
40
|
+
collection?: CollectionConfig<M>;
|
|
41
41
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
42
42
|
limit?: number;
|
|
43
43
|
offset?: number;
|
|
@@ -52,38 +52,50 @@ export interface FetchCollectionProps<M extends Record<string, unknown> = Record
|
|
|
52
52
|
* @internal
|
|
53
53
|
*/
|
|
54
54
|
export type ListenCollectionProps<M extends Record<string, unknown> = Record<string, unknown>> = FetchCollectionProps<M> & {
|
|
55
|
-
onUpdate: (
|
|
55
|
+
onUpdate: (rows: Record<string, unknown>[]) => void;
|
|
56
56
|
onError?: (error: Error) => void;
|
|
57
57
|
};
|
|
58
58
|
/**
|
|
59
59
|
* @internal
|
|
60
60
|
*/
|
|
61
|
-
export interface
|
|
61
|
+
export interface SaveProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
62
62
|
path: string;
|
|
63
63
|
values: Partial<EntityValues<M>>;
|
|
64
|
-
|
|
64
|
+
id?: string | number;
|
|
65
65
|
previousValues?: Partial<EntityValues<M>>;
|
|
66
|
-
collection?:
|
|
66
|
+
collection?: CollectionConfig<M>;
|
|
67
67
|
status: EntityStatus;
|
|
68
68
|
}
|
|
69
69
|
/**
|
|
70
70
|
* @internal
|
|
71
71
|
*/
|
|
72
|
-
export interface
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
export interface DeleteProps<M extends Record<string, unknown> = Record<string, unknown>> {
|
|
73
|
+
row: {
|
|
74
|
+
id: string | number;
|
|
75
|
+
path: string;
|
|
76
|
+
values?: Partial<EntityValues<M>>;
|
|
77
|
+
};
|
|
78
|
+
collection?: CollectionConfig<M>;
|
|
75
79
|
}
|
|
76
80
|
export type FilterCombinationValidProps = {
|
|
77
81
|
path: string;
|
|
78
82
|
databaseId?: string;
|
|
79
|
-
collection:
|
|
83
|
+
collection: CollectionConfig;
|
|
80
84
|
filterValues: FilterValues<string>;
|
|
81
85
|
sortBy?: [string, "asc" | "desc"];
|
|
82
86
|
};
|
|
83
87
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
88
|
+
* The integration SPI for plugging a data backend into Rebase.
|
|
89
|
+
*
|
|
90
|
+
* Implement this interface to connect a custom backend (or use a built-in
|
|
91
|
+
* driver such as the Firestore one) and register it on
|
|
92
|
+
* `<Rebase dataSources>`. Rebase wraps drivers via `buildRebaseData` and
|
|
93
|
+
* routes collections to them by their `dataSource` key.
|
|
94
|
+
*
|
|
95
|
+
* For *consuming* data in application code, use `RebaseData` /
|
|
96
|
+
* `context.data` instead — this interface is only for providing it.
|
|
97
|
+
*
|
|
98
|
+
* @group Datasource
|
|
87
99
|
*/
|
|
88
100
|
export interface DataDriver {
|
|
89
101
|
/**
|
|
@@ -97,9 +109,9 @@ export interface DataDriver {
|
|
|
97
109
|
/**
|
|
98
110
|
* Fetch data from a collection
|
|
99
111
|
* @param props
|
|
100
|
-
* @return Promise of
|
|
112
|
+
* @return Promise of flat rows
|
|
101
113
|
*/
|
|
102
|
-
fetchCollection<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<
|
|
114
|
+
fetchCollection<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
|
|
103
115
|
/**
|
|
104
116
|
* Listen to a collection in a given path. If you don't implement this method
|
|
105
117
|
* `fetchCollection` will be used instead, with no real time updates.
|
|
@@ -108,27 +120,27 @@ export interface DataDriver {
|
|
|
108
120
|
*/
|
|
109
121
|
listenCollection?<M extends Record<string, unknown> = Record<string, unknown>>(props: ListenCollectionProps<M>): () => void;
|
|
110
122
|
/**
|
|
111
|
-
* Retrieve
|
|
123
|
+
* Retrieve a single row given a path and a collection
|
|
112
124
|
* @param props
|
|
113
125
|
*/
|
|
114
|
-
|
|
126
|
+
fetchOne<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
|
|
115
127
|
/**
|
|
116
|
-
* Get realtime updates on one
|
|
128
|
+
* Get realtime updates on one row.
|
|
117
129
|
* @param props
|
|
118
130
|
* @return Function to cancel subscription
|
|
119
131
|
*/
|
|
120
|
-
|
|
132
|
+
listenOne?<M extends Record<string, unknown> = Record<string, unknown>>(props: ListenOneProps<M>): () => void;
|
|
121
133
|
/**
|
|
122
|
-
* Save
|
|
134
|
+
* Save a row to the specified path
|
|
123
135
|
* @param props
|
|
124
136
|
*/
|
|
125
|
-
|
|
137
|
+
save<M extends Record<string, unknown> = Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>>;
|
|
126
138
|
/**
|
|
127
|
-
* Delete
|
|
139
|
+
* Delete a entity
|
|
128
140
|
* @param props
|
|
129
141
|
* @return was the whole deletion flow successful
|
|
130
142
|
*/
|
|
131
|
-
|
|
143
|
+
delete<M extends Record<string, unknown> = Record<string, unknown>>(props: DeleteProps<M>): Promise<void>;
|
|
132
144
|
/**
|
|
133
145
|
* Delete all entities from a collection.
|
|
134
146
|
* @param path Collection path
|
|
@@ -139,15 +151,15 @@ export interface DataDriver {
|
|
|
139
151
|
* @param path Collection path
|
|
140
152
|
* @param name of the property
|
|
141
153
|
* @param value
|
|
142
|
-
* @param
|
|
154
|
+
* @param id
|
|
143
155
|
* @param collection
|
|
144
156
|
* @return `true` if there are no other fields besides the given entity
|
|
145
157
|
*/
|
|
146
|
-
checkUniqueField(path: string, name: string, value: unknown,
|
|
158
|
+
checkUniqueField(path: string, name: string, value: unknown, id?: string | number, collection?: CollectionConfig): Promise<boolean>;
|
|
147
159
|
/**
|
|
148
160
|
* Count the number of entities in a collection
|
|
149
161
|
*/
|
|
150
|
-
|
|
162
|
+
count?<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
|
|
151
163
|
/**
|
|
152
164
|
* Check if the given filter combination is valid
|
|
153
165
|
* @param props
|
|
@@ -165,7 +177,7 @@ export interface DataDriver {
|
|
|
165
177
|
context: RebaseContext;
|
|
166
178
|
path: string;
|
|
167
179
|
databaseId?: string;
|
|
168
|
-
collection:
|
|
180
|
+
collection: CollectionConfig;
|
|
169
181
|
parentCollectionSlugs?: string[];
|
|
170
182
|
parentEntityIds?: string[];
|
|
171
183
|
}) => Promise<boolean>;
|
|
@@ -175,7 +187,7 @@ export interface DataDriver {
|
|
|
175
187
|
needsInitTextSearch?: boolean;
|
|
176
188
|
/**
|
|
177
189
|
* Optional REST-optimised fetch service. When present, the REST API
|
|
178
|
-
* generator uses these methods instead of the generic `
|
|
190
|
+
* generator uses these methods instead of the generic `fetchOne` /
|
|
179
191
|
* `fetchCollection` pipeline, enabling include-aware eager-loading.
|
|
180
192
|
*/
|
|
181
193
|
restFetchService?: RestFetchService;
|
|
@@ -191,8 +203,9 @@ export interface DataDriver {
|
|
|
191
203
|
* REST-optimised fetch service exposed by drivers that support
|
|
192
204
|
* eager-loading of relations via `include`.
|
|
193
205
|
*
|
|
194
|
-
* The methods return flattened rows (`{ id, ...columns }`)
|
|
195
|
-
*
|
|
206
|
+
* The methods return flattened rows (`{ id, ...columns }`) with
|
|
207
|
+
* included relations inlined as plain nested rows — the shape served
|
|
208
|
+
* to app developers through the REST API / SDK client.
|
|
196
209
|
*
|
|
197
210
|
* @group DataDriver
|
|
198
211
|
*/
|
|
@@ -214,5 +227,5 @@ export interface RestFetchService {
|
|
|
214
227
|
/**
|
|
215
228
|
* Fetch a single flattened entity with optional relation includes.
|
|
216
229
|
*/
|
|
217
|
-
|
|
230
|
+
fetchOneForRest(collectionPath: string, id: string | number, include?: string[], databaseId?: string): Promise<Record<string, unknown> | null>;
|
|
218
231
|
}
|
|
@@ -11,7 +11,7 @@ export * from "./storage";
|
|
|
11
11
|
export * from "./email";
|
|
12
12
|
export * from "./client";
|
|
13
13
|
export * from "./customization_controller";
|
|
14
|
-
export * from "./
|
|
14
|
+
export * from "./side_panel_controller";
|
|
15
15
|
export * from "./side_dialogs_controller";
|
|
16
16
|
export * from "./dialogs_controller";
|
|
17
17
|
export * from "./snackbar";
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CollectionConfig } from "../types/collections";
|
|
2
2
|
/**
|
|
3
3
|
* @group Models
|
|
4
4
|
*/
|
|
5
|
-
export type
|
|
5
|
+
export type PartialCollectionConfig<M extends Record<string, unknown> = Record<string, unknown>> = Partial<CollectionConfig<M>>;
|
|
6
6
|
/**
|
|
7
7
|
* This interface is in charge of defining the controller that persists
|
|
8
8
|
* modifications to a collection or collection, and retrieves them back from
|
|
9
9
|
* a data source, such as local storage or Firestore.
|
|
10
10
|
*/
|
|
11
11
|
export interface UserConfigurationPersistence {
|
|
12
|
-
onCollectionModified: <M extends Record<string, unknown> = Record<string, unknown>>(path: string, partialCollection:
|
|
13
|
-
getCollectionConfig: <M extends Record<string, unknown> = Record<string, unknown>>(path: string) =>
|
|
12
|
+
onCollectionModified: <M extends Record<string, unknown> = Record<string, unknown>>(path: string, partialCollection: PartialCollectionConfig<M>) => void;
|
|
13
|
+
getCollectionConfig: <M extends Record<string, unknown> = Record<string, unknown>>(path: string) => PartialCollectionConfig<M>;
|
|
14
14
|
recentlyVisitedPaths: string[];
|
|
15
15
|
setRecentlyVisitedPaths: (paths: string[]) => void;
|
|
16
16
|
favouritePaths: string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type {
|
|
2
|
+
import type { CollectionConfig } from "../types/collections";
|
|
3
3
|
/**
|
|
4
4
|
* Controller that handles URL path building and resolution.
|
|
5
5
|
* @group Models
|
|
@@ -212,7 +212,7 @@ export interface NavigationEntry {
|
|
|
212
212
|
name: string;
|
|
213
213
|
slug: string;
|
|
214
214
|
type: "collection" | "view" | "admin";
|
|
215
|
-
collection?:
|
|
215
|
+
collection?: CollectionConfig;
|
|
216
216
|
view?: AppView;
|
|
217
217
|
description?: string;
|
|
218
218
|
group: string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from "react";
|
|
2
|
-
import type {
|
|
3
|
-
import type {
|
|
2
|
+
import type { CollectionConfig } from "../types/collections";
|
|
3
|
+
import type { CollectionConfigsBuilder, AppViewsBuilder } from "../types/builders";
|
|
4
4
|
import type { EntityCustomView } from "../types/entity_views";
|
|
5
5
|
import type { EntityAction } from "../types/entity_actions";
|
|
6
6
|
import type { AppView, NavigationGroupMapping } from "./navigation";
|
|
@@ -19,8 +19,8 @@ export interface CollectionEditorOptions {
|
|
|
19
19
|
/** Suggested base paths shown when creating new collections. */
|
|
20
20
|
pathSuggestions?: string[];
|
|
21
21
|
}
|
|
22
|
-
export interface RebaseCMSConfig<EC extends
|
|
23
|
-
collections?: EC[] |
|
|
22
|
+
export interface RebaseCMSConfig<EC extends CollectionConfig = CollectionConfig> {
|
|
23
|
+
collections?: EC[] | CollectionConfigsBuilder<EC>;
|
|
24
24
|
/**
|
|
25
25
|
* Custom top-level views added to the main navigation.
|
|
26
26
|
* Accepts a static array of views or an async builder function
|
|
@@ -45,6 +45,19 @@ export interface RebaseCMSConfig<EC extends EntityCollection = EntityCollection>
|
|
|
45
45
|
* are all auto-wired — no plugin or manual view injection needed.
|
|
46
46
|
*/
|
|
47
47
|
collectionEditor?: boolean | CollectionEditorOptions;
|
|
48
|
+
/**
|
|
49
|
+
* URL path prefix the admin is mounted under, when it does not live at the
|
|
50
|
+
* site root. Set this when the admin is rendered inside a path-prefixed
|
|
51
|
+
* route (e.g. `<Route path="/admin/*">`) so URL⇄collection resolution
|
|
52
|
+
* accounts for the prefix — otherwise the current path won't be recognized
|
|
53
|
+
* as a collection path and views hang on a spinner with no data fetch.
|
|
54
|
+
*
|
|
55
|
+
* Do NOT set this when mounting via a react-router `basename` — react-router
|
|
56
|
+
* already strips the prefix from the location, so the default (`/`) is correct.
|
|
57
|
+
*
|
|
58
|
+
* @default "/"
|
|
59
|
+
*/
|
|
60
|
+
basePath?: string;
|
|
48
61
|
}
|
|
49
62
|
export interface RebaseStudioConfig {
|
|
50
63
|
tools?: ("sql" | "js" | "rls" | "schema" | "storage" | "cron" | "schema-visualizer" | "branches" | "api" | "logs" | "api-keys")[];
|