@rebasepro/types 0.16.0 → 0.16.1-canary.g0d7af95

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 (57) hide show
  1. package/dist/call_context.d.ts +5 -5
  2. package/dist/controllers/auth_state.d.ts +1 -1
  3. package/dist/controllers/client.d.ts +22 -9
  4. package/dist/controllers/collection_registry.d.ts +2 -2
  5. package/dist/controllers/data.d.ts +4 -12
  6. package/dist/controllers/data_driver.d.ts +7 -7
  7. package/dist/controllers/email.d.ts +54 -2
  8. package/dist/controllers/index.d.ts +8 -9
  9. package/dist/controllers/storage.d.ts +4 -4
  10. package/dist/index.d.ts +5 -5
  11. package/dist/index.es.js +433 -3
  12. package/dist/index.es.js.map +1 -1
  13. package/dist/types/admin_block.d.ts +1 -1
  14. package/dist/types/auth_adapter.d.ts +18 -9
  15. package/dist/types/backend.d.ts +23 -10
  16. package/dist/types/collection_contract.d.ts +1 -1
  17. package/dist/types/collections.d.ts +34 -9
  18. package/dist/types/component_ref.d.ts +3 -2
  19. package/dist/types/cron.d.ts +1 -25
  20. package/dist/types/data_source.d.ts +1 -1
  21. package/dist/types/database_adapter.d.ts +5 -5
  22. package/dist/types/entities.d.ts +1 -1
  23. package/dist/types/entity_callbacks.d.ts +4 -4
  24. package/dist/types/index.d.ts +33 -29
  25. package/dist/types/indexes.d.ts +179 -0
  26. package/dist/types/project_manifest.d.ts +132 -27
  27. package/dist/types/properties.d.ts +58 -6
  28. package/dist/types/relations.d.ts +1 -1
  29. package/dist/types/resource_kinds.d.ts +189 -0
  30. package/dist/types/resources.d.ts +197 -0
  31. package/dist/types/schema_editing.d.ts +127 -0
  32. package/dist/types/schema_version.d.ts +1 -1
  33. package/dist/types/security_rules.d.ts +1 -1
  34. package/dist/types/storage_source.d.ts +27 -0
  35. package/dist/users/index.d.ts +1 -1
  36. package/package.json +2 -2
  37. package/src/controllers/client.ts +14 -1
  38. package/src/controllers/data.ts +0 -9
  39. package/src/controllers/email.ts +55 -2
  40. package/src/controllers/index.ts +0 -1
  41. package/src/controllers/storage.ts +4 -4
  42. package/src/types/admin_block.ts +1 -2
  43. package/src/types/auth_adapter.ts +18 -10
  44. package/src/types/backend.ts +18 -1
  45. package/src/types/collections.ts +28 -2
  46. package/src/types/component_ref.ts +3 -2
  47. package/src/types/cron.ts +0 -24
  48. package/src/types/index.ts +4 -0
  49. package/src/types/indexes.ts +180 -0
  50. package/src/types/project_manifest.ts +139 -26
  51. package/src/types/properties.ts +54 -0
  52. package/src/types/resource_kinds.ts +324 -0
  53. package/src/types/resources.ts +368 -0
  54. package/src/types/schema_editing.ts +154 -0
  55. package/src/types/storage_source.ts +28 -0
  56. package/dist/controllers/database_admin.d.ts +0 -11
  57. package/src/controllers/database_admin.ts +0 -22
@@ -29,7 +29,7 @@
29
29
  *
30
30
  * @group Models
31
31
  */
