@rebasepro/types 0.9.1-canary.ff338b5 → 0.10.1-canary.0a881d4

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 (101) hide show
  1. package/dist/call_context.d.ts +51 -0
  2. package/dist/controllers/auth_state.d.ts +23 -0
  3. package/dist/controllers/client.d.ts +7 -5
  4. package/dist/controllers/collection_registry.d.ts +5 -1
  5. package/dist/controllers/data_driver.d.ts +31 -2
  6. package/dist/controllers/index.d.ts +1 -10
  7. package/dist/index.d.ts +1 -1
  8. package/dist/index.es.js +375 -2
  9. package/dist/index.es.js.map +1 -1
  10. package/dist/types/admin_block.d.ts +34 -0
  11. package/dist/types/auth_adapter.d.ts +48 -4
  12. package/dist/types/backend.d.ts +77 -1
  13. package/dist/types/channel_bus.d.ts +192 -0
  14. package/dist/types/collection_contract.d.ts +43 -0
  15. package/dist/types/collections.d.ts +18 -785
  16. package/dist/types/component_ref.d.ts +39 -6
  17. package/dist/types/entity_callbacks.d.ts +1 -1
  18. package/dist/types/index.d.ts +7 -15
  19. package/dist/types/policy.d.ts +1 -1
  20. package/dist/types/project_manifest.d.ts +336 -0
  21. package/dist/types/properties.d.ts +12 -157
  22. package/dist/types/schema_version.d.ts +18 -0
  23. package/dist/types/security_rules.d.ts +320 -0
  24. package/dist/types/storage_authorize.d.ts +74 -0
  25. package/dist/types/websockets.d.ts +43 -0
  26. package/package.json +3 -6
  27. package/src/call_context.ts +56 -0
  28. package/src/controllers/auth_state.ts +24 -0
  29. package/src/controllers/client.ts +7 -6
  30. package/src/controllers/collection_registry.ts +5 -1
  31. package/src/controllers/data_driver.ts +58 -2
  32. package/src/controllers/index.ts +1 -10
  33. package/src/index.ts +1 -1
  34. package/src/types/admin_block.ts +75 -0
  35. package/src/types/auth_adapter.ts +52 -4
  36. package/src/types/backend.ts +83 -1
  37. package/src/types/channel_bus.ts +202 -0
  38. package/src/types/collection_contract.ts +278 -0
  39. package/src/types/collections.ts +26 -844
  40. package/src/types/component_ref.ts +40 -6
  41. package/src/types/entity_callbacks.ts +1 -1
  42. package/src/types/index.ts +7 -15
  43. package/src/types/policy.ts +26 -12
  44. package/src/types/project_manifest.ts +362 -0
  45. package/src/types/properties.ts +11 -169
  46. package/src/types/schema_version.ts +112 -0
  47. package/src/types/security_rules.ts +344 -0
  48. package/src/types/storage_authorize.ts +77 -0
  49. package/src/types/websockets.ts +45 -0
  50. package/dist/controllers/analytics_controller.d.ts +0 -7
  51. package/dist/controllers/auth.d.ts +0 -110
  52. package/dist/controllers/customization_controller.d.ts +0 -61
  53. package/dist/controllers/dialogs_controller.d.ts +0 -36
  54. package/dist/controllers/local_config_persistence.d.ts +0 -20
  55. package/dist/controllers/navigation.d.ts +0 -225
  56. package/dist/controllers/registry.d.ts +0 -80
  57. package/dist/controllers/side_dialogs_controller.d.ts +0 -67
  58. package/dist/controllers/side_panel_controller.d.ts +0 -97
  59. package/dist/controllers/snackbar.d.ts +0 -24
  60. package/dist/rebase_context.d.ts +0 -110
  61. package/dist/types/breadcrumbs.d.ts +0 -26
  62. package/dist/types/builders.d.ts +0 -15
  63. package/dist/types/component_overrides.d.ts +0 -196
  64. package/dist/types/entity_actions.d.ts +0 -104
  65. package/dist/types/entity_link_builder.d.ts +0 -7
  66. package/dist/types/entity_views.d.ts +0 -95
  67. package/dist/types/export_import.d.ts +0 -21
  68. package/dist/types/formex.d.ts +0 -40
  69. package/dist/types/locales.d.ts +0 -4
  70. package/dist/types/modify_collections.d.ts +0 -5
  71. package/dist/types/plugins.d.ts +0 -277
  72. package/dist/types/property_config.d.ts +0 -74
  73. package/dist/types/slots.d.ts +0 -262
  74. package/dist/types/translations.d.ts +0 -915
  75. package/dist/types/user_management_delegate.d.ts +0 -22
  76. package/src/controllers/analytics_controller.tsx +0 -57
  77. package/src/controllers/auth.tsx +0 -121
  78. package/src/controllers/customization_controller.tsx +0 -72
  79. package/src/controllers/dialogs_controller.tsx +0 -37
  80. package/src/controllers/local_config_persistence.tsx +0 -22
  81. package/src/controllers/navigation.ts +0 -267
  82. package/src/controllers/registry.ts +0 -95
  83. package/src/controllers/side_dialogs_controller.tsx +0 -82
  84. package/src/controllers/side_panel_controller.tsx +0 -112
  85. package/src/controllers/snackbar.ts +0 -29
  86. package/src/rebase_context.tsx +0 -130
  87. package/src/types/breadcrumbs.ts +0 -27
  88. package/src/types/builders.ts +0 -17
  89. package/src/types/component_overrides.ts +0 -244
  90. package/src/types/entity_actions.tsx +0 -125
  91. package/src/types/entity_link_builder.ts +0 -8
  92. package/src/types/entity_views.tsx +0 -114
  93. package/src/types/export_import.ts +0 -26
  94. package/src/types/formex.ts +0 -45
  95. package/src/types/locales.ts +0 -81
  96. package/src/types/modify_collections.tsx +0 -6
  97. package/src/types/plugins.tsx +0 -346
  98. package/src/types/property_config.tsx +0 -95
  99. package/src/types/slots.tsx +0 -307
  100. package/src/types/translations.ts +0 -1026
  101. package/src/types/user_management_delegate.ts +0 -23
