@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
@@ -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"];
32
+ export declare const ADMIN_COLLECTION_KEYS: readonly ["Actions", "additionalFields", "alwaysApplyDefaultValues", "browserCallbacks", "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
  /**
@@ -49,7 +49,7 @@ export type AdminCollectionKey = typeof ADMIN_COLLECTION_KEYS[number];
49
49
  *
50
50
  * @group Models
51
51
  */
52
- export declare const ADMIN_PROPERTY_KEYS: readonly ["canAddElements", "clearable", "columnWidth", "customProps", "disabled", "expanded", "Field", "Filter", "filterOperators", "fixedFilter", "hideFromCollection", "includeEntityLink", "includeId", "markdown", "minimalistView", "multiline", "Preview", "previewAsTag", "previewProperties", "readOnly", "sortable", "span", "spreadChildren", "urlPreview", "widget"];
52
+ export declare const ADMIN_PROPERTY_KEYS: readonly ["canAddElements", "clearable", "columnWidth", "customProps", "disabled", "expanded", "Field", "Filter", "filterOperators", "fixedFilter", "format", "hideFromCollection", "includeEntityLink", "includeId", "markdown", "minimalistView", "multiline", "Preview", "previewAsTag", "previewProperties", "readOnly", "renderInForm", "sortable", "span", "spreadChildren", "urlPreview", "widget"];
53
53
  /** A key of a property's `admin` block. @group Models */
54
54
  export type AdminPropertyKey = typeof ADMIN_PROPERTY_KEYS[number];
55
55
  /**
@@ -29,7 +29,30 @@
29
29
  *
30
30
  * @group Auth
31
31
  */
32
- import type { Hono } from "hono";
32
+ /**
33
+ * A sub-app an adapter hands back for the framework to mount.
34
+ *
35
+ * Structural on purpose. This was `Hono<any, any, any>`, and that one
36
+ * `import type { Hono } from "hono"` was the whole reason `@rebasepro/types`
37
+ * peer-depended on hono — which npm and pnpm auto-install, so **every browser
38
+ * app that installed `@rebasepro/client` got a 2.8 MB server framework in its
39
+ * lockfile and its security scanners**, for a type used by two optional methods
40
+ * that a browser app can never call.
41
+ *
42
+ * The shape is the same duck-type the function loader already validates at
43
+ * runtime (`isHonoLike`: a `fetch` method and a `routes` array), and a real
44
+ * `Hono` satisfies it — asserted in `auth_adapter.test.ts`, which does import
45
+ * hono, because a structural type is only worth having if something proves the
46
+ * real thing still fits.
47
+ *
48
+ * @group Auth
49
+ */
50
+ export interface MountableRouter {
51
+ /** Handle a request. Hono, Elysia and a hand-rolled router all have this. */
52
+ fetch: (request: Request) => Response | Promise<Response>;
53
+ /** The routes the sub-app declares. Only its presence is contractual. */
54
+ routes: readonly unknown[];
55
+ }
33
56
  /**
34
57
  * The normalized user object returned by `AuthAdapter.verifyRequest()`.
35
58
  *
@@ -51,6 +74,18 @@ export interface AuthenticatedUser {
51
74
  roles: string[];
52
75
  /** Whether the user has admin privileges. */
53
76
  isAdmin: boolean;
77
+ /**
78
+ * Whether this session is a GUEST — anonymous sign-in rather than an
79
+ * account.
80
+ *
81
+ * Optional, and absent means "not a guest", so an external adapter that
82
+ * has no such concept keeps behaving exactly as it did. When present it
83
+ * reaches the database as `rebase.is_anonymous()`, which is what lets a
84
+ * policy say "a signed-in person, not somebody who pressed Continue as
85
+ * guest" — two things that were the same principal inside every rule,
86
+ * because anonymous sign-in mints a real user row with a real uid.
87
+ */
88
+ isAnonymous?: boolean;
54
89
  /** Raw bearer token from the request (for forwarding). */
55
90
  rawToken?: string;
56
91
  /** Extra claims/metadata from the auth provider. */
@@ -363,19 +398,19 @@ export interface AuthAdapter {
363
398
  * - External adapter: typically returns `undefined` (auth is handled externally).
364
399
  * - Custom adapter: user mounts their own routes.
365
400
  *
366
- * The return type uses `Hono<any, any, any>` because this sub-app will be
367
- * mounted into a parent app via `.route()`, which accepts any Hono env type.
368
- * Adapter implementations are free to use their own env (e.g. `Hono<HonoEnv>`).
401
+ * The return type is the structural {@link MountableRouter} rather than
402
+ * `Hono`, so this package needs no hono dependency. A real `Hono<HonoEnv>`
403
+ * satisfies it, and implementations are free to use their own env.
369
404
  *
370
405
  * @returns A Hono sub-app with auth routes, or `undefined` to skip route mounting.
371
406
  */
372
- createAuthRoutes?(): Hono<any, any, any> | undefined;
407
+ createAuthRoutes?(): MountableRouter | undefined;
373
408
  /**
374
409
  * Mount admin routes (e.g. password reset for users).
375
410
  *
376
411
  * @returns A Hono sub-app with admin routes, or `undefined` to skip.
377
412
  */
378
- createAdminRoutes?(): Hono<any, any, any> | undefined;
413
+ createAdminRoutes?(): MountableRouter | undefined;
379
414
  /**
380
415
  * Advertise what this auth adapter supports.
381
416
  *
@@ -637,6 +637,27 @@ export interface BackendBootstrapper {
637
637
  * Return admin capabilities for this driver.
638
638
  */
639
639
  getAdmin?(driverResult: InitializedDriver): DatabaseAdmin | undefined;
640
+ /**
641
+ * Ask the database whether it is there, before anything else touches it.
642
+ *
643
+ * Boot's first database call is not `initializeDriver` — it is the schema
644
+ * provisioning that runs ahead of it, and a driver's connection diagnosis
645
+ * therefore never got the chance to run. A stopped database produced
646
+ * `Failed query: [redacted]` and a stack through drizzle internals: no host,
647
+ * no port, no `ECONNREFUSED`, and no hint about starting the thing.
648
+ *
649
+ * Implementations MUST issue the cheapest round trip they have (`SELECT 1`),
650
+ * MUST throw an error whose message names the host, the port and the
651
+ * driver's own reason, and MAY log a fuller diagnosis first. They MUST NOT
652
+ * throw for a reachable database that merely answered something unexpected —
653
+ * the caller treats a throw as fatal.
654
+ *
655
+ * `driverResult` is optional for the same reason as
656
+ * {@link ensureCollectionSchema}: this runs before `initializeDriver`, so an
657
+ * adapter that was constructed with its own connection has to fall back to
658
+ * it.
659
+ */
660
+ verifyConnection?(driverResult?: InitializedDriver): Promise<void>;
640
661
  /**
641
662
  * Bring the database's collection tables up to date, additively.
642
663
  *
@@ -680,6 +701,33 @@ export interface BackendBootstrapper {
680
701
  ensureCollectionPolicies?(collections: unknown[], driverResult?: InitializedDriver, log?: (message: string) => void): Promise<{
681
702
  applied: number;
682
703
  }>;
704
+ /**
705
+ * Create the RLS helper functions on this source's database. See
706
+ * `DatabaseAdapter.ensureRlsRuntime`; needed on every source that is not
707
+ * the default, whose helpers arrive with the auth tables.
708
+ */
709
+ ensureRlsRuntime?(driverResult?: InitializedDriver): Promise<void>;
710
+ /**
711
+ * Re-check, after the schema exists, that requests will actually be
712
+ * constrained by the database's own authorization.
713
+ *
714
+ * A driver that isolates user requests by switching to a restricted role has
715
+ * to decide at connect time whether the switch is needed — and on a fresh
716
+ * database that question is asked before there is anything to answer with.
717
+ * The process then creates the schema, becomes its owner, and an owner is
718
+ * exempt from the policies on what it owns. So the answer that was true when
719
+ * the driver initialized can be false by the time it serves a request.
720
+ *
721
+ * This is where a driver asks again. It runs once, after collection tables,
722
+ * auth tables and policies are all in place, and it MUST fail rather than
723
+ * serve when the answer changed and cannot be acted on: booting anyway
724
+ * produces exactly the unenforced server this exists to prevent.
725
+ *
726
+ * Optional, because it is only meaningful for drivers whose isolation
727
+ * depends on state the schema affects. A driver with nothing to re-check
728
+ * omits it.
729
+ */
730
+ finalizeSecurityPosture?(driverResult: InitializedDriver): Promise<void>;
683
731
  /**
684
732
  * Read the collections schema version this database was last provisioned
685
733
  * from, or `null` when nothing has ever stamped it.
@@ -76,6 +76,15 @@ export interface BaseCollectionConfig<M extends Record<string, unknown> = Record
76
76
  *
77
77
  * // A direct-transport Firestore data source registered as "analytics"
78
78
  * { slug: "events", dataSource: "analytics" }
79
+ *
80
+ * // The same, spelled once — `defineCollection` accepts the handle
81
+ * // `database("analytics")` returned and records its key here.
82
+ * import { analytics } from "../resources";
83
+ * defineCollection({ slug: "events", dataSource: analytics, … })
84
+ *
85
+ * A string on the recorded collection, because a collection is data past
86
+ * `defineCollection`: it serialises, it compares with `===`, and it reaches
87
+ * the admin UI over the wire, none of which a handle survives.
79
88
  */
80
89
  dataSource?: string;
81
90
  /**
@@ -188,8 +197,23 @@ export interface PostgresCollectionConfig<M extends Record<string, unknown> = Re
188
197
  engine?: "postgres" | undefined;
189
198
  /**
190
199
  * The PostgreSQL table name for this collection.
200
+ *
201
+ * Optional: it defaults to `toSnakeCase(slug)`, which is what
202
+ * `getTableName()` has always returned when it was absent. The type simply
203
+ * demanded what the runtime already derived, so the smallest collection
204
+ * anyone could write named its table twice —
205
+ * `{ slug: "todos", table: "todos", … }` — and "why do I write it twice"
206
+ * is the first question every evaluator asked.
207
+ *
208
+ * Set it only when the table name differs from the slug: an existing
209
+ * database whose table is `blog_posts` while the URL should stay `posts`.
210
+ *
211
+ * Note that a **derived** name is still a real name, and nothing yet warns
212
+ * when one moves. Foreign-key and junction column defaults are derived from
213
+ * the *slug* rather than from this field, so renaming a slug re-derives
214
+ * them on the next `db push` even where `table` is pinned.
191
215
  */
192
- table: string;
216
+ table?: string;
193
217
  /**
194
218
  * The PostgreSQL schema name for this table.
195
219
  * E.g. "public", "rebase", "auth".
@@ -12,6 +12,16 @@ import type { RebaseServerClient } from "../controllers/client.js";
12
12
  */
13
13
  export interface CronJobDefinition {
14
14
  schedule: string;
15
+ /**
16
+ * IANA time zone the schedule is read in, e.g. `"Europe/Madrid"`.
17
+ *
18
+ * Without it the expression is read in the process's own zone — UTC in
19
+ * nearly every container, the developer's own on a laptop — so `0 3 * * *`
20
+ * fires at a different wall-clock hour either side of a deploy. Name the
21
+ * zone and the schedule means one thing everywhere it runs. An unknown zone
22
+ * is refused when the job loads rather than silently read as local time.
23
+ */
24
+ timezone?: string;
15
25
  /** Human-readable name shown in the Studio UI. */
16
26
  name: string;
17
27
  /** Optional description shown in the Studio UI. */
@@ -74,6 +84,30 @@ export interface CronJobContext {
74
84
  scheduledAt: Date;
75
85
  /** A simple logger scoped to this job run. */
76
86
  log: (...args: unknown[]) => void;
87
+ /**
88
+ * Aborted when the run exceeds `timeoutSeconds`.
89
+ *
90
+ * The timeout has always stopped the scheduler *waiting* — it loses the
91
+ * race and the run is recorded as failed. It has never stopped the handler:
92
+ * a `fetch` to an unresponsive host kept its socket, and a job on a
93
+ * five-minute schedule with a five-minute timeout accumulated one abandoned
94
+ * request per tick until the process ran out of sockets, all of it invisible
95
+ * because the run was already marked failed.
96
+ *
97
+ * Pass it to anything that takes one, and the work stops when the run does:
98
+ *
99
+ * @example
100
+ * export default defineCron({
101
+ * name: "Sync inventory",
102
+ * schedule: "*\/15 * * * *",
103
+ * timeoutSeconds: 60,
104
+ * async handler({ signal, log }) {
105
+ * const res = await fetch("https://supplier.example.com/stock", { signal });
106
+ * log(`fetched ${res.status}`);
107
+ * }
108
+ * });
109
+ */
110
+ signal: AbortSignal;
77
111
  /**
78
112
  * The server-side Rebase singleton — the **same object** `import { rebase }
79
113
  * from "@rebasepro/server"` returns, and the same one `defineFunction`
@@ -75,6 +75,16 @@ export interface DatabaseAdapter {
75
75
  * accepted every client as already authenticated.
76
76
  */
77
77
  initializeWebsockets?(server: unknown, realtimeService: RealtimeProvider, driver: DataDriver, config?: unknown, adapter?: import("./auth_adapter.js").AuthAdapter): Promise<void> | void;
78
+ /**
79
+ * Ask the database whether it is there, before boot's first real query. See
80
+ * `BackendBootstrapper.verifyConnection` for the contract.
81
+ *
82
+ * Same forwarding requirement as the hooks below, and the symptom of
83
+ * dropping it is the failure this hook exists to prevent: a stopped database
84
+ * reported as `Failed query: [redacted]` and a stack through drizzle
85
+ * internals, naming no host, no port and no reason.
86
+ */
87
+ verifyConnection?(driverResult?: InitializedDriver): Promise<void>;
78
88
  /**
79
89
  * Bring the database's collection tables up to date, additively — the boot
80
90
  * companion to `db push`. See `BackendBootstrapper.ensureCollectionSchema`
@@ -109,6 +119,28 @@ export interface DatabaseAdapter {
109
119
  ensureCollectionPolicies?(collections: unknown[], driverResult?: InitializedDriver, log?: (message: string) => void): Promise<{
110
120
  applied: number;
111
121
  }>;
122
+ /**
123
+ * Create the helper functions the generated policies call (`rebase.uid()`,
124
+ * `rebase.roles()` and their siblings) on THIS source's database.
125
+ *
126
+ * On the default source these arrive with the auth tables, because auth
127
+ * lives there. A second database gets no auth tables and had no helpers
128
+ * either, so every policy on it failed to create and every user read was
129
+ * denied — a collection declared on `database("analytics")` answered 401 on
130
+ * a healthy-looking backend. Idempotent; runs before
131
+ * `ensureCollectionPolicies` on every non-default source.
132
+ */
133
+ ensureRlsRuntime?(driverResult?: InitializedDriver): Promise<void>;
134
+ /**
135
+ * Re-check, once the schema exists, that requests will really be constrained
136
+ * by the database's own authorization. See
137
+ * `BackendBootstrapper.finalizeSecurityPosture`.
138
+ *
139
+ * Same forwarding requirement as the two hooks above, and the most costly
140
+ * one to drop: a wrapper that omits it turns a fresh deployment's first boot
141
+ * into a server that applies no row-level policy at all, and says nothing.
142
+ */
143
+ finalizeSecurityPosture?(driverResult: InitializedDriver): Promise<void>;
112
144
  /**
113
145
  * Read the collections schema version this database was last provisioned
114
146
  * from, or `null` when nothing has ever stamped it.
@@ -153,6 +185,13 @@ export interface DatabaseAdapter {
153
185
  export interface DatabaseAdapterInitConfig {
154
186
  /** Registered collection definitions. */
155
187
  collections: CollectionConfig[];
188
+ /**
189
+ * The data-source key this driver serves — `(default)`, or a declared
190
+ * one such as `analytics`. A driver given every collection needs it to
191
+ * know which are its own: the ones whose `dataSource` names this key, or
192
+ * name none when this is the default.
193
+ */
194
+ dataSourceKey?: string;
156
195
  /** The shared collection registry to register into. */
157
196
  collectionRegistry: CollectionRegistryInterface;
158
197
  /**
@@ -91,11 +91,24 @@ export type BeforeSaveProps<M extends Record<string, unknown> = Record<string, u
91
91
  id?: string | number;
92
92
  };
93
93
  /**
94
- * Parameters passed to hooks before a entity is saved
94
+ * Parameters passed to hooks when a save fails.
95
+ *
96
+ * `id` is optional because a failed create may never have been assigned one.
97
+ * `error` is what the save threw — the reason the hook exists. Documented since
98
+ * the callbacks guide first shipped, and until now not on the type or on the
99
+ * object: a handler that read `props.error` compiled and logged `undefined`.
100
+ *
95
101
  * @group Models
96
102
  */
97
103
  export type AfterSaveErrorProps<M extends Record<string, unknown> = Record<string, unknown>, USER extends User = User> = Omit<AfterSaveProps<M, USER>, "id"> & {
98
104
  id?: string | number;
105
+ /**
106
+ * Whatever the save threw: a `RebaseApiError` when a `before*` callback
107
+ * or a validator refused it, otherwise the driver's error with the
108
+ * SQLSTATE in its cause chain. Not narrowed, because a callback may
109
+ * throw anything.
110
+ */
111
+ error: unknown;
99
112
  };
100
113
  /**
101
114
  * Parameters passed to hooks when a entity is saved
@@ -225,6 +225,13 @@ export type FilterValues<Key extends string> = Partial<Record<Key, [WhereFilterO
225
225
  * closed. A typo'd relation path is refused at runtime with the target
226
226
  * collection's real column list in the message.
227
227
  *
228
+ * A JSON path — `metadata->>tier` — is admitted on the same terms and for the
229
+ * same reason. It addresses a key *inside* a `json`/`jsonb` column, so nothing
230
+ * in a generated row type describes it either; the driver resolves it and
231
+ * refuses what it cannot. It has no dot, so the dotted branch above never
232
+ * covered it, and every documented `?metadata->>tier=eq.gold` filter was a
233
+ * compile error on a typed client while working perfectly over HTTP.
234
+ *
228
235
  * Undotted keys are unaffected and still checked against `keyof M`.
229
236
  *
230
237
  * When `M` is left at its default `Record<string, unknown>`, `keyof M` is
@@ -234,7 +241,23 @@ export type FilterValues<Key extends string> = Partial<Record<Key, [WhereFilterO
234
241
  *
235
242
  * @group Models
236
243
  */
237
- export type FieldPath<M extends Record<string, unknown> = Record<string, unknown>> = Extract<keyof M, string> | `${string}.${string}`;
244
+ export type FieldPath<M extends Record<string, unknown> = Record<string, unknown>> = Extract<keyof M, string> | NonColumnFieldPath;
245
+ /**
246
+ * A field key that is not a column: a relation path (`author.name`) or a JSON
247
+ * path (`metadata->>tier`).
248
+ *
249
+ * The fluent builder needs this on its own, where `FindParams` does not. Its
250
+ * `where(column, operator, value)` types the value against `M[column]`, which
251
+ * only means something for a real column — so paths take a second overload
252
+ * whose value is `unknown`. Keying that overload on the *shape* of a path,
253
+ * rather than on "everything that is not a column", is what keeps a real column
254
+ * with a wrong value type from falling through to it and being accepted: a
255
+ * mistyped column name has neither a dot nor a `->>`, so it matches neither
256
+ * overload and is still refused.
257
+ *
258
+ * @group Models
259
+ */
260
+ export type NonColumnFieldPath = `${string}.${string}` | `${string}->>${string}`;
238
261
  /**
239
262
  * Relaxed filter type that also accepts pre-serialized PostgREST strings.
240
263
  * **Internal only** — used at the wire-format boundary
@@ -17,7 +17,7 @@
17
17
  *
18
18
  * @group Models
19
19
  */
20
- export type PolicyExpression = TruePolicyExpression | FalsePolicyExpression | AndPolicyExpression | OrPolicyExpression | NotPolicyExpression | ComparePolicyExpression | RolesOverlapPolicyExpression | RolesContainPolicyExpression | AuthenticatedPolicyExpression | ServerContextPolicyExpression | ExistsInPolicyExpression | RawPolicyExpression;
20
+ export type PolicyExpression = TruePolicyExpression | FalsePolicyExpression | AndPolicyExpression | OrPolicyExpression | NotPolicyExpression | ComparePolicyExpression | RolesOverlapPolicyExpression | RolesContainPolicyExpression | AuthenticatedPolicyExpression | RegisteredPolicyExpression | ServerContextPolicyExpression | ExistsInPolicyExpression | RawPolicyExpression;
21
21
  /**
22
22
  * The id a request without a logged-in user reports as `rebase.uid()`.
23
23
  *
@@ -137,6 +137,33 @@ export interface RolesContainPolicyExpression {
137
137
  export interface AuthenticatedPolicyExpression {
138
138
  kind: "authenticated";
139
139
  }
140
+ /**
141
+ * True for a signed-in caller who has an ACCOUNT — not a guest.
142
+ *
143
+ * The distinction {@link AuthenticatedPolicyExpression} cannot make. Anonymous
144
+ * SIGN-IN (`POST /auth/anonymous`) mints a real user row with a real uid and a
145
+ * real session, so such a caller is "authenticated" by every test that looks at
146
+ * `rebase.uid()`: same shape, same default role, indistinguishable inside a
147
+ * policy. On a deployment with anonymous sign-in enabled, every rule meaning
148
+ * "a signed-in person" therefore also meant "anybody at all", since pressing
149
+ * Continue as guest needs no email, no password and no agreement to anything.
150
+ *
151
+ * Note the two senses of "anonymous", which is the reason this was easy to
152
+ * miss. {@link ANONYMOUS_USER_ID} is the sentinel for a request carrying NO
153
+ * session, and `authenticated()` already excludes it. A guest is the other
154
+ * thing: a session with nobody behind it. This node excludes both.
155
+ *
156
+ * Compiles to `authenticated() AND NOT rebase.is_anonymous()`.
157
+ *
158
+ * Use it wherever a rule is about a person who could be held responsible for
159
+ * something — writing a review, joining an organization, spending money. Use
160
+ * `authenticated()` where a guest is genuinely welcome, which is what
161
+ * anonymous sign-in is for: a cart before checkout, a draft before signup.
162
+ * @group Models
163
+ */
164
+ export interface RegisteredPolicyExpression {
165
+ kind: "registered";
166
+ }
140
167
  /**
141
168
  * True only in the trusted **server context** — the built-in flows that run
142
169
  * without a user (signup, migrations, `dataAsAdmin`) set no user GUC, so
@@ -255,6 +282,7 @@ export declare const policy: {
255
282
  rolesOverlap: (roles: readonly string[]) => RolesOverlapPolicyExpression;
256
283
  rolesContain: (roles: readonly string[]) => RolesContainPolicyExpression;
257
284
  authenticated: () => AuthenticatedPolicyExpression;
285
+ registered: () => RegisteredPolicyExpression;
258
286
  serverContext: () => ServerContextPolicyExpression;
259
287
  existsIn: (args: {
260
288
  collection: string;