@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,3 +1,4 @@
1
+ import type { ResourceRef } from "./resources.js";
1
2
  import type { Entity, EntityReference, EntityRelation, EntityValues, GeoPoint, Vector } from "./entities.js";
2
3
  import type { Relation, ResolvedRelation } from "./relations.js";
3
4
  import type { ColorKey, ColorScheme } from "./chips.js";
@@ -77,6 +78,129 @@ export type MongoProperties = {
77
78
  * unknown but the narrowed property constraint must be satisfied.
78
79
  */
79
80
  export type EngineProperties = PostgresProperties | FirebaseProperties | MongoProperties;
81
+ /**
82
+ * What a key the surrounding shape does not declare resolves to.
83
+ *
84
+ * Nothing is assignable to it — it has required members nobody can spell — so
85
+ * the key errors. It is a *named* type rather than `never` purely for the
86
+ * message: TypeScript prints the target, so the compiler reads back the key that
87
+ * is wrong and the keys that would have been right, instead of saying "not
88
+ * assignable to type 'never'".
89
+ *
90
+ * ```
91
+ * Type '{ required: true }' is not assignable to type
92
+ * '{ readonly required: true } & NoSuchKey<"validaton", "name" | "type" | "validation" | …>'.
93
+ * Property 'didYouMean' is missing …
94
+ * ```
95
+ *
96
+ * `Known` is the set of keys that *were* available. TypeScript elides a long
97
+ * union after a few members, so this is a hint and not an exhaustive listing —
98
+ * which is the right trade: the near-miss is usually alphabetically adjacent to
99
+ * the key that was meant, and a wall of 25 names would be read by nobody.
100
+ */
101
+ export type NoSuchKey<K extends PropertyKey, Known extends PropertyKey = never> = {
102
+ /** The key that was written, so the compiler reads it back verbatim. */
103
+ readonly noSuchKey: K;
104
+ /** The keys that were available. Required, so the type is unsatisfiable. */
105
+ readonly didYouMean: Known;
106
+ };
107
+ /**
108
+ * What a property whose `type` the engine does not have resolves to.
109
+ *
110
+ * Same trick as {@link NoSuchKey}, for a different mistake: a `relation` on a
111
+ * Firestore collection, a `vector` on MongoDB.
112
+ *
113
+ * Without it, `ExactProperty<V, never>` is `V & {}` — which is `V`, and
114
+ * therefore no check at all. The gate is normally caught one level up, by the
115
+ * builder's `P extends FirebaseProperties` constraint; this is what catches it
116
+ * when `StrictProperties` is applied to a property map that carries no such
117
+ * constraint, and it reports on the property rather than on the whole map.
118
+ */
119
+ export type PropertyTypeNotOnThisEngine<T> = {
120
+ /** The `type` tag that has no home on this engine. Required, so nothing fits. */
121
+ readonly __rebasePropertyTypeNotOnThisEngine: T;
122
+ };
123
+ /**
124
+ * A relation checked against the concrete member of the union its own `kind`
125
+ * selects, rather than against the union as a whole.
126
+ *
127
+ * `Relation` is closed — `BelongsToRelation` has `localKey` and no
128
+ * `foreignKeyOnTarget`, `HasManyRelation` the reverse — but excess-property
129
+ * checking against a *union* passes any key that any member declares. So
130
+ * `{ kind: "belongsTo", foreignKeyOnTarget: "author_id" }` typechecked, and
131
+ * described a link that cannot exist: the generator reads `localKey`, defaults
132
+ * it to `<relationName>_id`, and the column the author actually named is never
133
+ * looked at. The relation resolves to a different link than the one written.
134
+ *
135
+ * The runtime already refuses this — `validate-config` rejects a link field that
136
+ * does not belong to the kind — so this is the same rule one stage earlier,
137
+ * where it costs a red squiggle instead of a failed boot.
138
+ */
139
+ type StrictRelation<R> = R extends {
140
+ kind: infer K;
141
+ } ? R & {
142
+ [Key in Exclude<keyof R, keyof Extract<Relation, {
143
+ kind: K;
144
+ }>>]: NoSuchKey<Key, keyof Extract<Relation, {
145
+ kind: K;
146
+ }>>;
147
+ } : R;
148
+ /**
149
+ * `V`, plus every key it carries that `Shape` does not declare, typed so it
150
+ * cannot be satisfied.
151
+ *
152
+ * The `relation` block is descended into, because it is the one nested shape
153
+ * that is a closed union rather than a single interface — see
154
+ * {@link StrictRelation}. The rest (`array.of`, `map.properties`, the `admin`
155
+ * options) are left to the weak-type check and the boot validator.
156
+ */
157
+ type ExactProperty<V, Shape> = [Shape] extends [never] ? PropertyTypeNotOnThisEngine<V extends {
158
+ type: infer T;
159
+ } ? T : never> : V & {
160
+ [K in Exclude<keyof V, keyof Shape>]: NoSuchKey<K, keyof Shape>;
161
+ } & (V extends {
162
+ relation: infer R;
163
+ } ? {
164
+ relation: StrictRelation<R>;
165
+ } : unknown);
166
+ /**
167
+ * A property map in which each value is checked against the *concrete* member of
168
+ * the property union that its own `type` tag selects.
169
+ *
170
+ * ### Why this type has to exist
171
+ *
172
+ * `defineCollection` takes `properties: P` where `const P extends Properties`.
173
+ * `Properties` is an index signature, and **every object literal is assignable
174
+ * to an index signature** — extra keys included — so TypeScript's
175
+ * excess-property check never ran inside a property. The consequences were not
176
+ * theoretical:
177
+ *
178
+ * - `validation` misspelled `validaton` compiled, and the field was quietly not
179
+ * required.
180
+ * - `multiline: true` and `markdown: true` written flat — the shape the docs
181
+ * themselves showed — compiled, and then the backend refused to boot with a
182
+ * migration hint pointing at `admin: { … }`.
183
+ * - `multiSelect: true`, a key that exists nowhere in the codebase, compiled.
184
+ *
185
+ * Annotating `const c: PostgresCollectionConfig = { … }` *did* catch all three,
186
+ * which is the tell: the checking was never missing, only bypassed by the
187
+ * inference the builder exists to provide.
188
+ *
189
+ * The map is homomorphic (`[K in keyof P]`), so it stays invertible and `const P`
190
+ * inference is unaffected — `admin.display.title` still completes over the
191
+ * collection's own keys. Verified against the full probe suite before landing.
192
+ *
193
+ * Nested shapes (`array.of`, `map.properties`, and the `admin` block's own
194
+ * options) are not reached by this type; the weak-type check covers an `admin`
195
+ * block whose keys are *all* unknown, and the boot validator covers the rest.
196
+ */
197
+ export type StrictProperties<P, AllowedProperty> = {
198
+ [K in keyof P]: P[K] extends {
199
+ type: infer T;
200
+ } ? ExactProperty<P[K], Extract<AllowedProperty, {
201
+ type: T;
202
+ }>> : P[K];
203
+ };
80
204
  /**
81
205
  * A helper type to infer the underlying data type from a Property definition.
82
206
  * This is the core of the type inference system.
@@ -128,9 +252,20 @@ export type InferEntityType<P extends Properties> = {
128
252
  };
129
253
  export interface BaseProperty<CustomProps = unknown> {
130
254
  /**
131
- * Property name (e.g. Product)
255
+ * The label the admin panel shows for this field — a column header, a form
256
+ * label, a card caption.
257
+ *
258
+ * Optional, and derived from the property key when absent
259
+ * (`publishDate` -> "Publish Date", via `prettifyIdentifier`, which is the
260
+ * same derivation the collection editor already uses when it suggests one).
261
+ *
262
+ * It was required, and that was a UI concern levied on everyone: a headless
263
+ * project has no panel and no reason to invent display names, yet could not
264
+ * declare a property without one. Set it when the derived label is wrong —
265
+ * `"URL"` rather than "Url", `"Postcode"` rather than "Zip" — which is the
266
+ * only time it earns the line.
132
267
  */
133
- name: string;
268
+ name?: string;
134
269
  /**
135
270
  * Property description, always displayed under the field
136
271
  */
@@ -370,6 +505,15 @@ export interface VectorProperty extends BaseProperty {
370
505
  * Default value for new entities.
371
506
  */
372
507
  defaultValue?: Vector;
508
+ /**
509
+ * How many numbers each embedding has — 1536 for OpenAI's
510
+ * `text-embedding-3-small`, 768 for many sentence transformers.
511
+ *
512
+ * **Required, and effectively frozen.** It is the column's width
513
+ * (`vector(1536)`), so changing it is a rewrite of every row, and every
514
+ * stored embedding was produced by a model that no longer matches. Take the
515
+ * number from the model you are actually going to use.
516
+ */
373
517
  dimensions: number;
374
518
  /**
375
519
  * ANN index configuration for this column.
@@ -383,6 +527,11 @@ export interface VectorProperty extends BaseProperty {
383
527
  * embedding is left unindexed rather than failing the boot.
384
528
  */
385
529
  index?: VectorIndexConfig | false;
530
+ /**
531
+ * The rules every property has: `required`, `unique`. A vector has no
532
+ * length or range rules of its own — {@link dimensions} is the column's
533
+ * width, and the database enforces it.
534
+ */
386
535
  validation?: PropertyValidationSchema;
387
536
  }
388
537
  /**
@@ -394,6 +543,10 @@ export interface BinaryProperty extends BaseProperty {
394
543
  * Default value for new entities. Must be a base64-encoded string.
395
544
  */
396
545
  defaultValue?: string;
546
+ /**
547
+ * The rules every property has: `required`, `unique`. Size limits belong to
548
+ * the upload path, not here.
549
+ */
397
550
  validation?: PropertyValidationSchema;
398
551
  }