32
- export declare const ADMIN_COLLECTION_KEYS: readonly ["Actions", "additionalFields", "alwaysApplyDefaultValues", "components", "customViews", "defaultEntityAction", "defaultFilter", "defaultSelectedView", "defaultSize", "defaultViewMode", "disableDefaultActions", "display", "enabledViews", "entityActions", "entityViews", "exportable", "filterPresets", "fixedFilter", "form", "formAutoSave", "formView", "group", "hideFromEntityViews", "hideFromNavigation", "hideIdFromCollection", "hideIdFromForm", "icon", "includeJsonView", "inlineEditing", "kanban", "listProperties", "localChangesBackup", "openEntityMode", "orderProperty", "pagination", "previewProperties", "propertiesOrder", "selectionController", "selectionEnabled", "sideDialogWidth", "sort", "titleProperty"];
32
+ export declare const ADMIN_COLLECTION_KEYS: readonly ["Actions", "additionalFields", "alwaysApplyDefaultValues", "components", "customViews", "defaultEntityAction", "defaultFilter", "defaultSelectedView", "defaultSize", "defaultViewMode", "disableDefaultActions", "display", "enabledViews", "entityActions", "entityViews", "exportable", "filterPresets", "fixedFilter", "form", "formAutoSave", "formView", "group", "hideFromEntityViews", "hideFromNavigation", "hideIdFromCollection", "hideIdFromForm", "icon", "includeJsonView", "inlineEditing", "kanban", "listProperties", "localChangesBackup", "openEntityMode", "orderProperty", "pagination", "previewProperties", "propertiesOrder", "selectionController", "selectionEnabled", "sideDialogWidth", "sort"];
33
33
  /** A key of a collection's `admin` block. @group Models */
34
34
  export type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];
35
35
  /**
@@ -75,8 +75,16 @@ export interface AuthAdapterCapabilities {
75
75
  hasBuiltInAuthRoutes: boolean;
76
76
  /** Supports email/password login. */
77
77
  emailPasswordLogin: boolean;
78
- /** Supports new user registration. */
79
- registration: boolean;
78
+ /**
79
+ * Whether self-registration is open **right now**.
80
+ *
81
+ * A runtime answer, not a static feature list: the built-in adapter also
82
+ * reports `true` during the first-user bootstrap window (an empty user
83
+ * table) and `false` the moment `disableSelfRegistration` is set. Whatever
84
+ * this says, `POST /auth/register` does — both read the same predicate, so
85
+ * the UI can never be sent to a form that can only 403.
86
+ */
87
+ registrationEnabled: boolean;
80
88
  /**
81
89
  * Supports the end-user password reset flow (emailing a reset link).
82
90
  *
@@ -105,12 +113,15 @@ export interface AuthAdapterCapabilities {
105
113
  /** Supports passwordless magic link login. */
106
114
  magicLink: boolean;
107
115
  /**
108
- * Whether `POST /auth/anonymous` will mint a credential-less session.
116
+ * Supports passwordless sign-in with a six-digit code sent by email.
109
117
  *
110
- * Optional so an external adapter that predates the key keeps typechecking;
111
- * absent means "this adapter does not offer anonymous sign-in".
118
+ * Optional so that an external adapter written before this existed still
119
+ * satisfies the interface; absent reads as "no", which is what an adapter
120
+ * that has never heard of the flow means.
112
121
  */
113
- anonymousLogin?: boolean;
122
+ emailOtp?: boolean;
123
+ /** Whether `POST /auth/anonymous` will mint a credential-less session. */
124
+ anonymousLogin: boolean;
114
125
  /** List of enabled OAuth provider IDs (e.g. `["google", "github"]`). */
115
126
  enabledProviders: string[];
116
127
  /**
@@ -124,8 +135,6 @@ export interface AuthAdapterCapabilities {
124
135
  * Only applicable for built-in auth.
125
136
  */
126
137
  needsSetup?: boolean;
127
- /** Whether new user registration is enabled (may differ from `registration` capability at runtime). */
128
- registrationEnabled?: boolean;
129
138
  }
130
139
  /**
131
140
  * Options for paginated user listing.
@@ -284,7 +293,7 @@ export interface TransformAuthResponseContext {
284
293
  /** The authenticated user's ID. */
285
294
  uid: string;
286
295
  /** The auth method that triggered this response. */
287
- method: "login" | "register" | "oauth" | "refresh" | "anonymous" | "magic-link" | "mfa";
296
+ method: "login" | "register" | "oauth" | "refresh" | "anonymous" | "magic-link" | "otp" | "mfa";
288
297
  /** The raw HTTP request (for reading headers, IP, etc.). */