@@ -0,0 +1,51 @@
1
+ import type { StorageSource } from "./controllers/storage";
2
+ import type { RebaseClient } from "./controllers/client";
3
+ import type { RebaseSdkData } from "./controllers/data";
4
+ import type { User } from "./users";
5
+ /**
6
+ * Context that is provided to entity callbacks (hooks).
7
+ * It contains only the dependencies that are available in both the frontend and the backend.
8
+ *
9
+ * This is the *whole* context a collection callback gets, and it lives apart from
10
+ * {@link RebaseContext} on purpose. `RebaseContext` widens it with nine admin-panel
11
+ * controllers — navigation, side dialogs, snackbars — none of which exist in a
12
+ * backend process. Keeping them in one type meant every backend module that
13
+ * touched a callback signature transitively named the admin UI.
14
+ *
15
+ * @group Hooks and utilities
16
+ */
17
+ export type RebaseCallContext<USER extends User = User> = {
18
+ /**
19
+ * The Rebase client instance.
20
+ * Available in all entity callbacks (beforeSave, afterSave, afterRead,
21
+ * beforeDelete, afterDelete) and in CollectionActionsProps via context.
22
+ * Use it to call backend functions, access data, storage, etc.
23
+ *
24
+ * @example
25
+ * // In a beforeSave callback:
26
+ * const result = await context.client.functions.invoke('my-function', { ... });
27
+ *
28
+ * @example
29
+ * // In a CollectionAction component:
30
+ * const { client } = props.context;
31
+ * const result = await client.functions.invoke('extract-job', { url });
32
+ */
33
+ client: RebaseClient;
34
+ /**
35
+ * Unified data access — `context.data.products.create(...)`.
36
+ * Access any collection as a dynamic property.
37
+ *
38
+ * Returns flat rows (`{ id, ...columns }`), identical to the frontend SDK
39
+ * client — so `context.data` in a backend callback and `client.data` in the
40
+ * frontend behave the same way (`row.title`, never `row.values.title`).
41
+ */
42
+ data: RebaseSdkData;
43
+ /**
44
+ * Used storage implementation
45
+ */
46
+ storageSource: StorageSource;
47
+ /**
48
+ * Set by the backend when callbacks are executed on the server.
49
+ */
50
+ user?: USER;
51
+ };
@@ -0,0 +1,23 @@
1
+ import type { User } from "../users";
2
+ /**
3
+ * The read-only slice of authentication state that property resolution needs.
4
+ *
5
+ * `dynamicProps`, `conditions` and the JSON-Logic condition context all want the
6
+ * same one thing: who is asking. They used to be handed the entire
7
+ * {@link AuthController} — `signOut`, `googleLogin`, `authLoading`, thirty-odd
8
+ * members of frontend machinery — which meant `properties.ts` named a frontend
9
+ * controller, and so did `resolveProperty` in `@rebasepro/common`, which the
10
+ * Postgres schema generator calls at build time. A backend generating DDL had to
11
+ * satisfy a type with a login method in it.
12
+ *
13
+ * `User` already carries `roles` and `metadata`, so this is not a reduction in
14
+ * what a dynamic property can decide on — only in what it has to be given.
15
+ * An `AuthController` satisfies this structurally, so the frontend passes the
16
+ * controller it already has.
17
+ *
18
+ * @group Hooks and utilities
19
+ */
20
+ export interface AuthState<USER extends User = User> {
21
+ /** The signed-in user, or `null` when nobody is. */
22
+ user: USER | null;
23
+ }
@@ -131,7 +131,7 @@ export interface AdminAPI {
131
131
  limit: number;
132
132
  offset: number;
133
133
  }>;
