@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.
Files changed (71) hide show
  1. package/README.md +4 -0
  2. package/dist/call_context.d.ts +20 -0
  3. package/dist/controllers/client.d.ts +36 -4
  4. package/dist/controllers/data.d.ts +120 -10
  5. package/dist/errors.d.ts +83 -4
  6. package/dist/index.es.js +522 -160
  7. package/dist/index.es.js.map +1 -1
  8. package/dist/types/admin_block.d.ts +2 -2
  9. package/dist/types/auth_adapter.d.ts +41 -6
  10. package/dist/types/backend.d.ts +48 -0
  11. package/dist/types/collections.d.ts +25 -1
  12. package/dist/types/cron.d.ts +34 -0
  13. package/dist/types/database_adapter.d.ts +39 -0
  14. package/dist/types/entity_callbacks.d.ts +14 -1
  15. package/dist/types/filter-operators.d.ts +24 -1
  16. package/dist/types/policy.d.ts +29 -1
  17. package/dist/types/properties.d.ts +216 -3
  18. package/dist/types/relations.d.ts +65 -7
  19. package/dist/types/resource_kinds.d.ts +173 -17
  20. package/dist/types/resources.d.ts +108 -7
  21. package/dist/types/rls-functions.d.ts +11 -0
  22. package/dist/types/storage_source.d.ts +12 -23
  23. package/package.json +24 -23
  24. package/src/call_context.ts +0 -120
  25. package/src/controllers/auth_state.ts +0 -24
  26. package/src/controllers/client.ts +0 -494
  27. package/src/controllers/collection_registry.ts +0 -62
  28. package/src/controllers/data.ts +0 -1012
  29. package/src/controllers/data_driver.ts +0 -576
  30. package/src/controllers/effective_role.ts +0 -4
  31. package/src/controllers/email.ts +0 -91
  32. package/src/controllers/index.ts +0 -11
  33. package/src/controllers/storage.ts +0 -252
  34. package/src/errors.ts +0 -119
  35. package/src/index.ts +0 -5
  36. package/src/types/admin_block.ts +0 -209
  37. package/src/types/api_keys.ts +0 -108
  38. package/src/types/auth_adapter.ts +0 -580
  39. package/src/types/backend.ts +0 -987
  40. package/src/types/backup.ts +0 -26
  41. package/src/types/channel_bus.ts +0 -202
  42. package/src/types/chips.ts +0 -34
  43. package/src/types/collection_contract.ts +0 -278
  44. package/src/types/collections.ts +0 -763
  45. package/src/types/component_ref.ts +0 -92
  46. package/src/types/cron.ts +0 -213
  47. package/src/types/data_source.ts +0 -357
  48. package/src/types/database_adapter.ts +0 -267
  49. package/src/types/entities.ts +0 -226
  50. package/src/types/entity_callbacks.ts +0 -229
  51. package/src/types/filter-operators.ts +0 -444
  52. package/src/types/history.ts +0 -66
  53. package/src/types/index.ts +0 -36
  54. package/src/types/indexes.ts +0 -180
  55. package/src/types/policy.ts +0 -328
  56. package/src/types/postgres_introspection.ts +0 -101
  57. package/src/types/project_manifest.ts +0 -598
  58. package/src/types/properties.ts +0 -1368
  59. package/src/types/relations.ts +0 -417
  60. package/src/types/resource_kinds.ts +0 -390
  61. package/src/types/resources.ts +0 -368
  62. package/src/types/rls-functions.ts +0 -98
  63. package/src/types/schema_editing.ts +0 -157
  64. package/src/types/schema_version.ts +0 -112
  65. package/src/types/search.ts +0 -247
  66. package/src/types/security_rules.ts +0 -344
  67. package/src/types/storage_authorize.ts +0 -77
  68. package/src/types/storage_source.ts +0 -248
  69. package/src/types/websockets.ts +0 -117
  70. package/src/users/index.ts +0 -2
  71. package/src/users/user.ts +0 -69