289
298
  request: Request;
290
299
  }
@@ -1,9 +1,10 @@
1
- import type { CollectionConfig, FilterValues, WhereFilterOp } from "./collections";
2
- import type { OrderByTuple } from "./filter-operators";
3
- import type { LogicalCondition } from "../controllers/data";
4
- import type { AuthAdapter } from "./auth_adapter";
5
- import type { HistoryConfig } from "../controllers/client";
6
- import type { ChannelBusSetting } from "./channel_bus";
1
+ import type { CollectionConfig, FilterValues, WhereFilterOp } from "./collections.js";
2
+ import type { OrderByTuple } from "./filter-operators.js";
3
+ import type { LogicalCondition } from "../controllers/data.js";
4
+ import type { AuthAdapter } from "./auth_adapter.js";
5
+ import type { HistoryConfig } from "../controllers/client.js";
6
+ import type { ChannelBusSetting } from "./channel_bus.js";
7
+ import type { SchemaEditingAdmin } from "./schema_editing.js";
7
8
  /**
8
9
  * Abstract database connection interface.
9
10
  * Represents a connection to any database system.
@@ -433,7 +434,19 @@ export interface BranchAdmin {
433
434
  *
434
435
  * @group Admin
435
436
  */
436
- export type DatabaseAdmin = Partial<SQLAdmin> & Partial<DocumentAdmin> & Partial<SchemaAdmin> & Partial<BranchAdmin>;
437
+ export type DatabaseAdmin = Partial<SQLAdmin> & Partial<DocumentAdmin> & Partial<SchemaAdmin> & Partial<BranchAdmin> & Partial<SchemaEditingAdmin>;
438
+ /**
439
+ * Type guard: can this admin plan a live schema change?
440
+ *
441
+ * Planning is engine-specific — it renders DDL, a Drizzle schema and the
442
+ * declarative SQL artifacts — so the implementation lives in the driver
443
+ * package. The server detects the capability structurally, exactly as it does
444
+ * for SQL, rather than importing an engine it is supposed to know nothing
445
+ * about.
446
+ *
447
+ * @group Admin
448
+ */
449
+ export declare function isSchemaEditingAdmin(admin: DatabaseAdmin | undefined): admin is SchemaEditingAdmin;
437
450
  /**
438
451
  * Type guard: does this admin support SQL operations?
439
452
  * @group Admin
@@ -697,7 +710,7 @@ export interface BackendBootstrapper {
697
710
  /**
698
711
  * Initialize WebSocket server for realtime operations.
699
712
  */
700
- initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import("../controllers/data_driver").DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> | void;
713
+ initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: import("../controllers/data_driver.js").DataDriver, config?: unknown, authAdapter?: AuthAdapter): Promise<void> | void;
701
714
  }
702
715
  /**
703
716
  * Result of `BackendBootstrapper.initializeDriver()`.
@@ -705,7 +718,7 @@ export interface BackendBootstrapper {
705
718
  */