134
- getUser(userId: string): Promise<{
134
+ getUser(uid: string): Promise<{
135
135
  user: AdminUser;
136
136
  }>;
137
137
  createUser(data: {
@@ -143,7 +143,7 @@ export interface AdminAPI {
143
143
  }): Promise<{
144
144
  user: AdminUser;
145
145
  }>;
146
- updateUser(userId: string, data: {
146
+ updateUser(uid: string, data: {
147
147
  email?: string;
148
148
  displayName?: string;
149
149
  password?: string;
@@ -152,10 +152,10 @@ export interface AdminAPI {
152
152
  }): Promise<{
153
153
  user: AdminUser;
154
154
  }>;
155
- deleteUser(userId: string): Promise<{
155
+ deleteUser(uid: string): Promise<{
156
156
  success: boolean;
157
157
  }>;
158
- resetPassword(userId: string, options?: {
158
+ resetPassword(uid: string, options?: {
159
159
  password?: string;
160
160
  }): Promise<{
161
161
  user: AdminUser;
@@ -391,11 +391,13 @@ export interface RebaseServerClient<DB = unknown> extends RebaseClient<DB> {
391
391
  email: EmailService;
392
392
  /**
393
393
  * Execute raw SQL against the database. Always present server-side for SQL
394
- * engines.
394
+ * engines. Values interpolated into the query should be passed via
395
+ * `params`, referenced as `$1`, `$2`, … placeholders in the query text.
395
396
  */
396
397
  sql(query: string, options?: {
397
398
  database?: string;
398
399
  role?: string;
400
+ params?: unknown[];
399
401
  }): Promise<Record<string, unknown>[]>;
400
402
  }
401
403
  /**
@@ -10,8 +10,12 @@ export type CollectionRegistryController<DB = Record<string, unknown>, EC extend
10
10
  * Each entry relates to a collection in the root database.
11
11
  * Each of the navigation entries in this field
12
12
  * generates an entry in the main menu.
13
+ *
14
+ * `EC`, like {@link getCollection} — this was hardcoded to `CollectionConfig`
15
+ * while `getCollection` honoured the parameter, so the admin panel got its
16
+ * view model from one and the raw contract from the other.
13
17
  */
14
- collections?: CollectionConfig[];
18
+ collections?: EC[];
15
19
  /**
16
20
  * Is the registry ready to be used
17
21
  */
@@ -1,6 +1,6 @@
1
1
  import type { EntityStatus, EntityValues } from "../types/entities";
2
2
  import type { CollectionConfig, FilterValues } from "../types/collections";
3
- import type { RebaseContext } from "../rebase_context";
3
+ import type { RebaseCallContext } from "../call_context";
4
4
  /**
5
5
  * @internal
6
6
  */
@@ -32,6 +32,35 @@ export interface VectorSearchParams {
32
32
  /** Only return results within this distance threshold */
33
33
  threshold?: number;
34
34
  }
35
+ /** Rows returned for a plain / text-search list read when the client sends no `limit`. */
36
+ export declare const DEFAULT_LIST_LIMIT = 50;
37
+ /** Rows returned for a vector-search list read when the client sends no `limit`. */
38
+ export declare const DEFAULT_VECTOR_LIST_LIMIT = 10;
39
+ /** Hard ceiling clamped onto any client-supplied `limit`, on every surface. */
40
+ export declare const MAX_LIST_LIMIT = 1000;
41
+ /** Overridable bounds for {@link resolveClientListLimit}. */
42
+ export interface ListLimitBounds {
43
+ /** Default page size for plain and text-search reads. */
44
+ defaultLimit?: number;
45
+ /** Default page size for vector-search reads. */
46
+ vectorDefaultLimit?: number;
47
+ /** Upper bound clamped onto any client-supplied limit. */
48
+ maxLimit?: number;
49
+ }
50
+ /**
51
+ * Resolve a client-supplied list `limit` into a safe, always-defined value.
52
+ *
53
+ * - A provided limit is coerced to an integer and clamped to `[1, maxLimit]`,
54
+ * so `0`, negatives, and absurd values can never bypass the cap.
55
+ * - An absent / blank / non-numeric limit falls back to the mode default:
56
+ * `vectorDefaultLimit` for a vector search, otherwise `defaultLimit`.
57
+ *
58
+ * The return is never `undefined` — no ingress that routes its client limit
59
+ * through this can produce an unbounded read.
60
+ */
61
+ export declare function resolveClientListLimit(rawLimit: number | string | null | undefined, opts?: ListLimitBounds & {
62
+ vectorSearch?: boolean;
63
+ }): number;
35
64
  /**
36
65
  * @internal
37
66
  */
@@ -211,7 +240,7 @@ export interface DataDriver {
211
240
  delegateToCMSModel?: (data: unknown) => unknown;
212
241
  cmsToDelegateModel?: (data: unknown) => unknown;
213
242
  initTextSearch?: (props: {
214
- context: RebaseContext;
243
+ context: RebaseCallContext;
215
244
  path: string;
216
245
  databaseId?: string;
217
246
  collection: CollectionConfig;
@@ -1,18 +1,9 @@
1
1
  export * from "./collection_registry";
2
- export * from "./analytics_controller";
3
- export * from "./auth";
2
+ export * from "./auth_state";
4
3
  export * from "./data";
5
4
  export * from "./database_admin";
6
5
  export * from "./data_driver";
7
- export * from "./local_config_persistence";
8
- export * from "./navigation";
9
6
  export * from "./effective_role";
10
7
  export * from "./storage";
11
8
  export * from "./email";
12
9
  export * from "./client";
13
- export * from "./customization_controller";
14
- export * from "./side_panel_controller";
15
- export * from "./side_dialogs_controller";
16
- export * from "./dialogs_controller";
17
- export * from "./snackbar";
18
- export * from "./registry";
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export * from "./errors";
2
- export * from "./rebase_context";
2
+ export * from "./call_context";
3
3
  export * from "./types";
4
4
  export * from "./controllers";
5
5
  export * from "./users";
package/dist/index.es.js CHANGED
@@ -264,10 +264,90 @@ function toCanonicalOp(op) {
264
264
  return REST_TO_CANONICAL[op];
265
265
  }
266
266
  //#endregion
267
+ //#region src/types/admin_block.ts
268
+ /**
269
+ * The keys of a collection's admin block, as data.
270
+ *
271
+ * There is no *type* for the block in this package any more, and that is the point:
272
+ * `admin` is not declared on `BaseCollectionConfig` or on any property here, so a
273
+ * BaaS install cannot even write one. `@rebasepro/admin-types` adds the field back by
274
+ * declaration merging, which is why installing it is what makes the admin surface
275
+ * appear.
276
+ *
277
+ * The *list* still has to live here, because three runtime consumers need it and two
278
+ * of them are core — see below.
279
+ */
280
+ /**
281
+ * Every key that belongs inside a collection's `admin` block, as data.
282
+ *
283
+ * The type that describes these fields is `AdminCollectionOptions` in
284
+ * `@rebasepro/admin-types`, and it is erased at build time — but three runtime
285
+ * consumers need the list, and two of them are core:
286
+ *
287
+ * - `serializeCollections`, to drop the block from the contract
288
+ * - the ts-morph schema editor in `@rebasepro/server`, which rewrites collection
289
+ * files on disk from the admin panel and has to know where each key goes. A key
290
+ * missing from this list gets written to the *top level* of the file, where the
291
+ * backend ignores it and the panel never finds it again.
292
+ * - the `collections-admin-block` codemod
293
+ *
294
+ * `@rebasepro/admin-types` re-exports this and asserts it names only real option
295
+ * keys; the count is pinned by a test there.
296
+ *
297
+ * @group Models
298
+ */
299
+ var ADMIN_COLLECTION_KEYS = [
300
+ "Actions",
301
+ "additionalFields",
302
+ "alwaysApplyDefaultValues",
303
+ "components",
304
+ "defaultEntityAction",
305
+ "defaultFilter",
306
+ "defaultSelectedView",
307
+ "defaultSize",
308
+ "defaultViewMode",
309
+ "disableDefaultActions",
310
+ "enabledViews",
311
+ "entityActions",
312
+ "entityViews",
313
+ "exportable",
314
+ "filterPresets",
315
+ "fixedFilter",
316
+ "formAutoSave",
317
+ "formView",
318
+ "group",
319
+ "hideFromNavigation",
320
+ "hideIdFromCollection",
321
+ "hideIdFromForm",
322
+ "icon",
323
+ "includeJsonView",
324
+ "inlineEditing",
325
+ "kanban",
326
+ "listProperties",
327
+ "localChangesBackup",
328
+ "openEntityMode",
329
+ "orderProperty",
330
+ "pagination",
331
+ "previewProperties",
332
+ "propertiesOrder",
333
+ "selectionController",
334
+ "selectionEnabled",
335
+ "sideDialogWidth",
336
+ "sort",
337
+ "titleProperty"
338
+ ];
339
+ //#endregion
267
340
  //#region src/types/collections.ts
268
341
  /**
269
342
  * Type guard for PostgreSQL collections.
270
343
  * Returns true if the collection uses the Postgres engine (or the default engine).
344
+ *
345
+ * Generic over the *input* type, and narrows by intersection rather than
346
+ * replacement. Narrowing to a bare `PostgresCollectionConfig` discarded whatever
347
+ * the caller actually had — most visibly the admin panel's view model, whose
348
+ * flattened presentation fields vanished the moment a collection passed through
349
+ * one of these guards.
350
+ *
271
351
  * @group Models
272
352
  */
273
353
  function isPostgresCollectionConfig(collection) {
@@ -315,7 +395,7 @@ function getDeclaredSubcollections(collection) {
315
395
  /**
316
396
  * The id a request without a logged-in user reports as `auth.uid()`.
317
397
  *
318
- * A user-context request always sets `app.user_id`: blank would read back as
398
+ * A user-context request always sets `app.uid`: blank would read back as
319
399
  * `NULL`, and `NULL` is how the trusted server context is recognised, so an
320
400
  * anonymous visitor would be promoted to server privileges. The driver
321
401
  * therefore substitutes this sentinel at the single chokepoint where the GUC
@@ -416,6 +496,19 @@ function isBranchAdmin(admin) {
416
496
  return !!admin && typeof admin.createBranch === "function";
417
497
  }
418
498
  //#endregion
499
+ //#region src/types/channel_bus.ts
500
+ /**
501
+ * Whether `setting` is an already-constructed transport rather than a request
502
+ * for a built-in one.
503
+ *
504
+ * Structural rather than nominal so that an instance from a *different copy* of
505
+ * `@rebasepro/types` — an entirely normal outcome of a separately versioned
506
+ * transport package — is still recognised.
507
+ */
508
+ function isChannelBusInstance(setting) {
509
+ return typeof setting?.publish === "function";
510
+ }
511
+ //#endregion
419
512
  //#region src/types/data_source.ts
420
513
  /**
421
514
  * The default data-source key, used when a collection does not name a
@@ -541,6 +634,286 @@ function isLazyComponentRef(ref) {
541
634
  return typeof ref === "object" && ref !== null && "__rebaseLazy" in ref && ref.__rebaseLazy === true;
542
635
  }
543
636
  //#endregion
637
+ //#region src/types/project_manifest.ts
638
+ /**
639
+ * Version of the bundle *format* itself.
640
+ *
641
+ * Bumped only when the on-disk layout changes in a way an older runtime could
642
+ * not read. A runtime accepts any bundle whose `bundleFormat` is less than or
643
+ * equal to its own — old bundles keep booting on new runtimes, which is the
644
+ * whole point of separating the artifact from the engine.
645
+ */
646
+ var BUNDLE_FORMAT_VERSION = 1;
647
+ /**
648
+ * The runtime contract major.
649
+ *
650
+ * Distinct from the `@rebasepro/server` package version: the package may release
651
+ * any number of minors and patches while this stays put. It changes only when
652
+ * the bundle/runtime contract breaks compatibility, and a project's
653
+ * `manifest.runtime` range is matched against *this*.
654
+ */
655
+ var RUNTIME_CONTRACT_VERSION = 1;
656
+ /** Header carrying the schema version an SDK was generated from. */
657
+ var SCHEMA_VERSION_HEADER = "x-rebase-schema";
658
+ //#endregion
659
+ //#region src/types/collection_contract.ts
660
+ function isSerializedCollectionRef(value) {
661
+ return typeof value === "object" && value !== null && typeof value.__collectionRef === "string";
662
+ }
663
+ /** Depth limit for the walk — deep enough for real configs, finite for cyclic ones. */
664
+ var MAX_DEPTH = 64;
665
+ /**
666
+ * Resolve whatever a `target` thunk returns down to a collection.
667
+ *
668
+ * A target may be the collection, a module namespace (when the authoring file
669
+ * used `import * as`), or a default-export wrapper. All three appear in real
670
+ * projects, and the SDK generator already unwraps them the same way.
671
+ */
672
+ function unwrapTarget(value) {
673
+ if (!value || typeof value !== "object") return void 0;
674
+ const candidate = value;
675
+ if (candidate.default || candidate.__esModule) {
676
+ const inner = candidate.default;
677
+ if (inner && typeof inner === "object") return inner;
678
+ }
679
+ if (candidate.properties) return value;
680
+ }
681
+ /** The identity a serialized reference uses. Slug first — it is the routing key. */
682
+ function refFor(collection) {
683
+ if (!collection) return void 0;
684
+ const withPath = collection;
685
+ return collection.slug || withPath.path || collection.name;
686
+ }
687
+ function toSerializable(value, seen, depth, state, key) {
688
+ if (depth > MAX_DEPTH) {
689
+ state.truncations++;
690
+ return;
691
+ }
692
+ if (typeof value === "function") {
693
+ if (key === "target") try {
694
+ const ref = refFor(unwrapTarget(value()));
695
+ return ref ? { __collectionRef: ref } : void 0;
696
+ } catch {
697
+ return;
698
+ }
699
+ return;
700
+ }
701
+ if (value === null || typeof value !== "object") return value;
702
+ if (value instanceof Date) return value.toISOString();
703
+ if (value instanceof RegExp) return value.source;
704
+ if (seen.has(value)) {
705
+ state.truncations++;
706
+ return;
707
+ }
708
+ const cached = state.memo.get(value);
709
+ if (cached !== void 0) return cached;
710
+ seen.add(value);
711
+ const truncationsBefore = state.truncations;
712
+ const memoize = (result) => {
713
+ if (result !== void 0 && state.truncations === truncationsBefore) state.memo.set(value, result);
714
+ return result;
715
+ };
716
+ try {
717
+ if (Array.isArray(value)) {
718
+ const items = value.map((item) => toSerializable(item, seen, depth + 1, state)).filter((item) => item !== void 0);
719
+ return memoize(value.length > 0 && items.length === 0 ? void 0 : items);
720
+ }
721
+ if ("$$typeof" in value) return void 0;
722
+ const entries = Object.entries(value);
723
+ const out = {};
724
+ for (const [k, v] of entries) {
725
+ const converted = toSerializable(v, seen, depth + 1, state, k);
726
+ if (converted !== void 0) out[k] = converted;
727
+ }
728
+ if (entries.length > 0 && Object.keys(out).length === 0) return void 0;
729
+ return memoize(out);
730
+ } finally {
731
+ seen.delete(value);
732
+ }
733
+ }
734
+ /**
735
+ * Serialize collections for transport over the contract endpoint.
736
+ *
737
+ * Sorted by slug so the output — and therefore the schema hash computed from it
738
+ * — does not depend on filesystem ordering.
739
+ */
740
+ function serializeCollections(collections) {
741
+ return [...collections].sort((a, b) => String(a.slug ?? "").localeCompare(String(b.slug ?? ""))).map((collection) => toSerializable(withoutAdminBlock(collection), /* @__PURE__ */ new WeakSet(), 0, {
742
+ memo: /* @__PURE__ */ new WeakMap(),
743
+ truncations: 0
744
+ })).filter((c) => c !== void 0);
745
+ }
746
+ /**
747
+ * Drop the admin block before the walk.
748
+ *
749
+ * Nothing downstream of serialization is an admin panel. The contract endpoint
750
+ * feeds remote SDK generation, and `rebase build` writes the result into a bundle
751
+ * manifest that only the backend runtime reads. The block would survive the walk
752
+ * as a husk anyway — its React elements and component functions are dropped
753
+ * individually — and that husk has two costs worth avoiding: it puts every custom
754
+ * component's *file path* on an endpoint whose job is to describe data shapes, and
755
+ * it grows a payload that is fetched and cached per project.
756
+ *
757
+ * Removing it here rather than at each call site means one chokepoint, so a future
758
+ * consumer of `serializeCollections` cannot forget.
759
+ *
760
+ * Child collections carry their own block, so this recurses — stripping only the
761
+ * top level was the mistake `stripNonClientFields` in the contract routes already
762
+ * had to fix once for security rules.
763
+ */
764
+ function withoutAdminBlock(collection) {
765
+ const { admin: _admin, ...rest } = collection;
766
+ const nested = rest;
767
+ if (Array.isArray(nested.subcollections)) nested.subcollections = nested.subcollections.map((child) => withoutAdminBlock(child));
768
+ return rest;
769
+ }
770
+ /**
771
+ * Rebuild collections received from a contract endpoint.
772
+ *
773
+ * Relation refs become real thunks resolving through the returned set, so
774
+ * downstream consumers — the SDK generator above all — see exactly the shape
775
+ * they would have seen had the collections been imported from source.
776
+ *
777
+ * A ref naming a collection that is not in the payload resolves to `undefined`
778
+ * rather than throwing: the generator already tolerates an unresolvable target
779
+ * by falling back to a permissive key type, and a partial contract should still
780
+ * produce a usable SDK.
781
+ */
782
+ function deserializeCollections(payload) {
783
+ const collections = payload.filter((c) => typeof c === "object" && c !== null).map((c) => ({ ...c }));
784
+ const bySlug = /* @__PURE__ */ new Map();
785
+ for (const collection of collections) {
786
+ const ref = refFor(collection);
787
+ if (ref) bySlug.set(ref, collection);
788
+ }
789
+ const rehydrate = (value, depth) => {
790
+ if (depth > MAX_DEPTH || !value || typeof value !== "object") return;
791
+ if (Array.isArray(value)) {
792
+ for (const item of value) rehydrate(item, depth + 1);
793
+ return;
794
+ }
795
+ const record = value;
796
+ for (const [key, child] of Object.entries(record)) {
797
+ if (key === "target" && isSerializedCollectionRef(child)) {
798
+ const slug = child.__collectionRef;
799
+ record.target = () => bySlug.get(slug);
800
+ continue;
801
+ }
802
+ rehydrate(child, depth + 1);
803
+ }
804
+ };
805
+ for (const collection of collections) rehydrate(collection, 0);
806
+ return collections;
807
+ }
808
+ //#endregion
809
+ //#region src/types/schema_version.ts
810
+ /**
811
+ * The schema version stamp.
812
+ *
813
+ * One function, used in three places that must agree or the whole drift-detection
814
+ * story is noise: `rebase build` writes it into a bundle manifest, the runtime
815
+ * serves it from the contract endpoint, and a generated SDK records the value it
816
+ * was built from. If any two of those computed it differently, every client would
817
+ * look permanently out of date.
818
+ *
819
+ * It covers **collections only** — the client's contract is the shape of the
820
+ * data, so editing a hook or a server function must not invalidate every SDK in
821
+ * every repository. That is a deliberate narrowing, not an oversight.
822
+ */
823
+ /** Stable stringify: object keys sorted at every level, so key order cannot alter the hash. */
824
+ function canonicalize(value) {
825
+ if (value === null || typeof value !== "object") return JSON.stringify(value) ?? "null";
826
+ if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
827
+ return `{${Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${JSON.stringify(k)}:${canonicalize(v)}`).join(",")}}`;
828
+ }
829
+ /**
830
+ * Reduce a collection to the parts a generated client is actually built from.
831
+ *
832
+ * The version answers one question — "is this SDK stale?" — so it must change
833
+ * exactly when the generated types could change, and never otherwise. Hashing a
834
+ * whole collection fails both halves of that:
835
+ *
836
+ * - Security rules, callbacks, icons, groups and UI settings do not appear in a
837
+ * generated client, so including them reports perfectly current SDKs as stale.
838
+ * - Worse, they are not stable *inputs*. The runtime applies default security
839
+ * rules when it loads collections, so the same source hashed before and after
840
+ * loading produced two different answers — a build-time stamp that could never
841
+ * match the server that served it.
842
+ *
843
+ * Codegen reads the slug (for the `Database` key and type names), the properties,
844
+ * and the relations. That is the projection.
845
+ */
846
+ function projectForCodegen(collection) {
847
+ const source = collection;
848
+ return {
849
+ slug: collection.slug ?? source.path,
850
+ properties: collection.properties,
851
+ relations: source.relations,
852
+ engine: source.engine,
853
+ dataSource: source.dataSource,
854
+ subcollections: source.subcollections?.map(projectForCodegen)
855
+ };
856
+ }
857
+ /**
858
+ * Compute the canonical string a schema version hashes.
859
+ *
860
+ * Exposed separately so the hashing itself can differ by environment: Node has
861
+ * `crypto`, and callers without it can still compare canonical forms directly.
862
+ */
863
+ function canonicalSchemaPayload(collections) {
864
+ return canonicalize(serializeCollections(collections).map((collection) => projectForCodegen(collection)));
865
+ }
866
+ /**
867
+ * A short, non-cryptographic digest of the canonical payload.
868
+ *
869
+ * FNV-1a style, 64 bits, as two 32-bit halves. This is an identity, not a
870
+ * security boundary: nothing trusts a schema version to prove anything, it only
871
+ * answers "is this the same schema as before". A hand-rolled hash keeps this
872
+ * module free of `node:crypto`, so the identical function runs in the browser,
873
+ * in the CLI, and in the runtime — which is the property that actually matters.
874
+ */
875
+ function computeSchemaVersion(collections) {
876
+ const payload = canonicalSchemaPayload(collections);
877
+ let h1 = 2166136261;
878
+ let h2 = 16777619;
879
+ for (let i = 0; i < payload.length; i++) {
880
+ const code = payload.charCodeAt(i);
881
+ h1 ^= code;
882
+ h1 = h1 + ((h1 << 1) + (h1 << 4) + (h1 << 7) + (h1 << 8) + (h1 << 24)) >>> 0;
883
+ h2 ^= code + i;
884
+ h2 = h2 + ((h2 << 1) + (h2 << 5) + (h2 << 9) + (h2 << 15) + (h2 << 24)) >>> 0;
885
+ }
886
+ const hex = (n) => n.toString(16).padStart(8, "0");
887
+ return `v1:${hex(h1)}${hex(h2)}`;
888
+ }
889
+ //#endregion
890
+ //#region src/controllers/data_driver.ts
891
+ /** Rows returned for a plain / text-search list read when the client sends no `limit`. */
892
+ var DEFAULT_LIST_LIMIT = 50;
893
+ /** Rows returned for a vector-search list read when the client sends no `limit`. */
894
+ var DEFAULT_VECTOR_LIST_LIMIT = 10;
895
+ /** Hard ceiling clamped onto any client-supplied `limit`, on every surface. */
896
+ var MAX_LIST_LIMIT = 1e3;
897
+ /**
898
+ * Resolve a client-supplied list `limit` into a safe, always-defined value.
899
+ *
900
+ * - A provided limit is coerced to an integer and clamped to `[1, maxLimit]`,
901
+ * so `0`, negatives, and absurd values can never bypass the cap.
902
+ * - An absent / blank / non-numeric limit falls back to the mode default:
903
+ * `vectorDefaultLimit` for a vector search, otherwise `defaultLimit`.
904
+ *
905
+ * The return is never `undefined` — no ingress that routes its client limit
906
+ * through this can produce an unbounded read.
907
+ */
908
+ function resolveClientListLimit(rawLimit, opts = {}) {
909
+ const maxLimit = opts.maxLimit ?? 1e3;
910
+ if (rawLimit != null && String(rawLimit).trim() !== "") {
911
+ const parsed = typeof rawLimit === "number" ? rawLimit : parseInt(String(rawLimit), 10);
912
+ if (Number.isFinite(parsed)) return Math.min(Math.max(1, Math.floor(parsed)), maxLimit);
913
+ }
914
+ return opts.vectorSearch ? opts.vectorDefaultLimit ?? 10 : opts.defaultLimit ?? 50;
915
+ }
916
+ //#endregion
544
917
  //#region src/controllers/storage.ts
545
918
  /**
546
919
  * Path prefix that marks an object as **public**. Files stored under this
@@ -568,6 +941,6 @@ function isPublicStoragePath(path) {
568
941
  return p.startsWith("public/") || p.startsWith(`default/public/`);
569
942
  }
570
943
  //#endregion
571
- export { ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, CANONICAL_TO_REST, DEFAULT_CAPABILITIES, DEFAULT_DATA_SOURCE_KEY, DEFAULT_STORAGE_SOURCE_KEY, EntityReference, EntityRelation, FIREBASE_CAPABILITIES, GeoPoint, MONGODB_CAPABILITIES, NULL_OPS, POSTGRES_CAPABILITIES, PUBLIC_STORAGE_PREFIX, REST_TO_CANONICAL, RebaseApiError, RebaseClientError, Vector, getCollectionDataPath, getDataSourceCapabilities, getDeclaredSubcollections, isBranchAdmin, isDocumentAdmin, isFirebaseCollectionConfig, isLazyComponentRef, isMongoDBCollectionConfig, isPostgresCollectionConfig, isPublicStoragePath, isSQLAdmin, isSchemaAdmin, policy, registerDataSourceCapabilities, toCanonicalOp };
944
+ export { ADMIN_COLLECTION_KEYS, ALL_WHERE_FILTER_OPS, ANONYMOUS_USER_ID, BUNDLE_FORMAT_VERSION, CANONICAL_TO_REST, DEFAULT_CAPABILITIES, DEFAULT_DATA_SOURCE_KEY, DEFAULT_LIST_LIMIT, DEFAULT_STORAGE_SOURCE_KEY, DEFAULT_VECTOR_LIST_LIMIT, EntityReference, EntityRelation, FIREBASE_CAPABILITIES, GeoPoint, MAX_LIST_LIMIT, MONGODB_CAPABILITIES, NULL_OPS, POSTGRES_CAPABILITIES, PUBLIC_STORAGE_PREFIX, REST_TO_CANONICAL, RUNTIME_CONTRACT_VERSION, RebaseApiError, RebaseClientError, SCHEMA_VERSION_HEADER, Vector, canonicalSchemaPayload, computeSchemaVersion, deserializeCollections, getCollectionDataPath, getDataSourceCapabilities, getDeclaredSubcollections, isBranchAdmin, isChannelBusInstance, isDocumentAdmin, isFirebaseCollectionConfig, isLazyComponentRef, isMongoDBCollectionConfig, isPostgresCollectionConfig, isPublicStoragePath, isSQLAdmin, isSchemaAdmin, isSerializedCollectionRef, policy, registerDataSourceCapabilities, resolveClientListLimit, serializeCollections, toCanonicalOp };
572
945
 
573
946
  //# sourceMappingURL=index.es.js.map