@rebasepro/common 0.15.0 → 0.16.0

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.
@@ -18,7 +18,7 @@ export interface PolicyEvalContext {
18
18
  *
19
19
  * Null here means *anonymous visitor*, not "server context" — a client is
20
20
  * never the server context. `authUid` operands therefore resolve to
21
- * {@link ANONYMOUS_USER_ID} rather than `null`, matching the `auth.uid()`
21
+ * {@link ANONYMOUS_USER_ID} rather than `null`, matching the `rebase.uid()`
22
22
  * the database would see for the same request.
23
23
  */
24
24
  uid?: string | null;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rebasepro/common",
3
3
  "type": "module",
4
- "version": "0.15.0",
4
+ "version": "0.16.0",
5
5
  "description": "Rebase shared core — collection registry, data driver adapter and fluent query builder. No React dependency.",
6
6
  "funding": {
7
7
  "url": "https://github.com/sponsors/rebaseco"
@@ -40,8 +40,8 @@
40
40
  "dependencies": {
41
41
  "fast-equals": "6.0.2",
42
42
  "json-logic-js": "^2.0.5",
43
- "@rebasepro/types": "0.15.0",
44
- "@rebasepro/utils": "0.15.0"
43
+ "@rebasepro/utils": "0.16.0",
44
+ "@rebasepro/types": "0.16.0"
45
45
  },
46
46
  "devDependencies": {
47
47
  "@jest/globals": "^30.4.1",
@@ -30,7 +30,7 @@ import { getPolicyNamesForRules } from "@rebasepro/utils";
30
30
  * rows").
31
31
  *
32
32
  * **For auth collections additionally**
33
- * 3. A permissive **self SELECT** grant (`id = auth.uid()`), so users can read
33
+ * 3. A permissive **self SELECT** grant (`id = rebase.uid()`), so users can read
34
34
  * their own row (profile, session bootstrap) without every app re-declaring
35
35
  * it.
36
36
  * 4. A **restrictive** admin write gate. Restrictive policies are AND'd with
@@ -39,7 +39,7 @@ import { getPolicyNamesForRules } from "@rebasepro/utils";
39
39
  * such as "a user may edit their own row". Without this, a permissive owner
40
40
  * rule would let a user change their own `roles`.
41
41
  *
42
- * The server context is recognised as `auth.uid() IS NULL` (`policy.serverContext()`)
42
+ * The server context is recognised as `rebase.uid() IS NULL` (`policy.serverContext()`)
43
43
  * — the built-in flows that run without a user (signup, migrations) set no user
44
44
  * GUC — which also lets the owner connection satisfy these policies even under
45
45
  * FORCE RLS. A *user* request never reaches that state: an anonymous one carries
@@ -51,7 +51,7 @@ import { getPolicyNamesForRules } from "@rebasepro/utils";
51
51
  // Expressed structurally (not as raw SQL) so the admin UI can evaluate it
52
52
  // exactly — the framework's most security-critical policies must be reflected
53
53
  // precisely, not left as un-evaluable raw clauses. Compiles to
54
- // `auth.uid() IS NULL OR (string_to_array(auth.roles(), ',') && ARRAY['admin'])`.
54
+ // `rebase.uid() IS NULL OR (string_to_array(rebase.roles(), ',') && ARRAY['admin'])`.
55
55
  //
56
56
  // `serverContext()`, emphatically not `not(authenticated())`: the server arm of
57
57
  // this grant must match the server context and nothing else. Anonymous visitors
@@ -1,3 +1,4 @@
1
+ import { rewriteLegacyRlsFunctions } from "@rebasepro/types";
1
2
  import type {
2
3
  ArrayProperty,
3
4
  NumberProperty,
@@ -337,8 +338,14 @@ export function buildCollectionFromTableMetadata(
337
338
  case "UPDATE": operations = ["update"]; break;
338
339
  case "DELETE": operations = ["delete"]; break;
339
340
  }
340
- const qual = policy.qual ?? undefined;
341
- const withCheck = policy.with_check ?? undefined;
341
+ // Normalised on the way in, the same way `sqlToPolicy` normalises
342
+ // what the admin UI reads back. Without it, importing a table from a
343
+ // database provisioned before 1.0 copies `auth.uid()` straight into
344
+ // the project's config — a call to a function the framework no
345
+ // longer creates, which then boots with a legacy-helper warning
346
+ // forever and holds the `auth` schema open.
347
+ const qual = policy.qual ? rewriteLegacyRlsFunctions(policy.qual) : undefined;
348
+ const withCheck = policy.with_check ? rewriteLegacyRlsFunctions(policy.with_check) : undefined;
342
349
  if (qual) {
343
350
  securityRules.push({
344
351
  name: policy.policy_name,
@@ -20,7 +20,7 @@ export interface PolicyEvalContext {
20
20
  *
21
21
  * Null here means *anonymous visitor*, not "server context" — a client is
22
22
  * never the server context. `authUid` operands therefore resolve to
23
- * {@link ANONYMOUS_USER_ID} rather than `null`, matching the `auth.uid()`
23
+ * {@link ANONYMOUS_USER_ID} rather than `null`, matching the `rebase.uid()`
24
24
  * the database would see for the same request.
25
25
  */
26
26
  uid?: string | null;
@@ -68,7 +68,7 @@ export function evaluatePolicy(expr: PolicyExpression, ctx: PolicyEvalContext):
68
68
  return ctx.uid != null && !isAnonymousUid(ctx.uid);
69
69
  case "serverContext":
70
70
  // A client is never the server context. Postgres decides this by
71
- // `auth.uid() IS NULL`, which a client request can never produce:
71
+ // `rebase.uid() IS NULL`, which a client request can never produce:
72
72
  // the driver substitutes ANONYMOUS_USER_ID for a missing id.
73
73
  return false;
74
74
  case "existsIn":
@@ -108,10 +108,10 @@ function resolveOperand(operand: PolicyOperand, ctx: PolicyEvalContext): Resolve
108
108
  case "literal":
109
109
  return { known: true, value: operand.value };
110
110
  case "authUid":
111
- // The sentinel, not null: `auth.uid()` is never NULL for a request
111
+ // The sentinel, not null: `rebase.uid()` is never NULL for a request
112
112
  // that came from a client, so comparing against null here would
113
113
  // disagree with the database on exactly the rules that test for it
114
- // (e.g. `auth.uid() <> 'anonymous'`).
114
+ // (e.g. `rebase.uid() <> 'anonymous'`).
115
115
  return { known: true, value: ctx.uid ?? ANONYMOUS_USER_ID };
116
116
  case "authRoles":
117
117
  return { known: true, value: ctx.roles ?? [] };