706
719
  export interface InitializedDriver {
707
720
  /** The DataDriver instance, ready for use. */
708
- driver: import("../controllers/data_driver").DataDriver;
721
+ driver: import("../controllers/data_driver.js").DataDriver;
709
722
  /** The realtime service, if the driver created one during init. */
710
723
  realtimeProvider?: RealtimeProvider;
711
724
  /** A collection registry to register schema / tables into. */
@@ -716,7 +729,7 @@ export interface InitializedDriver {
716
729
  * Set by drivers that introspect in `baas` mode; the server serves these
717
730
  * instead of collections loaded from config files.
718
731
  */
719
- collections?: import("./collections").CollectionConfig[];
732
+ collections?: import("./collections.js").CollectionConfig[];
720
733
  /** The underlying database connection (for lifecycle management). */
721
734
  connection?: DatabaseConnection;
722
735
  /**
@@ -1,4 +1,4 @@
1
- import type { CollectionConfig } from "./collections";
1
+ import type { CollectionConfig } from "./collections.js";
2
2
  /**
3
3
  * Serializing collections so they survive a network hop.
4
4
  *
@@ -1,9 +1,11 @@
1
- import type { CollectionCallbacks } from "./entity_callbacks";
2
- import type { Properties, PostgresProperties, FirebaseProperties, MongoProperties } from "./properties";
3
- import type { User } from "../users";
4
- import type { Relation } from "./relations";
5
- import type { SecurityRule } from "./security_rules";
6
- import type { SearchConfig } from "./search";
1
+ import type { CollectionCallbacks } from "./entity_callbacks.js";
2
+ import type { Properties, PostgresProperties, FirebaseProperties, MongoProperties } from "./properties.js";
3
+ import type { User } from "../users/index.js";
4
+ import type { EmailSendResult } from "../controllers/email.js";
5
+ import type { Relation } from "./relations.js";
6
+ import type { SecurityRule } from "./security_rules.js";
7
+ import type { SearchConfig } from "./search.js";
8
+ import type { CollectionIndex } from "./indexes.js";
7
9
  /**
8
10
  * Base interface containing all driver-agnostic collection properties.
9
11
  * Use {@link PostgresCollectionConfig} or {@link FirebaseCollectionConfig} for
@@ -247,6 +249,23 @@ export interface PostgresCollectionConfig<M extends Record<string, unknown> = Re
247
249
  * @see SearchConfig
248
250
  */
249
251
  search?: SearchConfig;
252
+ /**
253
+ * Ordinary indexes on this collection's table.
254
+ *
255
+ * Collection-level, not per-property, because an index over two columns
256
+ * has no single property to hang on and a partial index has none at all —
257
+ * and because a second declaration site for the single-column case would
258
+ * put the same object in two places. An index's identity is a column list
259
+ * in an order; the single-column case is a degenerate one, not a special
260
+ * one.
261
+ *
262
+ * `VectorProperty.index` stays where it is: an ANN structure is a property
263
+ * of the column's type, not of a query.
264
+ *
265
+ * Postgres-only, like {@link SearchConfig}: refused on another engine
266
+ * rather than silently ignored.
267
+ */
268
+ indexes?: readonly CollectionIndex<Extract<keyof M, string>>[];
250
269
  }
251
270
  /**
252
271
  * A collection backed by Firebase / Firestore.
@@ -475,7 +494,7 @@ export interface EntityChildView<M extends Record<string, unknown> = Record<stri
475
494
  collection: CollectionConfig<M>;
476
495
  source: ChildViewSource;
477
496
  }
478
- export type { WhereFilterOp, FilterValues, WireFilterValues, FilterPreset } from "./filter-operators";
497
+ export type { WhereFilterOp, FilterValues, WireFilterValues, FilterPreset } from "./filter-operators.js";
479
498
  export type InferCollectionConfigType<S extends CollectionConfig> = S extends CollectionConfig<infer M> ? M : never;
480
499
  /**
481
500
  * Configuration for authentication collections.
@@ -557,13 +576,19 @@ export interface AuthCollectionConfig {
557
576
  export interface AuthCollectionContext {
558
577
  /** Hash a password using the configured algorithm (scrypt by default). */
559
578
  hashPassword: (password: string) => Promise<string>;
560
- /** Send an email. Only available when email service is configured. */
579
+ /**
580
+ * Send an email. Only available when email service is configured.
581
+ *
582
+ * Resolves with what the provider reported — the assigned Message-ID, most
583
+ * usefully — so a hook that sends a message can store the id and later
584
+ * thread a reply back to it. Callers that do not care may ignore it.
585
+ */
561
586
  sendEmail?: (options: {
562
587
  to: string;
563
588
  subject: string;
564
589
  html: string;
565
590
  text?: string;
566
- }) => Promise<void>;
591
+ }) => Promise<EmailSendResult>;
567
592
  /** Whether the email service is configured and available. */
568
593
  emailConfigured: boolean;
569
594
  /** The app name from email config (for templates). */
@@ -2,8 +2,9 @@
2
2
  * How a collection points at a UI component without the backend learning about React.
3
3
  *
4
4
  * This file is the hinge the BaaS/admin split turns on. `ComponentRef` is named