399
552
  export interface DateProperty extends BaseProperty {
@@ -530,6 +683,12 @@ export interface RelationProperty extends BaseProperty {
530
683
  * relation: { kind: "manyToMany", target: () => tagsCollection }
531
684
  * }
532
685
  * ```
686
+ *
687
+ * Optional because there is a second way to say the same thing: an entry in
688
+ * the collection's `relations` array whose `relationName` is this
689
+ * property's key. One of the two is required, and boot refuses a
690
+ * `type: "relation"` property that has neither — a field with no link is a
691
+ * relation in name only.
533
692
  */
534
693
  relation?: Relation;
535
694
  /**
@@ -645,15 +804,29 @@ export interface MapProperty extends BaseProperty {
645
804
  keyValue?: boolean;
646
805
  }
647
806
  /**
807
+ * What a `dynamicProps` builder is handed when it computes a property.
808
+ *
809
+ * Called on every render of the form, for one field, so it is a pure function of
810
+ * these arguments: the entity being edited, the value in this field, and who is
811
+ * editing. It runs **in the browser** and shapes what is offered — it is not a
812
+ * security boundary, and a rule that must hold has to be a security rule as well.
813
+ *
648
814
  * @group Entity properties
649
815
  */
650
816
  export type PropertyBuilderProps<M extends Record<string, unknown> = Record<string, unknown>> = {
817
+ /** The entity's values as they stand right now, including unsaved edits. */
651
818
  values: Partial<M>;
819
+ /** The values as last saved, or `undefined` for an entity being created. */
652
820
  previousValues?: Partial<M>;
821
+ /** The value in *this* field. `undefined` before anything is typed. */
653
822
  propertyValue?: unknown;
823
+ /** Position within the parent array, when this property is an array element. */
654
824
  index?: number;
825
+ /** The collection path this entity belongs to. */
655
826
  path: string;
827
+ /** The entity's id, or `undefined` while it is being created. */
656
828
  entityId?: string | number;
829
+ /** Who is editing — the signed-in user and their roles. */
657
830
  authController: AuthState;
658
831
  };
659
832
  /**
@@ -723,12 +896,19 @@ export interface PropertyValidationSchema {
723
896
  * @group Entity properties
724
897
  */
725
898
  export interface NumberPropertyValidationSchema extends PropertyValidationSchema {
899
+ /** Smallest accepted value, **inclusive**. `min: 0` accepts `0`. */
726
900
  min?: number;
901
+ /** Largest accepted value, **inclusive**. `max: 100` accepts `100`. */
727
902
  max?: number;
903
+ /** Must be strictly below this. The exclusive twin of {@link max}. */
728
904
  lessThan?: number;
905
+ /** Must be strictly above this. The exclusive twin of {@link min}. */
729
906
  moreThan?: number;
907
+ /** Must be greater than zero. `0` is rejected — use `min: 0` to allow it. */
730
908
  positive?: boolean;
909
+ /** Must be less than zero. `0` is rejected. */
731
910
  negative?: boolean;
911
+ /** No fractional part. Does not change the column type; see `columnType`. */
732
912
  integer?: boolean;
733
913
  }
734
914
  /**
@@ -736,16 +916,42 @@ export interface NumberPropertyValidationSchema extends PropertyValidationSchema
736
916
  * @group Entity properties
737
917
  */
738
918
  export interface StringPropertyValidationSchema extends PropertyValidationSchema {
919
+ /** Exactly this many characters — a country code, a fixed-width reference. */
739
920
  length?: number;
921
+ /** Fewest characters accepted, inclusive. */
740
922
  min?: number;
923
+ /**
924
+ * Most characters accepted, inclusive.
925
+ *
926
+ * Also sizes the column: a `max` turns `TEXT` into `VARCHAR(max)`, so
927
+ * lowering it on a table that already has longer rows is a migration the
928
+ * database will refuse, not just a stricter form.
929
+ */
741
930
  max?: number;
931
+ /**
932
+ * A pattern the whole value must match.
933
+ *
934
+ * A `string` is compiled per request, so it must be a valid regular
935
+ * expression — one that will not compile is rejected at boot, because
936
+ * `toPattern` answers `undefined` for it and the caller reads
937
+ * `if (pattern && !pattern.test(value))`: the rule would silently become no
938
+ * rule. A `RegExp` literal has already been compiled by the engine.
939
+ */
742
940
  matches?: string | RegExp;
743
941
  /**
744
942
  * Message displayed when the input does not satisfy the regex in `matches`
745
943
  */
746
944
  matchesMessage?: string;
945
+ /**
946
+ * Strip leading and trailing whitespace **before** saving.
947
+ *
948
+ * A transform, not a check: it changes the value that is written, which is
949
+ * what makes it the fix for "the same tag twice, one with a trailing space".
950
+ */
747
951
  trim?: boolean;
952
+ /** Lowercase the value before saving. A transform, like {@link trim}. */
748
953
  lowercase?: boolean;
954
+ /** Uppercase the value before saving. A transform, like {@link trim}. */
749
955
  uppercase?: boolean;
750
956
  }
751
957
  /**
@@ -753,7 +959,9 @@ export interface StringPropertyValidationSchema extends PropertyValidationSchema
753
959
  * @group Entity properties
754
960
  */
755
961
  export interface DatePropertyValidationSchema extends PropertyValidationSchema {
962
+ /** Earliest accepted date, inclusive. A fixed instant, not "today". */
756
963
  min?: Date;
964
+ /** Latest accepted date, inclusive. A fixed instant, not "today". */
757
965
  max?: Date;
758
966
  }
759
967
  /**
@@ -761,7 +969,9 @@ export interface DatePropertyValidationSchema extends PropertyValidationSchema {
761
969
  * @group Entity properties
762
970
  */
763
971
  export interface ArrayPropertyValidationSchema extends PropertyValidationSchema {
972
+ /** Fewest elements accepted, inclusive. Counts elements, not characters. */
764
973
  min?: number;
974
+ /** Most elements accepted, inclusive. Counts elements, not characters. */
765
975
  max?: number;
766
976
  }
767
977
  /**
@@ -775,8 +985,11 @@ export type StorageConfig = {
775
985
  * in `initializeRebaseBackend({ storage: { ... } })`.
776
986
  *
777
987
  * When omitted, the default storage source is used.
988
+ *
989
+ * A string key, or the handle `bucket("media")` returned — the handle is
990
+ * the same name spelled once, and `defineCollection` records its key.
778
991
  */
779
- storageSource?: string;
992
+ storageSource?: ResourceRef;
780
993
  /**
781
994
  * Store files for this property as **public**: they are placed under the
782
995
  * public prefix and served via stable, token-less, permanent, CDN-cacheable
@@ -34,13 +34,45 @@ export interface RelationBase {
34
34
  relationName?: string;
35
35
  /** The collection on the other end. */
36
36
  target: () => AnyCollectionConfig;
37
+ /**
38
+ * What the database does to this side's foreign key when the target row's
39
+ * key changes. Emitted as the constraint's `ON UPDATE`.
40
+ *
41
+ * Unset means no clause, which Postgres reads as `NO ACTION`. Set
42
+ * `"cascade"` when the target's key is a natural key that can be edited —
43
+ * a slug, a SKU — so the pointers follow it.
44
+ *
45
+ * Only a `belongsTo` puts the key on this table, so this is the only kind
46
+ * where the clause is written here; on the other kinds it describes the
47
+ * constraint the target's own column carries.
48
+ */
37
49
  onUpdate?: OnAction;
50
+ /**
51
+ * What the database does to this side's rows when the target row is
52
+ * deleted. Emitted as the constraint's `ON DELETE`.
53
+ *
54
+ * Defaults, when unset, to `"set null"` for an optional relation and
55
+ * **`"restrict"`** for a required one. `NOT NULL` says a child cannot exist
56
+ * without a parent; it does not say deleting the parent should delete the
57
+ * child. Ask for `"cascade"` when that is what you mean — it is the one
58
+ * value that destroys rows you did not name.
59
+ *
60
+ * A `manyToMany` is the exception: its junction rows default to
61
+ * `"cascade"`, because the row deleted there is the link and not the target.
62
+ */
38
63
  onDelete?: OnAction;
39
- /** Presentation overrides applied when this relation is rendered as a tab. */
64
+ /**
65
+ * Presentation overrides applied when this relation is rendered as a tab.
66
+ *
67
+ * Whether the link is *required* is not here: it is
68
+ * `validation: { required: true }` on the declaring property, the same key
69
+ * every other field uses. A relation carried its own copy until 0.18, and
70
+ * the two disagreed by construction — the DDL generator read the property
71
+ * (so the column was `NOT NULL`) while codegen read the relation (so the
72
+ * generated `Insert` type made it optional), and a `create()` that
73
+ * typechecked failed at the database.
74
+ */
40
75
  overrides?: Partial<AnyCollectionConfig>;
41
- validation?: {
42
- required?: boolean;
43
- };
44
76
  }
45
77
  /**
46
78
  * This collection holds the foreign key. One target row per source row.
@@ -179,6 +211,14 @@ export interface ViaRelation extends RelationBase {
179
211
  kind: "via";
180
212
  /** Whether the chain yields one row or many. Cannot be derived from a join chain. */
181
213
  cardinality: "one" | "many";
214
+ /**
215
+ * The joins, in order, from this collection's table to the target's.
216
+ *
217
+ * Each step names a table and the columns to join it on; the last step's
218
+ * table is the target. Read-only, because Rebase will not work out how to
219
+ * write through an arbitrary chain, and guessing is what this kind exists to
220
+ * stop.
221
+ */
182
222
  joinPath: JoinStep[];
183
223
  }
184
224
  /**
@@ -212,15 +252,27 @@ export type ResolvedRelation = ResolvedBelongsTo | ResolvedHasOne | ResolvedHasM
212
252
  export interface ResolvedRelationBase {
213
253
  /** Always set: defaulted during resolution if the author omitted it. */
214
254
  relationName: string;
255
+ /**
256
+ * The collection on the other end.
257
+ *
258
+ * Still a thunk — a relation between two collections that import each other
259
+ * has to be — but normalised: resolution unwraps the module namespace the
260
+ * author's `() => import(…)` may hand back, so every consumer gets the
261
+ * config and not a `{ default: … }` wrapper.
262
+ */
215
263
  target: () => AnyCollectionConfig;
216
264
  /** The target's slug, resolved once so consumers need not call `target()`. */
217
265
  targetSlug: string;
266
+ /** As authored — see {@link RelationBase.onUpdate}. Defaults are not filled in. */
218
267
  onUpdate?: OnAction;
268
+ /**
269
+ * As authored — see {@link RelationBase.onDelete}. `undefined` here means
270
+ * the author said nothing, and the DDL generator picks the default; it does
271
+ * **not** mean "no action".
272
+ */
219
273
  onDelete?: OnAction;
274
+ /** Presentation overrides applied when this relation is rendered as a tab. */
220
275
  overrides?: Partial<AnyCollectionConfig>;
221
- validation?: {
222
- required?: boolean;
223
- };
224
276
  /**
225
277
  * Whether one row or many come back. Derived from `kind` — kept because it
226
278
  * is what most consumers actually branch on, and because `via` is the one
@@ -290,6 +342,11 @@ export interface ResolvedManyToMany extends ResolvedRelationBase {
290
342
  cardinality: "many";
291
343
  writable: true;
292
344
  shared: true;
345
+ /**
346
+ * The junction table and its two key columns, with every default filled in:
347
+ * the table from both table names sorted and joined, the columns from each
348
+ * endpoint's slug.
349
+ */
293
350
  through: {
294
351
  table: string;
295
352
  sourceColumn: string;
@@ -300,6 +357,7 @@ export interface ResolvedManyToMany extends ResolvedRelationBase {
300
357
  export interface ResolvedVia extends ResolvedRelationBase {
301
358
  kind: "via";
302
359
  writable: false;
360
+ /** The chain as authored — see {@link ViaRelation.joinPath}. Nothing to default. */
303
361
  joinPath: JoinStep[];
304
362
  }
305
363
  /** Relations whose target row carries this collection's key. @group Models */
@@ -11,7 +11,9 @@
11
11
  * that ships an engine this package has never heard of says so at the call site
12
12
  * instead of looking like a typo of one that exists.
13
13
  */
14
- import { type DeclareOptions, type ResourceHandle, type ResourceTransport } from "./resources.js";
14
+ import { type DeclareOptions, type ResourceDeclaration, type ResourceHandle } from "./resources.js";
15
+ import { type DataSourceDefinition } from "./data_source.js";
16
+ import { type StorageSourceDefinition } from "./storage_source.js";
15
17
  /** Options a database accepts beyond the common ones. */
16
18
  export interface DatabaseOptions extends DeclareOptions {
17
19
  /**
@@ -94,6 +96,20 @@ export interface BucketOptions extends DeclareOptions {
94
96
  publicRead?: boolean;
95
97
  /** Key prefix within the bucket, for sharing one bucket between sources. */
96
98
  prefix?: string;
99
+ /**
100
+ * Serve unqualified uploads — a storage property with no `storageSource` —
101
+ * from this bucket.
102
+ *
103
+ * A project that declares only `bucket("media")` has no default bucket, and
104
+ * the registry used to promote the one it found with a warning. That is a
105
+ * decision about where a user's files land, made by the framework, on the
106
+ * strength of declaration order; it also produced two different
107
+ * destinations either side of a deploy, because the synthesized local
108
+ * default is dropped in production and the promotion is not. So it is now
109
+ * a boot error, and this is one of the two ways to answer it — the other
110
+ * being `bucket()`, which declares the default bucket itself.
111
+ */
112
+ default?: boolean;
97
113
  /**
98
114
  * The credential set this bucket signs with, when several share one.
99
115
  *
@@ -116,12 +132,20 @@ export type BucketHandle = ResourceHandle;
116
132
  * Declare a bucket.
117
133
  *
118
134
  * ```ts
119
- * export const media = bucket("media", { transport: "direct" });
135
+ * export const uploads = bucket({ engine: "s3" }); // the default one
136
+ * export const media = bucket("media", { transport: "direct" });
120
137
  * ```
121
138
  *
122
139
  * `transport: "direct"` means a provider SDK talks to the bucket and the
123
140
  * backend is not in the upload path.
141
+ *
142
+ * The options-only form exists for the same reason `database`'s does: the
143
+ * default bucket has no name to pass, and without it the only way to configure
144
+ * one was `bucket("(default)", { … })` — writing out an internal sentinel to
145
+ * reach the options. Passing options where a key belongs used to throw "a
146
+ * bucket needs a non-empty key", which names neither the mistake nor the fix.
124
147
  */
148
+ export declare function bucket(options?: BucketOptions): BucketHandle;
125
149
  export declare function bucket(key?: string, options?: BucketOptions): BucketHandle;
126
150
  /**
127
151
  * How hard the runtime tries to deliver.
@@ -203,10 +227,152 @@ export interface TopicHandle<T> extends ResourceHandle {
203
227
  * ```
204
228
  */
205
229
  export declare function topic<T = unknown>(key: string, options?: TopicOptions): TopicHandle<T>;
230
+ /** What a cron declaration records, beyond its handler. */
231
+ export interface CronResourceOptions extends DeclareOptions {
232
+ /** Five-field cron expression, e.g. `0 3 * * *`. */
233
+ schedule: string;
234
+ /**
235
+ * IANA zone the schedule is read in, e.g. `Europe/Madrid`.
236
+ *
237
+ * Without it the schedule is read in the process's own zone, which is
238
+ * whatever the host happens to be set to — UTC in nearly every container,
239
+ * the developer's own on a laptop. "3 AM" then means two different hours
240
+ * either side of a deploy. Naming the zone makes the declaration mean one
241
+ * thing everywhere.
242
+ */
243
+ timezone?: string;
244
+ description?: string;
245
+ enabled?: boolean;
246
+ timeoutSeconds?: number;
247
+ catchUpWindowSeconds?: number;
248
+ }
249
+ /**
250
+ * Declare a cron, as the scheduler's `defineCron` does on its way through.
251
+ *
252
+ * Projects do not call this: `defineCron` in `@rebasepro/server` does, so a
253
+ * cron file is both the handler and the declaration — one file, one name, and
254
+ * the graph derived from it says what a host needs to know without evaluating
255
+ * the handler. Exported so the derive step and the scheduler spell the
256
+ * declaration identically.
257
+ */
258
+ export declare function declareCron(name: string, options: CronResourceOptions): ResourceHandle;
259
+ /**
260
+ * What a function declaration records.
261
+ *
262
+ * Recorded by the derive step from the bundler's static analysis rather than
263
+ * by evaluating the function module: a function's handler is a Hono app that
264
+ * only needs to exist at request time, and evaluating it at build time would
265
+ * run its module-scope code in a process with none of its environment.
266
+ */
267
+ export interface FunctionResourceOptions extends DeclareOptions {
268
+ /** Path inside the project, so a host can point at the file. */
269
+ file?: string;
270
+ /** `false` when the source imports a Node built-in or a package that needs one. */
271
+ portable?: boolean;
272
+ /** Why it is not portable — one short phrase per reason. */
273
+ requires?: string[];
274
+ }
275
+ /** Declare a function. Called by the derive step, not by projects. */
276
+ export declare function declareFunction(name: string, options?: FunctionResourceOptions): ResourceHandle;
277
+ /** Options a queue accepts beyond the common ones. */
278
+ export interface QueueOptions extends DeclareOptions {
279
+ /** Attempts before a job is left failed. Default 5. */
280
+ maxAttempts?: number;
281
+ }
282
+ /** What a queue's handler receives. `attempt` counts from 1. */
283
+ export type QueueHandler<T> = (payload: T, context: {
284
+ attempt: number;
285
+ queue: string;
286
+ jobId: string;
287
+ }) => Promise<void> | void;
288
+ /** Per-job options at enqueue time. */
289
+ export interface QueueEnqueueOptions {
290
+ /** Earliest time the job may run. Defaults to now. */
291
+ runAt?: Date;
292
+ /** Attempts for this job, overriding the queue's. */
293
+ maxAttempts?: number;
294
+ }
295
+ /**
296
+ * What a queue enqueues through.
297
+ *
298
+ * Installed by `@rebasepro/server` at boot, alongside the topic runtime.
299
+ * Absent — config evaluated by the CLI, a unit test — enqueueing throws with
300
+ * the cause named, rather than resolving and dropping the job.
301
+ */
302
+ export interface QueueRuntime {
303
+ enqueue(queue: string, payload: unknown, options?: QueueEnqueueOptions): Promise<{
304
+ id: string;
305
+ }>;
306
+ }
307
+ /** Install the transport queues enqueue through. Called by the server at boot. */
308
+ export declare function setQueueRuntime(runtime: QueueRuntime | null): void;
309
+ /** A queue's handler, as recorded for the worker to wire. */
310
+ export interface QueueConsumer<T = unknown> {
311
+ queue: string;
312
+ handler: QueueHandler<T>;
313
+ }
314
+ /** Every declared queue handler, for the worker to wire. */
315
+ export declare function declaredQueueConsumers(): QueueConsumer[];
316
+ /** Forget declared queue handlers. For tests, alongside `resetDeclaredResources`. */
317
+ export declare function resetDeclaredQueueConsumers(): void;
318
+ /** A queue handle, carrying its payload type. */
319
+ export interface QueueHandle<T> extends ResourceHandle {
320
+ /**
321
+ * Put a job on the queue.
322
+ *
323
+ * Resolves once the job is durably recorded, not once it has run. A row
324
+ * insert, so enqueued inside a transaction that rolls back it was never
325
+ * enqueued.
326
+ */
327
+ enqueue(payload: T, options?: QueueEnqueueOptions): Promise<{
328
+ id: string;
329
+ }>;
330
+ /**
331
+ * Declare the handler.
332
+ *
333
+ * One per queue: a queue is a work list with one consumer, which is what
334
+ * separates it from a topic. Work that several things must react to is a
335
+ * topic with several subscriptions.
336
+ */
337
+ handler(fn: QueueHandler<T>): void;
338
+ }
339
+ /**
340
+ * Declare a queue.
341
+ *
342
+ * ```ts
343
+ * export const thumbnails = queue<{ key: string }>("thumbnails");
344
+ * thumbnails.handler(async ({ key }) => { … });
345
+ * await thumbnails.enqueue({ key }, { runAt: new Date(Date.now() + 60_000) });
346
+ * ```
347
+ *
348
+ * The difference from a topic is the number of consumers: a queue has one, a
349
+ * topic fans out to every subscription. Both ride on the durable job queue, so
350
+ * declaring either turns it on.
351
+ */
352
+ export declare function queue<T = unknown>(key: string, options?: QueueOptions): QueueHandle<T>;
353
+ /**
354
+ * One declaration, as the data layer's definition.
355
+ *
356
+ * There is exactly one of these per kind, and everything that needs a
357
+ * definition goes through it — the frontend, the managed runtime's boot path,
358
+ * and an ejected project's own entrypoint. That is not tidiness: the mapping
359
+ * used to exist twice, once here and once in `@rebasepro/server`'s
360
+ * `graphToStorageSources`, and the two disagreed. The server's copy carried a
361
+ * bucket's `account`; this one dropped it, so a bucket declared with shared
362
+ * credentials resolved them on the managed runtime and resolved *nothing* in an
363
+ * ejected backend — the source was skipped and every upload to it answered 501.
364
+ *
365
+ * A field-by-field map is one line away from that failure at all times, so
366
+ * there is now one line to keep right instead of two to keep equal.
367
+ */
368
+ export declare function resourceToDataSource(declaration: ResourceDeclaration): DataSourceDefinition;
369
+ /** One declaration, as the storage layer's definition. See {@link resourceToDataSource}. */
370
+ export declare function resourceToStorageSource(declaration: ResourceDeclaration): StorageSourceDefinition;
206
371
  /**
207
- * The declared databases, in the shape `<Rebase dataSources>` takes.
372
+ * The declared databases, as definitions.
208
373
  *
209
- * The frontend needs to know which sources exist and how they are reached — a
374
+ * Both the frontend and a project's own backend entrypoint read this. The
375
+ * frontend needs to know which sources exist and how they are reached — a
210
376
  * `direct`-transport source is one the browser talks to itself — and it imports
211
377
  * the same config package the backend does. Without these it would mean writing
212
378
  * the list a second time, by hand, next to the declarations, which is precisely
@@ -223,16 +389,6 @@ export declare function topic<T = unknown>(key: string, options?: TopicOptions):
223
389
  * would leave this empty — hence the side-effect import above rather than a
224
390
  * bare re-export.
225
391
  */
226
- export declare function declaredDataSources(): {
227
- key: string;
228
- engine: string;
229
- transport: ResourceTransport;
230
- label?: string;
231
- }[];
232
- /** The declared buckets, in the shape `<Rebase storageSources>` takes. */
233
- export declare function declaredStorageSources(): {
234
- key: string;
235
- engine: string;
236
- transport: ResourceTransport;
237
- label?: string;
238
- }[];
392
+ export declare function declaredDataSources(): DataSourceDefinition[];
393
+ /** The declared buckets, as definitions. */
394
+ export declare function declaredStorageSources(): StorageSourceDefinition[];