@@ -1,576 +0,0 @@
1
- import { RebaseApiError } from "../errors";
2
- import type { CollectionRegistryController } from "./collection_registry";
3
- import type { EntityStatus, EntityValues } from "../types/entities";
4
- import type { CollectionConfig, FilterValues } from "../types/collections";
5
- import type { OrderByTuple } from "../types/filter-operators";
6
- import type { RebaseCallContext } from "../call_context";
7
- import type { LogicalCondition } from "./data";
8
-
9
-
10
- /**
11
- * @internal
12
- */
13
- export interface FetchOneProps<M extends Record<string, unknown> = Record<string, unknown>> {
14
- path: string;
15
- id: string | number;
16
- databaseId?: string;
17
- collection?: CollectionConfig<M>
18
- }
19
-
20
- /**
21
- * @internal
22
- */
23
- export type ListenOneProps<M extends Record<string, unknown> = Record<string, unknown>> =
24
- FetchOneProps<M>
25
- & {
26
- onUpdate: (row: Record<string, unknown> | null) => void,
27
- onError?: (error: Error) => void,
28
- }
29
-
30
- /**
31
- * Configuration for vector similarity search queries.
32
- * Vector search applies an ORDER BY distance expression and optionally
33
- * filters results by a distance threshold.
34
- */
35
- export interface VectorSearchParams {
36
- /** Property name containing the vector column */
37
- property: string;
38
- /** Query vector to compare against */
39
- vector: number[];
40
- /** Distance function (default: "cosine") */
41
- distance?: "cosine" | "l2" | "inner_product";
42
- /** Only return results within this distance threshold */
43
- threshold?: number;
44
- }
45
-
46
- // ── List pagination bounds ────────────────────────────────────────────────
47
- //
48
- // Client-driven list reads (REST `GET /<collection>` and the WebSocket
49
- // `subscribe_collection` message) accept a client-supplied `limit`. Without
50
- // bounds, an ABSENT limit streams the entire table into memory — a trivial
51
- // OOM/DoS — and `limit=100000000` (or `limit=0`, historically an unlimited
52
- // bypass) is honoured verbatim. `resolveClientListLimit` is the single shared
53
- // enforcement point so every untrusted ingress behaves identically. Trusted
54
- // server-side callers build fetch options directly and are intentionally NOT
55
- // bounded here (migrations, admin exports, and CDC refetches may need the full
56
- // set).
57
- //
58
- // A limit the platform will not serve is REFUSED, not quietly shrunk. Clamping
59
- // answers a request for 100 000 rows with 1 000 of them, and a short page is
60
- // indistinguishable from "that is all the data there is" — which is how a CSV
61
- // export shipped 50 rows of a 100 000-row collection under a filename that read
62
- // like the whole thing. `meta.total`/`meta.hasMore` make truncation *detectable*
63
- // on the REST list response, but only for a caller who thinks to compare what it
64
- // asked for against what it got, and the WebSocket `collection_update` frame
65
- // carries neither — so signalling cannot be the answer on every surface and
66
- // rejecting is. An ABSENT limit still defaults: naming no window is not the same
67
- // as asking for one that cannot be served.
68
-
69
- /** Rows returned for a plain / text-search list read when the client sends no `limit`. */
70
- export const DEFAULT_LIST_LIMIT = 50;
71
- /** Rows returned for a vector-search list read when the client sends no `limit`. */
72
- export const DEFAULT_VECTOR_LIST_LIMIT = 10;
73
- /** Largest `limit` a client may ask for on any surface. Above it, the read is refused. */
74
- export const MAX_LIST_LIMIT = 1000;
75
-
76
- /** Overridable bounds for {@link resolveClientListLimit}. */
77
- export interface ListLimitBounds {
78
- /** Default page size for plain and text-search reads. */
79
- defaultLimit?: number;
80
- /** Default page size for vector-search reads. */
81
- vectorDefaultLimit?: number;
82
- /** Largest limit a client may ask for. A larger one is rejected, not clamped. */
83
- maxLimit?: number;
84
- }
85
-
86
- /**
87
- * Thrown by {@link resolveClientListLimit} for a `limit` the platform will not
88
- * serve. Carries an HTTP status so an ingress that speaks HTTP can forward it
89
- * verbatim, and `maxLimit` so one can be built without re-deriving the ceiling.
90
- *
91
- * @group Errors
92
- */
93
- export class ListLimitError extends RebaseApiError {
94
- /** The ceiling that was exceeded — what the caller should page by instead. */
95
- readonly maxLimit: number;
96
-
97
- constructor(message: string, maxLimit: number) {
98
- super(message, { status: 400, code: "INVALID_LIMIT" });
99
- this.name = "ListLimitError";
100
- this.maxLimit = maxLimit;
101
- // Keeps `instanceof` working when this is compiled down for an older
102
- // target, where extending a builtin otherwise loses the prototype.
103
- Object.setPrototypeOf(this, ListLimitError.prototype);
104
- }
105
- }
106
-
107
- /**
108
- * Resolve a client-supplied list `limit` into a safe, always-defined value.
109
- *
110
- * - An absent / blank limit falls back to the mode default:
111
- * `vectorDefaultLimit` for a vector search, otherwise `defaultLimit`.
112
- * - A limit that is present must be an integer in `[1, maxLimit]`. Anything
113
- * else — `0`, a negative, `1.5`, `abc`, `100000000` — throws
114
- * {@link ListLimitError} rather than being coerced into range, because every
115
- * coercion answers a question the caller did not ask with a page it cannot
116
- * tell apart from the whole collection.
117
- *
118
- * The return is never `undefined` — no ingress that routes its client limit
119
- * through this can produce an unbounded read.
120
- *
121
- * @throws {ListLimitError} when a present `limit` is not an integer in range.
122
- */
123
- export function resolveClientListLimit(
124
- rawLimit: number | string | null | undefined,
125
- opts: ListLimitBounds & { vectorSearch?: boolean } = {}
126
- ): number {
127
- const maxLimit = opts.maxLimit ?? MAX_LIST_LIMIT;
128
- if (rawLimit != null && String(rawLimit).trim() !== "") {
129
- // `Number`, not `parseInt`: `parseInt("50rows")` is 50, which silently
130
- // reads a typo as a window the caller never wrote.
131
- const parsed = typeof rawLimit === "number" ? rawLimit : Number(String(rawLimit).trim());
132
- if (!Number.isInteger(parsed) || parsed < 1) {
133
- throw new ListLimitError(
134
- `Invalid \`limit\`: ${String(rawLimit)}. Expected a whole number between 1 and ${maxLimit}.`,
135
- maxLimit
136
- );
137
- }
138
- if (parsed > maxLimit) {
139
- throw new ListLimitError(
140
- `\`limit\` ${parsed} is above the maximum of ${maxLimit}. Ask for at most ${maxLimit} rows ` +
141
- "per read and page through the rest with `offset` — answering with a smaller page would be " +
142
- "indistinguishable from there being no more rows.",
143
- maxLimit
144
- );
145
- }
146
- return parsed;
147
- }
148
- return opts.vectorSearch
149
- ? (opts.vectorDefaultLimit ?? DEFAULT_VECTOR_LIST_LIMIT)
150
- : (opts.defaultLimit ?? DEFAULT_LIST_LIMIT);
151
- }
152
-
153
- /**
154
- * @internal
155
- */
156
- export interface FetchCollectionProps<M extends Record<string, unknown> = Record<string, unknown>> {
157
- path: string;
158
- collection?: CollectionConfig<M>;
159
- filter?: FilterValues<Extract<keyof M, string>>,
160
- /**
161
- * An `or(...)`/`and(...)` group, applied alongside `filter`.
162
- *
163
- * The REST layer parsed `?or=` into this and then had nowhere to put it, so
164
- * the group was dropped and the read ran unfiltered — returning every row
165
- * the caller's policies allowed rather than the ones they asked for.
166
- */
167
- logical?: LogicalCondition;
168
- limit?: number;
169
- offset?: number;
170
- startAfter?: unknown;
171
- /**
172
- * The sort, in either of two spellings:
173
- *
174
- * - a field name, whose direction is the separate `order` below — the
175
- * original single-column contract, which every existing driver reads;
176
- * - a list of `[field, direction]` tuples applied in order of significance,
177
- * which carries a multi-column sort and ignores `order` entirely.
178
- *
179
- * `normalizeDriverOrderBy` in `@rebasepro/common` collapses the pair to the
180
- * list form. A driver that has not been taught the list form should read it
181
- * through that helper rather than assume a string: handed an array, `String()`
182
- * would produce a field name like `roles,asc` and the sort would 400 (or,
183
- * with unknown-field warnings on, silently vanish).
184
- */
185
- orderBy?: string | OrderByTuple[];
186
- searchString?: string;
187
- /** Ask each row which declared search field matched — populates `_matches`. */
188
- searchExplain?: boolean;
189
- /** Direction for the string form of `orderBy`. Ignored when `orderBy` is a list. */
190
- order?: "desc" | "asc";
191
- /** Vector similarity search configuration */
192
- vectorSearch?: VectorSearchParams;
193
- }
194
-
195
- /**
196
- * @internal
197
- */
198
- export type ListenCollectionProps<M extends Record<string, unknown> = Record<string, unknown>> =
199
- FetchCollectionProps<M> &
200
- {
201
- onUpdate: (rows: Record<string, unknown>[]) => void;
202
- onError?: (error: Error) => void;
203
- };
204
-
205
- /**
206
- * @internal
207
- */
208
- export interface SaveProps<M extends Record<string, unknown> = Record<string, unknown>> {
209
- path: string;
210
- values: Partial<EntityValues<M>>;
211
- id?: string | number; // can be empty for new entities
212
- previousValues?: Partial<EntityValues<M>>;
213
- collection?: CollectionConfig<M>;
214
- status: EntityStatus;
215
- /**
216
- * Write the row with INSERT ... ON CONFLICT DO UPDATE on the primary key
217
- * instead of choosing between insert and update up front.
218
- *
219
- * One statement, so it does not lose the race a read-then-write can, and it
220
- * succeeds whether or not the row is already there — what a re-runnable
221
- * import needs. Requires every primary key column to be present; without
222
- * them there is no conflict target and the row is inserted normally.
223
- */
224
- upsert?: boolean;
225
- }
226
-
227
- /**
228
- * @internal
229
- */
230
- export interface SaveManyProps<M extends Record<string, unknown> = Record<string, unknown>> {
231
- path: string;
232
- /**
233
- * The rows to write. A row carrying its primary key updates (or, with
234
- * `upsert`, inserts-or-updates) that row; one without inserts.
235
- */
236
- rows: Partial<EntityValues<M>>[];
237
- collection?: CollectionConfig<M>;
238
- /** Apply every row as INSERT ... ON CONFLICT DO UPDATE. See {@link SaveProps.upsert}. */
239
- upsert?: boolean;
240
- }
241
-
242
- /**
243
- * @internal
244
- */
245
- export interface UpdateManyProps<M extends Record<string, unknown> = Record<string, unknown>> {
246
- path: string;
247
- /**
248
- * The rows to update, each named by its address.
249
- *
250
- * Distinct from {@link SaveManyProps.rows}, which carries keys *inside* the
251
- * values and is insert-shaped — `saveMany` passes `status: "new"` and no
252
- * `id`, so it cannot express "update exactly this row". This can, and it is
253
- * why bulk update is a separate driver method rather than a flag on that one.
254
- */
255
- updates: { id: string | number; values: Partial<EntityValues<M>> }[];
256
- collection?: CollectionConfig<M>;
257
- }
258
-
259
- /**
260
- * @internal
261
- */
262
- export interface DeleteProps<M extends Record<string, unknown> = Record<string, unknown>> {
263
- row: { id: string | number; path: string; values?: Partial<EntityValues<M>> };
264
- collection?: CollectionConfig<M>;
265
- }
266
-
267
- /**
268
- * @internal
269
- */
270
- export interface DeleteManyProps<M extends Record<string, unknown> = Record<string, unknown>> {
271
- path: string;
272
- ids: (string | number)[];
273
- collection?: CollectionConfig<M>;
274
- }
275
-
276
- export type FilterCombinationValidProps = {
277
- path: string;
278
- databaseId?: string;
279
- collection: CollectionConfig;
280
- filterValues: FilterValues<string>;
281
- sortBy?: [string, "asc" | "desc"];
282
- };
283
-
284
- /**
285
- * The integration SPI for plugging a data backend into Rebase.
286
- *
287
- * Implement this interface to connect a custom backend (or use a built-in
288
- * driver such as the Firestore one) and register it on
289
- * `<Rebase dataSources>`. Rebase wraps drivers via `buildRebaseData` and
290
- * routes collections to them by their `dataSource` key.
291
- *
292
- * For *consuming* data in application code, use `RebaseData` /
293
- * `context.data` instead — this interface is only for providing it.
294
- *
295
- * @group Datasource
296
- */
297
- export interface DataDriver {
298
-
299
- /**
300
- * Key that identifies this driver
301
- */
302
- key?: string;
303
-
304
- /**
305
- * If the driver has been initialised
306
- */
307
- initialised?: boolean;
308
-
309
- /**
310
- * Fetch data from a collection
311
- * @param props
312
- * @return Promise of flat rows
313
- */
314
- fetchCollection<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<Record<string, unknown>[]>;
315
-
316
- /**
317
- * Listen to a collection in a given path. If you don't implement this method
318
- * `fetchCollection` will be used instead, with no real time updates.
319
- * @param props
320
- * @return Function to cancel subscription
321
- */
322
- listenCollection?<M extends Record<string, unknown> = Record<string, unknown>>(props: ListenCollectionProps<M>): () => void;
323
-
324
- /**
325
- * Retrieve a single row given a path and a collection
326
- * @param props
327
- */
328
- fetchOne<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchOneProps<M>): Promise<Record<string, unknown> | undefined>;
329
-
330
- /**
331
- * Get realtime updates on one row.
332
- * @param props
333
- * @return Function to cancel subscription
334
- */
335
- listenOne?<M extends Record<string, unknown> = Record<string, unknown>>(props: ListenOneProps<M>): () => void;
336
-
337
- /**
338
- * Save a row to the specified path
339
- * @param props
340
- */
341
- save<M extends Record<string, unknown> = Record<string, unknown>>(props: SaveProps<M>): Promise<Record<string, unknown>>;
342
-
343
- /**
344
- * Save many rows as one unit of work.
345
- *
346
- * Every row runs the same pipeline as {@link save} — callbacks, relations
347
- * and row-level security all still apply — but they share a single
348
- * transaction, so the batch either lands whole or not at all. That, and the
349
- * single round trip, is what makes importing tens of thousands of rows
350
- * viable without dropping to raw SQL.
351
- *
352
- * Optional: drivers that cannot do this leave it undefined and callers fall
353
- * back to `save` per row.
354
- */
355
- saveMany?<M extends Record<string, unknown> = Record<string, unknown>>(props: SaveManyProps<M>): Promise<Record<string, unknown>[]>;
356
-
357
- /**
358
- * Update many rows in one transaction, each addressed by id.
359
- *
360
- * Optional for the same reason `saveMany` is: a driver that cannot make the
361
- * batch atomic should not pretend to. The REST layer reports
362
- * `BULK_UNSUPPORTED` rather than silently falling back to a loop of single
363
- * writes, which would be neither atomic nor one round trip — the two things
364
- * a caller reaches for a batch to get.
365
- */
366
- updateMany?<M extends Record<string, unknown> = Record<string, unknown>>(props: UpdateManyProps<M>): Promise<Record<string, unknown>[]>;
367
-
368
- /**
369
- * Delete the row `props.row` addresses.
370
- *
371
- * **Resolving means the row is gone because this call removed it.** A
372
- * delete that matched nothing must reject with a not-found error
373
- * (`ApiError.notFound`, `statusCode: 404`) rather than resolving quietly.
374
- *
375
- * The rule is here rather than in each driver because the two
376
- * implementations answered differently and each had a test pinning its own
377
- * habit: Postgres threw, Mongo logged a warning and resolved. Three things
378
- * decide it in favour of rejecting.
379
- *
380
- * The REST layer already says 404 — `DELETE /api/data/<c>/<id>` reads the
381
- * row before removing it — so a quiet resolve made the driver API disagree
382
- * with the HTTP API about the same operation, and only in-process
383
- * `rebase.data` callers could see the difference.
384
- *
385
- * A caller cannot tell "deleted" from "there was nothing there" without it,
386
- * and those are different facts: one means the caller's model of the data
387
- * was right, the other that it was stale. Silence hands back the wrong one
388
- * and the caller carries on.
389
- *
390
- * And on a driver with row-level security, "matched nothing" is *also* how
391
- * a policy refusal arrives — Postgres filters `DELETE` through `USING`
392
- * rather than raising. A driver that resolves on zero rows therefore
393
- * reports a refused delete as a completed one, which is the defect
394
- * `explainZeroRowWrite` exists to prevent (see `write-denial.ts`).
395
- *
396
- * Conformance for both server drivers lives in
397
- * `packages/server/test/contract/delete-contract.ts`, run by each driver's
398
- * own suite against its own database. `packages/firebase`'s Firestore
399
- * driver does not honour it: `deleteDoc` resolves for a missing document
400
- * and reporting otherwise would cost a read on every delete. It runs in the
401
- * browser against Firestore's own semantics rather than behind
402
- * `rebase.data`, and that exception is stated here rather than left to be
403
- * discovered.
404
- */
405
- delete<M extends Record<string, unknown> = Record<string, unknown>>(props: DeleteProps<M>): Promise<void>;
406
-
407
- /**
408
- * Delete all entities from a collection.
409
- * @param path Collection path
410
- */
411
- deleteAll?(path: string): Promise<void>;
412
-
413
- /**
414
- * Delete many rows in one transaction, addressed by id.
415
- *
416
- * Ids rather than a filter, deliberately — see
417
- * {@link SDKCollectionClient.deleteMany}. Optional, as `saveMany` is.
418
- */
419
- deleteMany?<M extends Record<string, unknown> = Record<string, unknown>>(props: DeleteManyProps<M>): Promise<void>;
420
-
421
- /**
422
- * Check if the given property is unique in the given collection
423
- * @param path Collection path
424
- * @param name of the property
425
- * @param value
426
- * @param id
427
- * @param collection
428
- * @return `true` if there are no other fields besides the given entity
429
- */
430
- checkUniqueField(
431
- path: string,
432
- name: string,
433
- value: unknown,
434
- id?: string | number,
435
- collection?: CollectionConfig
436
- ): Promise<boolean>;
437
-
438
- /**
439
- * Count the number of entities in a collection
440
- */
441
- count?<M extends Record<string, unknown> = Record<string, unknown>>(props: FetchCollectionProps<M>): Promise<number>;
442
-
443
- /**
444
- * Check if the given filter combination is valid
445
- * @param props
446
- */
447
- isFilterCombinationValid?(props: Omit<FilterCombinationValidProps, "collection"> & {
448
- databaseId?: string
449
- }): boolean;
450
-
451
- /**
452
- * Get the object to generate the current time in the driver
453
- */
454
- currentTime?: () => unknown;
455
-
456
- delegateToCMSModel?: (data: unknown) => unknown;
457
-
458
- cmsToDelegateModel?: (data: unknown) => unknown;
459
-
460
- initTextSearch?: (props: {
461
- context: RebaseCallContext,
462
- path: string,
463
- databaseId?: string,
464
- collection: CollectionConfig,
465
- parentCollectionSlugs?: string[];
466
- parentEntityIds?: string[];
467
- }) => Promise<boolean>;
468
-
469
- /**
470
- * Flag to indicate if the driver has requested the initialization of the text search index
471
- */
472
- needsInitTextSearch?: boolean;
473
-
474
- // ── REST fetch capabilities ─────────────────────────────────────────
475
-
476
- /**
477
- * Optional REST-optimised fetch service. When present, the REST API
478
- * generator uses these methods instead of the generic `fetchOne` /
479
- * `fetchCollection` pipeline, enabling include-aware eager-loading.
480
- */
481
- restFetchService?: RestFetchService;
482
-
483
- // ── Admin capabilities ─────────────────────────────────────────────
484
- //
485
- // Admin operations are now modelled as capability-specific interfaces
486
- // (SQLAdmin, DocumentAdmin, SchemaAdmin) in `@rebasepro/types/backend`.
487
- //
488
- // Drivers that support admin features should expose them here.
489
- // Consumers should use the `isSQLAdmin()`, `isSchemaAdmin()` etc.
490
- // type guards to safely narrow the type before calling methods.
491
-
492
- /**
493
- * Return the admin capabilities of this driver.
494
- * @see SQLAdmin
495
- * @see DocumentAdmin
496
- * @see SchemaAdmin
497
- */
498
- admin?: import("../types/backend").DatabaseAdmin;
499
-
500
- }
501
-
502
- /**
503
- * REST-optimised fetch service exposed by drivers that support
504
- * eager-loading of relations via `include`.
505
- *
506
- * The methods return flattened rows — exactly the table's columns, under their
507
- * own names and with the types the database returned — and included relations
508
- * inlined as plain nested rows. This is the shape served to app developers
509
- * through the REST API / SDK client.
510
- *
511
- * No synthesized `id`: identity is a primary key, which may be named anything
512
- * and span several columns, so an address is derived by whoever needs one (see
513
- * `buildCompositeId`) rather than written into the row on top of the data.
514
- *
515
- * @group DataDriver
516
- */
517
- export interface RestFetchService {
518
- /**
519
- * Fetch a collection of flattened entities with optional relation includes.
520
- */
521
- fetchCollectionForRest(
522
- collectionPath: string,
523
- options?: {
524
- filter?: FilterValues<string>;
525
- /** An `or(...)`/`and(...)` group, applied alongside `filter`. */
526
- logical?: LogicalCondition;
527
- /** See `FetchCollectionProps.orderBy`: a field name plus `order`, or a list of tuples. */
528
- orderBy?: string | OrderByTuple[];
529
- order?: "desc" | "asc";
530
- limit?: number;
531
- offset?: number;
532
- startAfter?: Record<string, unknown>;
533
- searchString?: string;
534
- /** Ask each row which declared search fields matched — populates `_matches`. */
535
- searchExplain?: boolean;
536
- databaseId?: string;
537
- vectorSearch?: VectorSearchParams;
538
- },
539
- include?: string[]
540
- ): Promise<Record<string, unknown>[]>;
541
-
542
- /**
543
- * `count`/`sum`/`avg`/`min`/`max` over the rows a filter selects,
544
- * optionally grouped.
545
- *
546
- * Optional, and the REST route answers 501 where a driver does not
547
- * implement it — an aggregate is not a thing to approximate, and an empty
548
- * result set would read as "nothing matched".
549
- *
550
- * Any implementation **must apply the same row-level authorization as a
551
- * read**. An aggregate is an efficient way to learn about rows you cannot
552
- * select, and `count(*)` over a table whose policies would return nothing
553
- * has to be zero.
554
- */
555
- aggregate?(
556
- collectionPath: string,
557
- options: {
558
- aggregates: { fn: "count" | "sum" | "avg" | "min" | "max"; field?: string; alias: string }[];
559
- groupBy?: string[];
560
- filter?: FilterValues<string>;
561
- logical?: LogicalCondition;
562
- searchString?: string;
563
- limit?: number;
564
- }
565
- ): Promise<Record<string, unknown>[]>;
566
-
567
- /**
568
- * Fetch a single flattened entity with optional relation includes.
569
- */
570
- fetchOneForRest(
571
- collectionPath: string,
572
- id: string | number,
573
- include?: string[],
574
- databaseId?: string
575
- ): Promise<Record<string, unknown> | null>;
576
- }
@@ -1,4 +0,0 @@
1
- export interface EffectiveRoleController {
2
- effectiveRole: string | null;
3
- setEffectiveRole: (role: string | null) => void;
4
- }
@@ -1,91 +0,0 @@
1
- /**
2
- * Email service types — portable interface shared by RebaseClient and server.
3
- *
4
- * The concrete SMTP implementation lives in `@rebasepro/server/email`.
5
- * This file provides only the consumer-facing contract so that it can be
6
- * referenced from `RebaseClient` without dragging in nodemailer.
7
- */
8
-
9
- /**
10
- * Options for sending an email via the Rebase email service.
11
- */
12
- export interface EmailSendOptions {
13
- /** Recipient email address(es). */
14
- to: string | string[];
15
- /** Email subject line. */
16
- subject: string;
17
- /** HTML body content. */
18
- html: string;
19
- /** Optional plain-text fallback. */
20
- text?: string;
21
- /** Optional reply-to address. */
22
- replyTo?: string;
23
- /**
24
- * Additional headers, verbatim.
25
- *
26
- * The reason this exists is that several things a real sender must do are
27
- * only expressible as headers, and without a passthrough an application had
28
- * to choose between not doing them and not using this interface:
29
- *
30
- * - `List-Unsubscribe` and `List-Unsubscribe-Post`, which give a mail client
31
- * its own one-click opt-out. The large providers weigh their presence when
32
- * deciding whether bulk mail reaches an inbox at all.
33
- * - `In-Reply-To` and `References`, without which a reply is a new thread.
34
- *
35
- * **Values are validated, not escaped.** A value containing CR or LF is
36
- * rejected rather than sanitised, because a newline in a header value ends
37
- * the header and starts a new one — so a field built from user input is an
38
- * injection point for `Bcc:` and anything else. Rejecting is the only safe
39
- * response: silently stripping the newline would deliver a message the
40
- * caller did not write, and neither would tell them.
41
- */
42
- headers?: Record<string, string>;
43
- }
44
-
45
- /**
46
- * What the provider reported about a message it accepted.
47
- *
48
- * Every field is optional because not every backend reports them: a custom
49
- * `sendEmail` function that posts to an HTTP API may know nothing beyond "no
50
- * error". An absent `messageId` therefore means "not reported", never "not
51
- * sent" — the absence of an id is not a delivery failure, which is signalled by
52
- * a thrown error.
53
- */
54
- export interface EmailSendResult {
55
- /**
56
- * The message's RFC 5322 Message-ID, **without** angle brackets.
57
- *
58
- * Stripped because this is an identifier to store and compare — against a
59
- * reply's `In-Reply-To`, most often — and a value that sometimes carries
60
- * brackets and sometimes does not is a bug waiting in every comparison.
61
- * Re-add them when writing it into a header: `<${messageId}>`.
62
- */
63
- messageId?: string;
64
- /** Recipients the provider accepted, when it says. */
65
- accepted?: string[];
66
- /** Recipients the provider refused, when it says. A non-empty list is not an error. */
67
- rejected?: string[];
68
- }
69
-
70
- /**
71
- * Abstraction over an email delivery backend.
72
- *
73
- * Implementations may use SMTP, AWS SES, Resend, Postmark, or any other
74
- * provider — consumers only interact through this interface.
75
- */
76
- export interface EmailService {
77
- /**
78
- * Send a single email.
79
- *
80
- * Resolves with what the provider reported (see {@link EmailSendResult});
81
- * throws on failure. It returned `void` before 0.17: an application that
82
- * sent a message had no way to learn the id the server assigned it, so
83
- * threading a reply back to the message that prompted it was impossible
84
- * through this interface. Callers that do not care may still ignore it.
85
- */
86
- send(options: EmailSendOptions): Promise<EmailSendResult>;
87
- /** Returns `true` when the service has valid credentials / is ready to send. */
88
- isConfigured(): boolean;
89
- /** Verify connection/credentials with the email provider. */
90
- verifyConnection?(): Promise<boolean>;
91
- }
@@ -1,11 +0,0 @@
1
- export * from "./collection_registry";
2
- export * from "./auth_state";
3
-
4
- export * from "./data";
5
- export * from "./data_driver";
6
-
7
- export * from "./effective_role";
8
-
9
- export * from "./storage";
10
- export * from "./email";
11
- export * from "./client";