5
- * by `properties.ts` (`ui.Field`, `ui.Preview`, `ui.Filter`), and `properties.ts`
6
- * must stay in the React-free core because every backend subsystem — validation,
5
+ * by a property's `admin` block (`admin.Field`, `admin.Preview`, `admin.Filter`)
6
+ * and imported by `properties.ts`, which must stay in the React-free core
7
+ * because every backend subsystem — validation,
7
8
  * the drizzle schema generator, the OpenAPI generator, the SDK codegen — reads
8
9
  * property definitions. If `ComponentRef` needed `React.ComponentType`, the whole
9
10
  * property model would have to move to the admin layer with it.
@@ -1,5 +1,4 @@
1
- import type { RebaseServerClient } from "../controllers/client";
2
- import type { RebaseSdkData } from "../controllers/data";
1
+ import type { RebaseServerClient } from "../controllers/client.js";
3
2
  /**
4
3
  * Cron Job type definitions for Rebase.
5
4
  *
@@ -107,29 +106,6 @@ export interface CronJobContext {
107
106
  * });
108
107
  */
109
108
  rebase: RebaseServerClient;
110
- /**
111
- * The same object as {@link rebase}, under the name this context used
112
- * before.
113
- *
114
- * @deprecated Use `rebase` instead. Two things made the old name a problem,
115
- * and neither was cosmetic. It contradicted every other server surface,
116
- * where the singleton is `rebase` — the previous docstring had to end with
117
- * *"it is only named `client` here"*. And typing it as `RebaseClient`
118
- * re-exposed `client.data`, the alias that {@link RebaseServerClient}
119
- * deliberately `Omit`s so the RLS-bypassing plane has exactly one name and
120
- * the privilege is visible at the call site. A reader who learned
121
- * `client.data` here carried it to a collection callback, where
122
- * `context.data` is the *user-scoped* plane — same spelling, opposite
123
- * privilege.
124
- *
125
- * Still the full server client at runtime, and `data` still resolves, so
126
- * existing cron files keep working and keep compiling. It will be removed
127
- * in the next major.
128
- */
129
- client: RebaseServerClient & {
130
- /** @deprecated Use `rebase.dataAsAdmin` — the name states the privilege. */
131
- data: RebaseSdkData;
132
- };
133
109
  }
134
110
  export type CronJobRunState = "idle" | "running" | "success" | "error" | "disabled";
135
111
  /**
@@ -1,4 +1,4 @@
1
- import { WhereFilterOp } from "./filter-operators";
1
+ import { WhereFilterOp } from "./filter-operators.js";
2
2
  /**
3
3
  * Describes the capabilities and features supported by a data source (driver).
4
4
  *
@@ -19,10 +19,10 @@
19
19
  *
20
20
  * @group Backend
21
21
  */
22
- import type { DataDriver } from "../controllers/data_driver";
23
- import type { CollectionConfig } from "./collections";
24
- import type { CollectionRegistryInterface, DatabaseAdmin, InitializedDriver, RealtimeProvider, BootstrappedAuth } from "./backend";
25
- import type { HistoryConfig } from "../controllers/client";
22
+ import type { DataDriver } from "../controllers/data_driver.js";
23
+ import type { CollectionConfig } from "./collections.js";
24
+ import type { CollectionRegistryInterface, DatabaseAdmin, InitializedDriver, RealtimeProvider, BootstrappedAuth } from "./backend.js";
25
+ import type { HistoryConfig } from "../controllers/client.js";
26
26
  /**
27
27
  * A `DatabaseAdapter` provides data persistence for Rebase.
28
28
  *
@@ -74,7 +74,7 @@ export interface DatabaseAdapter {
74
74
  * here — turning an adapter-authenticated server's socket into one that
75
75
  * accepted every client as already authenticated.
76
76
  */
