@rebasepro/types 0.13.0 → 0.13.1-canary.g18cfeb7

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.
@@ -225,7 +225,7 @@ export interface BaseProperty<CustomProps = unknown> {
225
225
  * written and queryable server-side; it is stripped from every row the API
226
226
  * serves, for every caller, including admins and service keys.
227
227
  *
228
- * This is a server-side guarantee, unlike `ui.hideFromCollection`, which
228
+ * This is a server-side guarantee, unlike `admin.hideFromCollection`, which
229
229
  * only stops the admin panel from *rendering* a field and leaves it in the
230
230
  * JSON payload.
231
231
  */
@@ -1035,6 +1035,18 @@ export interface ImageResize {
1035
1035
  */
1036
1036
  export type JsonLogicRule = Record<string, any>;
1037
1037
 
1038
+ /**
1039
+ * A condition that is either a JSON Logic rule or a literal answer.
1040
+ *
1041
+ * The unconditional case is the common one — "this field is never editable",
1042
+ * "this field is never shown" — and with only a rule accepted it had to be
1043
+ * spelled `{ "==": [1, 1] }`, which reads as a puzzle at the call site. A plain
1044
+ * `true` says the same thing.
1045
+ *
1046
+ * @group Entity properties
1047
+ */
1048
+ export type ConditionRule = JsonLogicRule | boolean;
1049
+
1038
1050
  /**
1039
1051
  * Conditions for individual enum values within a property.
1040
1052
  * @group Entity properties
@@ -1084,8 +1096,10 @@ export interface PropertyConditions {
1084
1096
  * \`\`\`json
1085
1097
  * { "==": [{ "var": "values.status" }, "archived"] }
1086
1098
  * \`\`\`
1099
+ *
1100
+ * A literal `true` disables it unconditionally.
1087
1101
  */
1088
- disabled?: JsonLogicRule;
1102
+ disabled?: ConditionRule;
1089
1103
 
1090
1104
  /**
1091
1105
  * Message to display when the field is disabled by a condition.
@@ -1101,14 +1115,19 @@ export interface PropertyConditions {
1101
1115
  /**
1102
1116
  * Hide the field completely when this condition evaluates to true.
1103
1117
  * The field is removed from the form (not just visually hidden).
1118
+ *
1119
+ * A literal `true` hides it unconditionally. This is the way to keep a
1120
+ * property out of the form without keeping it out of the collection.
1104
1121
  */
1105
- hidden?: JsonLogicRule;
1122
+ hidden?: ConditionRule;
1106
1123
 
1107
1124
  /**
1108
1125
  * Make the field read-only when this condition evaluates to true.
1109
1126
  * Renders as a preview instead of an input.
1127
+ *
1128
+ * A literal `true` makes it read-only unconditionally.
1110
1129
  */
1111
- readOnly?: JsonLogicRule;
1130
+ readOnly?: ConditionRule;
1112
1131
 
1113
1132
  // ═══════════════════════════════════════════════════════════════════════
1114
1133
  // VALIDATION CONDITIONS
@@ -0,0 +1,98 @@
1
+ /**
2
+ * The SQL helper functions RLS policies call, and the schema they live in.
3
+ *
4
+ * ## One schema, and it is ours
5
+ *
6
+ * Rebase creates exactly one schema in a project's database: `rebase`. These
7
+ * three functions live in it alongside the framework's own tables, and that is
8
+ * the whole contract — a reader can look at a database and know precisely which
9
+ * namespace belongs to the framework and that nothing else was touched.
10
+ *
11
+ * It used to be two. `uid()`, `jwt()` and `roles()` sat in a schema called
12
+ * `auth`, which is Supabase's name, chosen so that a developer who had written
13
+ * Supabase RLS would recognise `auth.uid()`. The familiarity was real but the
14
+ * name was not Rebase's to take, and taking it had a concrete cost: pointing
15
+ * Rebase at a database that already had a Supabase `auth` schema meant
16
+ * `CREATE OR REPLACE FUNCTION auth.uid() RETURNS text` against Supabase's
17
+ * `RETURNS uuid`, which Postgres rejects outright —
18
+ *
19
+ * ERROR: cannot change return type of existing function
20
+ * HINT: Use DROP FUNCTION auth.uid() first.
21
+ *
22
+ * — and the failure landed inside a catch-all that logged a warning and carried
23
+ * on, leaving a database with auth tables, no helper functions, and policies
24
+ * calling functions that did not exist. Under `rebase db migrate` the same
25
+ * statements aborted the migration instead.
26
+ *
27
+ * `rebase.uid()` collides with nobody. A Supabase database keeps its `auth`
28
+ * schema untouched and gains a `rebase` one, which is what a gradual migration
29
+ * needs.
30
+ *
31
+ * ## Why functions at all, rather than inlining `current_setting`
32
+ *
33
+ * Because the indirection has already been spent once. `uid()` resolves
34
+ * `app.uid` and falls back to the pre-rename `app.user_id`, so that during a
35
+ * rolling deploy — old and new pods serving one database — both eras resolve
36
+ * the principal. That was a single `CREATE OR REPLACE`. Inlined into policy
37
+ * bodies it would have been a rewrite of every policy on every table.
38
+ *
39
+ * ## Why the name is not configurable
40
+ *
41
+ * A policy body is stored SQL: Postgres parses `USING (…)` once and keeps it, so
42
+ * these strings are written into every policy in every database Rebase has
43
+ * provisioned. Everything that reads policies back — the SQL-to-policy parser
44
+ * behind the admin UI, the drift checker, `rls-check` — would have to know the
45
+ * configured value to recognise its own output. One frozen name is the feature.
46
+ */
47
+
48
+ /** The schema Rebase owns. The only schema Rebase creates. */
49
+ export const REBASE_SCHEMA = "rebase";
50
+
51
+ /**
52
+ * The principal of the current request, as text, or NULL in the server context.
53
+ *
54
+ * Never NULL for a user request — an anonymous one carries
55
+ * {@link ANONYMOUS_USER_ID} — which is what makes `IS NULL` a reliable test for
56
+ * the trusted server plane and `IS NOT NULL` a tautology.
57
+ */
58
+ export const RLS_UID_SQL = `${REBASE_SCHEMA}.uid()`;
59
+
60
+ /** The request's roles as a comma-separated string, for `string_to_array`. */
61
+ export const RLS_ROLES_SQL = `${REBASE_SCHEMA}.roles()`;
62
+
63
+ /** The request's JWT claims as `jsonb`, or `{}`. */
64
+ export const RLS_JWT_SQL = `${REBASE_SCHEMA}.jwt()`;
65
+
66
+ /**
67
+ * The pre-1.0 spellings, for recognising policies and hand-written SQL that
68
+ * predate the move.
69
+ *
70
+ * Kept because policies outlive the server that wrote them: a database migrated
71
+ * by an older release still holds `auth.uid()` in its policy bodies until the
72
+ * next push or boot recompiles them, and anything that reads policies back has
73
+ * to recognise both eras or report the framework's own output as foreign drift.
74
+ * Also used to give a project whose `securityRules` contain raw `auth.uid()` a
75
+ * message naming the replacement, instead of a parse failure.
76
+ */
77
+ export const LEGACY_RLS_SCHEMA = "auth";
78
+ export const LEGACY_RLS_UID_SQL = `${LEGACY_RLS_SCHEMA}.uid()`;
79
+ export const LEGACY_RLS_ROLES_SQL = `${LEGACY_RLS_SCHEMA}.roles()`;
80
+ export const LEGACY_RLS_JWT_SQL = `${LEGACY_RLS_SCHEMA}.jwt()`;
81
+
82
+ /**
83
+ * Rewrites the pre-1.0 function calls in a fragment of policy SQL.
84
+ *
85
+ * Deliberately anchored on a word boundary and the schema qualifier, so a column
86
+ * called `auth_uid` or a table named `auth` is left alone.
87
+ */
88
+ export function rewriteLegacyRlsFunctions(sql: string): string {
89
+ return sql.replace(
90
+ /\bauth\.(uid|jwt|roles)\s*\(\s*\)/gi,
91
+ (_match, fn: string) => `${REBASE_SCHEMA}.${fn.toLowerCase()}()`
92
+ );
93
+ }
94
+
95
+ /** Whether a fragment of SQL still calls the pre-1.0 functions. */
96
+ export function usesLegacyRlsFunctions(sql: string): boolean {
97
+ return /\bauth\.(uid|jwt|roles)\s*\(\s*\)/i.test(sql);
98
+ }