77
- initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, adapter?: import("./auth_adapter").AuthAdapter): Promise<void> | void;
77
+ initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, adapter?: import("./auth_adapter.js").AuthAdapter): Promise<void> | void;
78
78
  /**
79
79
  * Bring the database's collection tables up to date, additively — the boot
80
80
  * companion to `db push`. See `BackendBootstrapper.ensureCollectionSchema`
@@ -1,4 +1,4 @@
1
- import type { SearchMatch } from "./search";
1
+ import type { SearchMatch } from "./search.js";
2
2
  /**
3
3
  * New or existing status
4
4
  * @group Models
@@ -1,7 +1,7 @@
1
- import type { CollectionConfig } from "./collections";
2
- import type { EntityStatus, EntityValues } from "./entities";
3
- import type { User } from "../users";
4
- import type { RebaseCallContext } from "../call_context";
1
+ import type { CollectionConfig } from "./collections.js";
2
+ import type { EntityStatus, EntityValues } from "./entities.js";
3
+ import type { User } from "../users/index.js";
4
+ import type { RebaseCallContext } from "../call_context.js";
5
5
  /**
6
6
  * Lifecycle callbacks for entity CRUD operations.
7
7
  *
@@ -1,29 +1,33 @@
1
- export * from "./entities";
2
- export * from "./filter-operators";
3
- export * from "./chips";
4
- export * from "./properties";
5
- export * from "./admin_block";
6
- export * from "./collections";
7
- export * from "./search";
8
- export * from "./relations";
9
- export * from "./policy";
10
- export * from "./rls-functions";
11
- export * from "./security_rules";
12
- export * from "./entity_callbacks";
13
- export * from "./websockets";
14
- export * from "./backend";
15
- export * from "./channel_bus";
16
- export * from "./data_source";
17
- export * from "./storage_source";
18
- export * from "./cron";
19
- export * from "./backup";
20
- export * from "./component_ref";
21
- export * from "./auth_adapter";
22
- export * from "./database_adapter";
23
- export * from "./api_keys";
24
- export * from "./history";
25
- export * from "./postgres_introspection";
26
- export * from "./project_manifest";
27
- export * from "./collection_contract";
28
- export * from "./schema_version";
29
- export * from "./storage_authorize";
1
+ export * from "./entities.js";
2
+ export * from "./filter-operators.js";
3
+ export * from "./chips.js";
4
+ export * from "./properties.js";
5
+ export * from "./admin_block.js";
6
+ export * from "./collections.js";
7
+ export * from "./search.js";
8
+ export * from "./indexes.js";
9
+ export * from "./relations.js";
10
+ export * from "./policy.js";
11
+ export * from "./rls-functions.js";
12
+ export * from "./security_rules.js";
13
+ export * from "./entity_callbacks.js";
14
+ export * from "./websockets.js";
15
+ export * from "./backend.js";
16
+ export * from "./schema_editing.js";
17
+ export * from "./channel_bus.js";
18
+ export * from "./data_source.js";
19
+ export * from "./resources.js";
20
+ export * from "./resource_kinds.js";
21
+ export * from "./storage_source.js";
22
+ export * from "./cron.js";
23
+ export * from "./backup.js";
24
+ export * from "./component_ref.js";
25
+ export * from "./auth_adapter.js";
26
+ export * from "./database_adapter.js";
27
+ export * from "./api_keys.js";
28
+ export * from "./history.js";
29
+ export * from "./postgres_introspection.js";
30
+ export * from "./project_manifest.js";
31
+ export * from "./collection_contract.js";
32
+ export * from "./schema_version.js";
33
+ export * from "./storage_authorize.js";
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Ordinary indexes, declared on a collection.
3
+ *
4
+ * Distinct from the two index-shaped things Rebase already builds. A `search`
5
+ * block builds a GIN index over a generated `tsvector`, and a `vector`
6
+ * property builds an ANN index over an embedding; both are structures the
7
+ * *feature* owns and neither is a query the developer wrote. This is the plain
8
+ * case — the btree behind a `where` clause — which had no declaration site at
9
+ * all, so the only way to have one was to write it by hand, where the next
10
+ * `rebase db push` planned it away.
11
+ *
12
+ * Every form here is core Postgres, deliberately. See {@link CollectionIndex}.
13
+ */
14
+ /**
15
+ * A key column of an index whose access method has no ordering.
16
+ *
17
+ * `gin` and `brin` reject `ASC`/`DESC`/`NULLS` outright — Postgres answers
18
+ * `access method "gin" does not support ASC/DESC options` — so those methods
19
+ * take this narrower shape and the combination is unrepresentable rather than
20
+ * refused at build time.
21
+ */
22
+ export interface UnorderedIndexKey<Keys extends string = string> {
23
+ /**
24
+ * A property key on this collection — never a column name.
25
+ *
26
+ * Which column that resolves to depends on the property, and the two
27
+ * differ in exactly the case an index is most often wanted for: a
28
+ * `belongsTo` relation compiles to its resolved `localKey`
29
+ * (`primaryCategory` → `primary_category_id`), not to the snake-cased
30
+ * property key. Anything else resolves through `columnName`, or the
31
+ * snake-case default when it declares none.
32
+ *
33
+ * Writing the column name here would work for most properties and quietly
34
+ * index nothing for a foreign key, which is the one people reach for.
35
+ */
36
+ prop: Keys | (string & {});
37
+ }
38
+ /**
39
+ * A key column of an index, when its order matters.
40
+ *
41
+ * `direction` and `nulls` earn their place only when a query's `ORDER BY`
42
+ * mixes directions. A lone `DESC` index is redundant with its `ASC` twin —
43
+ * Postgres scans a btree backwards just as fast — and declaring both is
44
+ * refused.
45
+ *
46
+ * Writing the Postgres default down explicitly is free: the derived name
47
+ * hashes the *effective* order, so adding `direction: "asc"` to a column that
48
+ * was already ascending is not a redefinition and rebuilds nothing.
49
+ */
50
+ export interface IndexKey<Keys extends string = string> extends UnorderedIndexKey<Keys> {
51
+ direction?: "asc" | "desc";
52
+ /** Postgres's own default: `last` under `asc`, `first` under `desc`. */
53
+ nulls?: "first" | "last";
54
+ }
55
+ /**
56
+ * The rows a partial index covers.
57
+ *
58
+ * Structure rather than a SQL string, and this is the most load-bearing choice
59
+ * in the type. A string would be replayed verbatim by Atlas in a scratch
60
+ * database, would be the one place a caller reaches for an extension operator
61
+ * class or a subquery, could not be checked against the collection's
62
+ * properties, and could not be fingerprinted — its own text would have to go
63
+ * into the derived name, so reformatting it would rename a live index.
64
+ *
65
+ * Structure keeps every reference resolvable at build time, keeps literals
66
+ * going through the same quoting as the rest of the DDL, and keeps the name
67
+ * stable under any rendering change.
68
+ *
69
+ * There is no `or`. An OR predicate almost always means the index should not
70
+ * be partial at all; a caller who genuinely needs one declares two indexes.
71
+ */
72
+ export type IndexPredicate<Keys extends string = string> = {
73
+ prop: Keys | (string & {});
74
+ op: "=";
75
+ value: string | number | boolean;
76
+ } | {
77
+ prop: Keys | (string & {});
78
+ op: "!=" | "<" | "<=" | ">" | ">=";
79
+ value: string | number;
80
+ } | {
81
+ prop: Keys | (string & {});
82
+ op: "is null" | "is not null";
83
+ }
84
+ /**
85
+ * A non-empty list, enforced in the type. An empty `IN` is a predicate
86
+ * matching nothing: it builds an index over zero rows and reports success,
87
+ * which is the silent-empty-condition shape this codebase has been bitten
88
+ * by before.
89
+ */
90
+ | {
91
+ prop: Keys | (string & {});
92
+ op: "in";
93
+ value: readonly [string | number, ...(string | number)[]];
94
+ } | {
95
+ and: readonly [IndexPredicate<Keys>, ...IndexPredicate<Keys>[]];
96
+ };
97
+ interface BaseCollectionIndex<Keys extends string = string> {
98
+ /**
99
+ * The key columns, in order. This *is* the index's identity.
100
+ *
101
+ * Postgres can only use a leading subset, so `["ownerId", "createdAt"]`
102
+ * serves a query filtering on `ownerId`, and one filtering on both, and
103
+ * never one filtering on `createdAt` alone.
104
+ *
105
+ * Capped at five keys. Postgres allows thirty-two; past four the trailing
106
+ * columns are dead weight on every write, and the declaration is usually
107
+ * someone hoping a query gets faster by accretion. Payload columns that
108
+ * are not searched belong in `include`, which does not count against this.
109
+ */
110
+ on: readonly [Keys | IndexKey<Keys>, ...(Keys | IndexKey<Keys>)[]];
111
+ where?: IndexPredicate<Keys>;
112
+ /**
113
+ * Why this index exists, in one line. Required, and the only required
114
+ * field carrying no SQL.
115
+ *
116
+ * An index is the only thing a Rebase config can declare that costs money
117
+ * forever and whose benefit is invisible from the config. `rebase doctor`
118
+ * prints this beside "0 scans in 34 days, 412 MB", which is the one moment
119
+ * anyone is in a position to decide whether to delete it. Without it
120
+ * nobody can decide, so nobody does, and the table accretes indexes for
121
+ * the life of the product.
122
+ */
123
+ reason: string;
124
+ }
125
+ /**
126
+ * The default. Answers equality, range, `ORDER BY`, and uniqueness.
127
+ */
128
+ export interface BtreeIndex<Keys extends string = string> extends BaseCollectionIndex<Keys> {
129
+ using?: "btree";
130
+ /**
131
+ * A composite uniqueness guarantee.
132
+ *
133
+ * Single-column uniqueness is `validation.unique` on the property, and
134
+ * declaring it here is refused rather than accepted as a synonym.
135
+ * `validation.unique` compiles to an inline `UNIQUE` whose backing index
136
+ * Postgres — not Rebase — names `<table>_<column>_key`. That name is in
137
+ * every deployed database, appears in no contract file, and no release can
138
+ * reach in and rename it.
139
+ */
140
+ unique?: boolean;
141
+ /**
142
+ * Payload columns carried in the leaf pages, for index-only scans. Not
143
+ * searchable and not ordered — they save a heap fetch at the cost of a
144
+ * fatter index. May not overlap `on`.
145
+ */
146
+ include?: readonly (Keys | (string & {}))[];
147
+ }
148
+ /**
149
+ * Containment over an `array` property or a JSONB `map`, using core operator
150
+ * classes only. Trigram and full-text search are `search:`, not this.
151
+ */
152
+ export interface GinIndex<Keys extends string = string> extends BaseCollectionIndex<Keys> {
153
+ using: "gin";
154
+ on: readonly [Keys | UnorderedIndexKey<Keys>, ...(Keys | UnorderedIndexKey<Keys>)[]];
155
+ }
156
+ /**
157
+ * A naturally-ordered column on an append-only table — tiny, and useless the
158
+ * moment rows arrive out of order.
159
+ */
160
+ export interface BrinIndex<Keys extends string = string> extends BaseCollectionIndex<Keys> {
161
+ using: "brin";
162
+ on: readonly [Keys | UnorderedIndexKey<Keys>, ...(Keys | UnorderedIndexKey<Keys>)[]];
163
+ }
164
+ /**
165
+ * An index on a collection's table.
166
+ *
167
+ * No `gist` and no `hash`: every interesting gist operator class ships in an
168
+ * extension, and hash indexes cannot be unique, composite, or ordered.
169
+ *
170
+ * The restriction to core Postgres is not conservatism, it is what keeps the
171
+ * whole model on the Atlas path. `rebase db push` materialises the desired
172
+ * state in a bare scratch database to plan against, `--exclude` does not
173
+ * suppress that replay, and `CREATE EXTENSION` cannot be put in the file — so
174
+ * an index needing `gin_trgm_ops` or `vector_cosine_ops` is refused at build
175
+ * time rather than emitted to fail later against a database the author has
176
+ * never heard of. Trigram search is `search:`; ANN is a `vector` property.
177
+ */
178
+ export type CollectionIndex<Keys extends string = string> = BtreeIndex<Keys> | GinIndex<Keys> | BrinIndex<Keys>;
179
+ export {};