@rebasepro/server-postgres 0.12.1-canary.gdfba2a1 → 0.12.1-canary.ge1bb178

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 (87) hide show
  1. package/dist/PostgresBackendDriver.d.ts +1 -1
  2. package/dist/PostgresBootstrapper.d.ts +25 -1
  3. package/dist/auth/services.d.ts +6 -1
  4. package/dist/backup/backup-service.d.ts +10 -1
  5. package/dist/backup/pg-tools.d.ts +47 -0
  6. package/dist/{backup-service-DH9kPg-E.js → backup-service-CD8o_1Sl.js} +144 -11
  7. package/dist/backup-service-CD8o_1Sl.js.map +1 -0
  8. package/dist/cli-helpers.d.ts +39 -0
  9. package/dist/{connection-B5Wndbr1.js → connection-BuZ97wsr.js} +57 -3
  10. package/dist/connection-BuZ97wsr.js.map +1 -0
  11. package/dist/connection.d.ts +42 -0
  12. package/dist/ensure-collection-policies-BrUVgjz3.js +57 -0
  13. package/dist/ensure-collection-policies-BrUVgjz3.js.map +1 -0
  14. package/dist/ensure-collection-tables-Da2oGkX2.js +650 -0
  15. package/dist/ensure-collection-tables-Da2oGkX2.js.map +1 -0
  16. package/dist/index.es.js +529 -142
  17. package/dist/index.es.js.map +1 -1
  18. package/dist/policy-CeA1JcxP.js +105 -0
  19. package/dist/policy-CeA1JcxP.js.map +1 -0
  20. package/dist/schema/ensure-collection-policies.d.ts +60 -0
  21. package/dist/schema/ensure-collection-tables.d.ts +44 -2
  22. package/dist/schema/generate-postgres-ddl-logic.d.ts +135 -1
  23. package/dist/schema/introspect-db-constraints.d.ts +57 -0
  24. package/dist/schema/introspect-db-logic.d.ts +94 -5
  25. package/dist/schema/introspect-db-queries.d.ts +119 -0
  26. package/dist/schema/introspect-db-structure.d.ts +263 -0
  27. package/dist/schema/introspect-db-types.d.ts +11 -0
  28. package/dist/services/FetchService.d.ts +4 -1
  29. package/dist/services/RelationService.d.ts +24 -1
  30. package/dist/services/channel-bus/index.d.ts +1 -7
  31. package/dist/services/collection-helpers.d.ts +24 -1
  32. package/dist/services/dataService.d.ts +3 -1
  33. package/dist/services/row-pipeline.d.ts +1 -1
  34. package/dist/{src-DihrDFuP.js → src-CzbghKwf.js} +189 -101
  35. package/dist/src-CzbghKwf.js.map +1 -0
  36. package/dist/utils/connection-string.d.ts +29 -0
  37. package/dist/utils/drizzle-conditions.d.ts +5 -4
  38. package/dist/utils/pg-error-utils.d.ts +19 -0
  39. package/dist/{websocket-BKcGvILX.js → websocket-B2LsrINK.js} +8 -6
  40. package/dist/websocket-B2LsrINK.js.map +1 -0
  41. package/package.json +6 -6
  42. package/src/PostgresAdapter.ts +21 -2
  43. package/src/PostgresBackendDriver.ts +4 -0
  44. package/src/PostgresBootstrapper.ts +192 -33
  45. package/src/auth/services.ts +6 -3
  46. package/src/backup/backup-cli.ts +41 -2
  47. package/src/backup/backup-service.ts +38 -5
  48. package/src/backup/pg-tools.ts +96 -3
  49. package/src/cli-helpers.ts +70 -0
  50. package/src/cli.ts +44 -26
  51. package/src/collections/validate-relations.ts +15 -0
  52. package/src/connection.ts +73 -0
  53. package/src/data-transformer.ts +9 -3
  54. package/src/databasePoolManager.ts +5 -2
  55. package/src/schema/ensure-collection-policies.ts +105 -0
  56. package/src/schema/ensure-collection-tables.test.ts +105 -9
  57. package/src/schema/ensure-collection-tables.ts +220 -32
  58. package/src/schema/generate-drizzle-schema-logic.ts +16 -3
  59. package/src/schema/generate-postgres-ddl-logic.ts +375 -16
  60. package/src/schema/introspect-db-constraints.ts +385 -0
  61. package/src/schema/introspect-db-inference.ts +18 -8
  62. package/src/schema/introspect-db-logic.ts +385 -71
  63. package/src/schema/introspect-db-queries.ts +326 -0
  64. package/src/schema/introspect-db-structure.ts +670 -0
  65. package/src/schema/introspect-db-types.ts +56 -0
  66. package/src/schema/introspect-db.ts +37 -80
  67. package/src/schema/introspect-runtime.test.ts +56 -8
  68. package/src/schema/introspect-runtime.ts +31 -9
  69. package/src/security/policy-drift.test.ts +11 -3
  70. package/src/services/FetchService.ts +76 -14
  71. package/src/services/PersistService.ts +20 -6
  72. package/src/services/RelationService.ts +249 -48
  73. package/src/services/channel-bus/index.ts +0 -9
  74. package/src/services/collection-helpers.ts +40 -1
  75. package/src/services/dataService.ts +3 -1
  76. package/src/services/realtimeService.ts +3 -3
  77. package/src/services/row-pipeline.ts +1 -1
  78. package/src/utils/connection-string.ts +58 -0
  79. package/src/utils/drizzle-conditions.ts +31 -6
  80. package/src/utils/pg-error-utils.ts +46 -0
  81. package/src/websocket.ts +18 -9
  82. package/dist/backup-service-DH9kPg-E.js.map +0 -1
  83. package/dist/connection-B5Wndbr1.js.map +0 -1
  84. package/dist/ensure-collection-tables-CIHSH962.js +0 -304
  85. package/dist/ensure-collection-tables-CIHSH962.js.map +0 -1
  86. package/dist/src-DihrDFuP.js.map +0 -1
  87. package/dist/websocket-BKcGvILX.js.map +0 -1
@@ -1,8 +1,9 @@
1
1
  import { createRequire as __createRequire } from "module";
2
2
  import "process";
3
3
  __createRequire(import.meta.url);
4
- import { c as __require, o as __commonJSMin } from "./connection-B5Wndbr1.js";
4
+ import { l as __require, s as __commonJSMin } from "./connection-BuZ97wsr.js";
5
5
  import { a as getDataSourceCapabilities, c as toCanonicalOp, n as isPostgresCollectionConfig, o as NULL_OPS, r as isRelationalCollectionConfig, s as REST_TO_CANONICAL, t as getDeclaredSubcollections } from "./src-DoU9yPqq.js";
6
+ import { n as ANONYMOUS_USER_IDS, r as policy, t as ANONYMOUS_USER_ID } from "./policy-CeA1JcxP.js";
6
7
  //#region ../types/src/types/entities.ts
7
8
  /**
8
9
  * Class used to create a reference to a entity in a different path
@@ -55,81 +56,6 @@ function isManyToMany(relation) {
55
56
  return relation.kind === "manyToMany";
56
57
  }
57
58
  //#endregion
58
- //#region ../types/src/types/policy.ts
59
- /**
60
- * The id a request without a logged-in user reports as `auth.uid()`.
61
- *
62
- * A user-context request always sets `app.uid`: blank would read back as
63
- * `NULL`, and `NULL` is how the trusted server context is recognised, so an
64
- * anonymous visitor would be promoted to server privileges. The driver
65
- * therefore substitutes this sentinel at the single chokepoint where the GUC
66
- * is set.
67
- *
68
- * The consequence for policy authors is that **`auth.uid() IS NOT NULL` is a
69
- * tautology on the user path** — it is true for anonymous visitors too. Use
70
- * {@link policy.authenticated} (or `auth.uid() <> 'anonymous'`) to mean "signed
71
- * in", and {@link policy.serverContext} to mean "the trusted server context".
72
- *
73
- * @group Models
74
- */
75
- var ANONYMOUS_USER_ID = "anonymous";
76
- /** @group Models */
77
- var policy = {
78
- true: () => ({ kind: "true" }),
79
- false: () => ({ kind: "false" }),
80
- and: (...operands) => ({
81
- kind: "and",
82
- operands
83
- }),
84
- or: (...operands) => ({
85
- kind: "or",
86
- operands
87
- }),
88
- not: (operand) => ({
89
- kind: "not",
90
- operand
91
- }),
92
- compare: (left, op, right) => ({
93
- kind: "compare",
94
- op,
95
- left,
96
- right
97
- }),
98
- rolesOverlap: (roles) => ({
99
- kind: "rolesOverlap",
100
- roles
101
- }),
102
- rolesContain: (roles) => ({
103
- kind: "rolesContain",
104
- roles
105
- }),
106
- authenticated: () => ({ kind: "authenticated" }),
107
- serverContext: () => ({ kind: "serverContext" }),
108
- existsIn: (args) => ({
109
- kind: "existsIn",
110
- collection: args.collection,
111
- where: args.where
112
- }),
113
- raw: (sql) => ({
114
- kind: "raw",
115
- sql
116
- }),
117
- field: (name) => ({
118
- kind: "field",
119
- name
120
- }),
121
- outerField: (name) => ({
122
- kind: "outerField",
123
- name
124
- }),
125
- literal: (value) => ({
126
- kind: "literal",
127
- value
128
- }),
129
- authUid: () => ({ kind: "authUid" }),
130
- authRoles: () => ({ kind: "authRoles" })
131
- };
132
- //#endregion
133
59
  //#region ../common/src/util/common.ts
134
60
  var DEFAULT_ONE_OF_TYPE = "type";
135
61
  var DEFAULT_ONE_OF_VALUE = "value";
@@ -1141,16 +1067,12 @@ function removeFunctions(o) {
1141
1067
  if (o === void 0) return void 0;
1142
1068
  if (o === null) return null;
1143
1069
  if (typeof o === "object") {
1144
- if (Array.isArray(o)) return o.map((v) => removeFunctions(v));
1070
+ if (Array.isArray(o)) return o.filter((v) => typeof v !== "function").map((v) => removeFunctions(v));
1145
1071
  if (!isPlainObject(o)) return o;
1146
- return Object.entries(o).filter(([_, value]) => typeof value !== "function").map(([key, value]) => {
1147
- if (Array.isArray(value)) return { [key]: value.map((v) => removeFunctions(v)) };
1148
- else if (typeof value === "object") return { [key]: removeFunctions(value) };
1149
- else return { [key]: value };
1150
- }).reduce((a, b) => ({
1151
- ...a,
1152
- ...b
1153
- }), {});
1072
+ return Object.entries(o).filter(([_, value]) => typeof value !== "function").reduce((acc, [key, value]) => {
1073
+ acc[key] = removeFunctions(value);
1074
+ return acc;
1075
+ }, {});
1154
1076
  }
1155
1077
  return o;
1156
1078
  }
@@ -1289,11 +1211,101 @@ function getPolicyNamesForRule(rule, tableName) {
1289
1211
  return ops.map((op, opIdx) => rule.name ? ops.length > 1 ? `${rule.name}_${op}` : rule.name : `${tableName}_${op}_${ruleHash}${ops.length > 1 ? `_${opIdx}` : ""}`);
1290
1212
  }
1291
1213
  //#endregion
1214
+ //#region ../utils/src/plurals.ts
1215
+ /**
1216
+ * Returns the singular of an English word.
1217
+ *
1218
+ * @param {string} word
1219
+ * @param {number} [amount]
1220
+ * @returns {string}
1221
+ */
1222
+ function singular(word, amount) {
1223
+ if (amount !== void 0 && amount !== 1) return word;
1224
+ const singulars = {
1225
+ "(quiz)zes$": "$1",
1226
+ "(matr)ices$": "$1ix",
1227
+ "(vert|ind)ices$": "$1ex",
1228
+ "^(ox)en$": "$1",
1229
+ "(alias)es$": "$1",
1230
+ "(octop|vir)i$": "$1us",
1231
+ "(cris|ax|test)es$": "$1is",
1232
+ "(shoe)s$": "$1",
1233
+ "(o)es$": "$1",
1234
+ "(bus)es$": "$1",
1235
+ "([m|l])ice$": "$1ouse",
1236
+ "(x|ch|ss|sh)es$": "$1",
1237
+ "(m)ovies$": "$1ovie",
1238
+ "(s)eries$": "$1eries",
1239
+ "([^aeiouy]|qu)ies$": "$1y",
1240
+ "([lr])ves$": "$1f",
1241
+ "(tive)s$": "$1",
1242
+ "(hive)s$": "$1",
1243
+ "(li|wi|kni)ves$": "$1fe",
1244
+ "(shea|loa|lea|thie)ves$": "$1f",
1245
+ "(^analy)ses$": "$1sis",
1246
+ "((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$": "$1$2sis",
1247
+ "([ti])a$": "$1um",
1248
+ "(n)ews$": "$1ews",
1249
+ "(h|bl)ouses$": "$1ouse",
1250
+ "(corpse)s$": "$1",
1251
+ "(us)es$": "$1",
1252
+ s$: ""
1253
+ };
1254
+ const irregular = {
1255
+ move: "moves",
1256
+ foot: "feet",
1257
+ goose: "geese",
1258
+ sex: "sexes",
1259
+ child: "children",
1260
+ man: "men",
1261
+ tooth: "teeth",
1262
+ person: "people"
1263
+ };
1264
+ if ([
1265
+ "sheep",
1266
+ "fish",
1267
+ "deer",
1268
+ "moose",
1269
+ "series",
1270
+ "species",
1271
+ "money",
1272
+ "rice",
1273
+ "information",
1274
+ "equipment",
1275
+ "bison",
1276
+ "cod",
1277
+ "offspring",
1278
+ "pike",
1279
+ "salmon",
1280
+ "shrimp",
1281
+ "swine",
1282
+ "trout",
1283
+ "aircraft",
1284
+ "hovercraft",
1285
+ "spacecraft",
1286
+ "sugar",
1287
+ "tuna",
1288
+ "you",
1289
+ "wood"
1290
+ ].indexOf(word.toLowerCase()) >= 0) return word;
1291
+ for (const w in irregular) {
1292
+ const pattern = new RegExp(`${irregular[w]}$`, "i");
1293
+ if (pattern.test(word)) return word.replace(pattern, w);
1294
+ }
1295
+ for (const reg in singulars) {
1296
+ const pattern = new RegExp(reg, "i");
1297
+ if (pattern.test(word)) return word.replace(pattern, singulars[reg]);
1298
+ }
1299
+ return word;
1300
+ }
1301
+ //#endregion
1292
1302
  //#region ../utils/src/names.ts
1293
1303
  /**
1294
1304
  * Generates a foreign key column name from a given string, typically a collection slug or name.
1295
- * It converts the name to snake_case, attempts to singularize it by removing a trailing 's'
1296
- * (a common convention for collection names), and appends '_id'.
1305
+ * It singularizes the name, converts it to snake_case and appends '_id'.
1306
+ *
1307
+ * Singularization runs *before* snake-casing so that acronyms survive: `toSnakeCase`
1308
+ * splits on every capital, which turned "URLs" into "ur_ls" and then "ur_l_id".
1297
1309
  *
1298
1310
  * @param name The base name to convert to a foreign key.
1299
1311
  * @returns A foreign key name in the format 'singular_name_id'.
@@ -1303,8 +1315,8 @@ function getPolicyNamesForRule(rule, tableName) {
1303
1315
  * generateForeignKeyName("users")
1304
1316
  *
1305
1317
  * @example
1306
- * // returns "post_id"
1307
- * generateForeignKeyName("posts")
1318
+ * // returns "category_id"
1319
+ * generateForeignKeyName("categories")
1308
1320
  *
1309
1321
  * @example
1310
1322
  * // returns "product_id"
@@ -1312,8 +1324,41 @@ function getPolicyNamesForRule(rule, tableName) {
1312
1324
  *
1313
1325
  */
1314
1326
  function generateForeignKeyName(name) {
1315
- const snakeCaseName = toSnakeCase(name);
1316
- return `${snakeCaseName.endsWith("s") ? snakeCaseName.slice(0, -1) : snakeCaseName}_id`;
1327
+ return `${toSnakeCase(singularizeForKey(name))}_id`;
1328
+ }
1329
+ /**
1330
+ * `singular()` handles real English plurals, but its final catch-all rule strips
1331
+ * any trailing "s", which mangles words that only look plural. Guard the two
1332
+ * cases that produce a column name nobody would recognise:
1333
+ *
1334
+ * - a double "s" ending is never a plural marker ("address", "class", "process"),
1335
+ * so stripping it yields "addres";
1336
+ * - a name that singularizes to nothing (the literal "s") would yield "_id".
1337
+ */
1338
+ function singularizeForKey(name) {
1339
+ if (/ss$/i.test(name)) return name;
1340
+ const result = singular(name);
1341
+ return result.length > 0 ? result : name;
1342
+ }
1343
+ /**
1344
+ * What `generateForeignKeyName` returned before it learned to singularize:
1345
+ * snake-case the name, then chop one trailing "s".
1346
+ *
1347
+ * This is here to be *detected*, never to be generated. A database provisioned
1348
+ * under the old rule carries `categorie_id`, `addresse_id`, `children_id` or
1349
+ * `ur_l_id` where the current rule expects `category_id`, `address_id`,
1350
+ * `child_id` and `url_id` — and the boot-time schema ensure is additive, so it
1351
+ * would create the new column empty beside the populated old one and leave the
1352
+ * relation reading nothing. No error, no missing table: the failure is silent,
1353
+ * which is the only reason this function still exists.
1354
+ *
1355
+ * `ensureCollectionSchema` calls it to recognise that shape and say so.
1356
+ * Returns the same string as `generateForeignKeyName` for every regular plural,
1357
+ * so a caller can compare the two and act only when they differ.
1358
+ */
1359
+ function legacyForeignKeyName(name) {
1360
+ const snake = toSnakeCase(name);
1361
+ return `${snake.endsWith("s") ? snake.slice(0, -1) : snake}_id`;
1317
1362
  }
1318
1363
  //#endregion
1319
1364
  //#region ../common/src/util/entities.ts
@@ -1553,8 +1598,7 @@ function enumToObjectEntries(enumValues) {
1553
1598
  function resolveRelation(relation, sourceCollection, propertyKey) {
1554
1599
  const target = relation.target;
1555
1600
  if (typeof target !== "function") throw new Error(`Relation${relation.relationName ? ` '${relation.relationName}'` : ""} on '${sourceCollection.slug}' has no \`target\`. Give it a thunk: \`target: () => otherCollection\`.`);
1556
- const targetCollection = target();
1557
- if (!targetCollection?.slug) throw new Error(`Relation${relation.relationName ? ` '${relation.relationName}'` : ""} on '${sourceCollection.slug}' has a \`target\` that did not resolve to a collection.`);
1601
+ const targetCollection = callTarget(relation, sourceCollection, propertyKey, target);
1558
1602
  const relationName = relation.relationName ?? propertyKey ?? toSnakeCase(targetCollection.slug);
1559
1603
  const shared = {
1560
1604
  relationName,
@@ -1581,7 +1625,8 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
1581
1625
  cardinality: "one",
1582
1626
  writable: true,
1583
1627
  shared: false,
1584
- foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName)
1628
+ foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),
1629
+ sourceKey: relation.sourceKey
1585
1630
  };
1586
1631
  case "hasMany": return {
1587
1632
  ...shared,
@@ -1589,7 +1634,8 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
1589
1634
  cardinality: "many",
1590
1635
  writable: true,
1591
1636
  shared: false,
1592
- foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName)
1637
+ foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),
1638
+ sourceKey: relation.sourceKey
1593
1639
  };
1594
1640
  case "manyToMany": {
1595
1641
  const sourceTable = getTableName(sourceCollection);
@@ -1618,6 +1664,43 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
1618
1664
  default: throw new Error(`Unknown relation kind: ${JSON.stringify(relation)}`);
1619
1665
  }
1620
1666
  }
1667
+ /** How this relation is addressed in an error message, before it has a resolved name. */
1668
+ function describe(relation, sourceCollection, propertyKey) {
1669
+ const name = relation.relationName ?? propertyKey;
1670
+ return `Relation${name ? ` '${name}'` : ""} on '${sourceCollection.slug}'`;
1671
+ }
1672
+ /**
1673
+ * Call the `target` thunk, and translate the two ways an import cycle breaks it
1674
+ * into an error that names the cause.
1675
+ *
1676
+ * The thunk exists to defer the reference until every module has finished
1677
+ * evaluating, and for a cycle that closes at import time it does. What it cannot
1678
+ * defer is a cycle that leaves the binding permanently unusable, and there are
1679
+ * two shapes of that:
1680
+ *
1681
+ * - **ESM/TDZ.** `const` and `class` bindings in a not-yet-evaluated module are
1682
+ * in the temporal dead zone, so reading one throws `ReferenceError: x is not
1683
+ * defined`. The stack points at the thunk — a one-line arrow function that is
1684
+ * obviously fine — and says nothing about the cycle that made it throw.
1685
+ * - **CJS interop.** The half-initialised module object has no `default` yet,
1686
+ * the import resolves to `undefined`, and the thunk returns it without
1687
+ * complaint. That one used to surface here as "did not resolve to a
1688
+ * collection", which is true and unhelpful.
1689
+ *
1690
+ * Both mean the same thing, and the fix for both is the same: break the cycle,
1691
+ * or move the relation into the collection that does not close it.
1692
+ */
1693
+ function callTarget(relation, sourceCollection, propertyKey, target) {
1694
+ let targetCollection;
1695
+ try {
1696
+ targetCollection = target();
1697
+ } catch (error) {
1698
+ if (error instanceof ReferenceError) throw new Error(`${describe(relation, sourceCollection, propertyKey)} targets a collection that is not initialized yet — almost always an import cycle between the two collection files. Break the cycle (move the shared piece into a third module, or import the target lazily) so the target's module finishes evaluating before the registry is built.`, { cause: error });
1699
+ throw error;
1700
+ }
1701
+ if (!targetCollection?.slug) throw new Error(`${describe(relation, sourceCollection, propertyKey)} has a \`target\` that resolved to ${targetCollection === void 0 ? "`undefined`" : "something that is not a collection"}. ` + (targetCollection === void 0 ? "Under CommonJS interop an import cycle resolves the default import to `undefined`, so check whether this collection and its target import each other. Otherwise the thunk is returning the wrong value — it must return the collection itself, not a promise or a module." : "The thunk must return a collection config with a `slug`."));
1702
+ return targetCollection;
1703
+ }
1621
1704
  //#endregion
1622
1705
  //#region ../common/src/util/relations.ts
1623
1706
  /**
@@ -1938,7 +2021,12 @@ var UID_NOT_NULL = /auth\.uid\(\)\s+IS\s+NOT\s+NULL/i;
1938
2021
  * is how the trusted *server* context is recognised), so:
1939
2022
  *
1940
2023
  * - `auth.uid() IS NOT NULL` is a tautology on the user path, and
1941
- * - `auth.uid() != 'anon'` compares against a string no caller ever has.
2024
+ * - `auth.uid() != 'anon'` excludes one spelling of anonymous and admits the
2025
+ * other. This one is not hypothetical and was not only a foreign habit:
2026
+ * rebase's own request path reported `'anon'` while everything that compiled
2027
+ * or checked a policy used `'anonymous'`, so whichever literal an author
2028
+ * picked, half the anonymous callers walked through. See
2029
+ * {@link ANONYMOUS_USER_IDS}.
1942
2030
  *
1943
2031
  * Either one turns a lockdown into a full grant, and neither looks wrong. No
1944
2032
  * real user id is ever one of these literals, and a user-context request is
@@ -1976,7 +2064,7 @@ function findAnonymousGrants(expr) {
1976
2064
  found.push({
1977
2065
  pattern: "foreign-uid-literal",
1978
2066
  detail: literal.value,
1979
- explanation: `'${literal.value}' is a ${platform} convention. Rebase reports an anonymous request as '${ANONYMOUS_USER_ID}', so comparing against '${literal.value}' passes for every caller. Use \`condition: policy.authenticated()\` to mean "signed in".`
2067
+ explanation: `'${literal.value}' is a ${platform} convention. Rebase reports an anonymous request as '${ANONYMOUS_USER_ID}', so comparing against '${literal.value}' passes for every caller. Use \`condition: policy.authenticated()\` to mean "signed in" — it compiles to NOT IN (${ANONYMOUS_USER_IDS.map((v) => `'${v}'`).join(", ")}), covering every spelling rebase has reported rather than whichever one you remember.`
1980
2068
  });
1981
2069
  return;
1982
2070
  }
@@ -2071,7 +2159,7 @@ function compile(expr, scope) {
2071
2159
  }
2072
2160
  case "rolesOverlap": return `string_to_array(auth.roles(), ',') && ${rolesArraySql(expr.roles)}`;
2073
2161
  case "rolesContain": return `string_to_array(auth.roles(), ',') @> ${rolesArraySql(expr.roles)}`;
2074
- case "authenticated": return `auth.uid() IS NOT NULL AND auth.uid() <> ${quoteLiteral(ANONYMOUS_USER_ID)}`;
2162
+ case "authenticated": return `auth.uid() IS NOT NULL AND auth.uid() NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(", ")})`;
2075
2163
  case "serverContext": return "auth.uid() IS NULL";
2076
2164
  case "existsIn": return compileExistsIn(expr, scope);
2077
2165
  case "raw": return expr.sql.replace(/\{(\w+)\}/g, (_, col) => `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);
@@ -3800,7 +3888,7 @@ function rowToEntity(row, slug, primaryKeys = []) {
3800
3888
  };
3801
3889
  }
3802
3890
  /**
3803
- * The relation envelope `toCmsRow` writes where a relation was:
3891
+ * The relation envelope `toFlatRow` writes where a relation was:
3804
3892
  * `{ id, path, __type: "relation", data: { id, path, values } }`. It is the
3805
3893
  * admin's view-model, and the only pipeline that produces one is postgres'.
3806
3894
  */
@@ -4178,6 +4266,6 @@ function buildSdkData(driver) {
4178
4266
  return wrapAsSdkData(buildRebaseData(driver));
4179
4267
  }
4180
4268
  //#endregion
4181
- export { DEFAULT_ONE_OF_VALUE as A, updateDateAutoValues as C, camelCase as D, mergeDeep as E, hasForeignKeyOnTarget as M, isManyToMany as N, toSnakeCase as O, Vector as P, normalizeToEntityRelation as S, getPolicyNamesForRule as T, buildCompositeId as _, getJunctionSecurityRules as a, createRelationRef as b, policyToPostgres as c, findRelation as d, getColumnName as f, resolveCollectionRelations as g, getTableVarName as h, getJunctionCollectionConfig as i, ANONYMOUS_USER_ID as j, DEFAULT_ONE_OF_TYPE as k, securityRuleToConditions as l, getTableName as m, CollectionRegistry as n, resolveJunctionSpecs as o, getEnumVarName as p, resolveStringColumnLength as r, getEffectiveSecurityRules as s, buildSdkData as t, findAnonymousGrants as u, getDeclaredPrimaryKeys as v, generateForeignKeyName as w, createRelationRefWithData as x, parseIdValues as y };
4269
+ export { DEFAULT_ONE_OF_TYPE as A, updateDateAutoValues as C, mergeDeep as D, getPolicyNamesForRule as E, hasForeignKeyOnTarget as M, isManyToMany as N, camelCase as O, Vector as P, normalizeToEntityRelation as S, legacyForeignKeyName as T, buildCompositeId as _, getJunctionSecurityRules as a, createRelationRef as b, policyToPostgres as c, findRelation as d, getColumnName as f, resolveCollectionRelations as g, getTableVarName as h, getJunctionCollectionConfig as i, DEFAULT_ONE_OF_VALUE as j, toSnakeCase as k, securityRuleToConditions as l, getTableName as m, CollectionRegistry as n, resolveJunctionSpecs as o, getEnumVarName as p, resolveStringColumnLength as r, getEffectiveSecurityRules as s, buildSdkData as t, findAnonymousGrants as u, getDeclaredPrimaryKeys as v, generateForeignKeyName as w, createRelationRefWithData as x, parseIdValues as y };
4182
4270
 
4183
- //# sourceMappingURL=src-DihrDFuP.js.map
4271
+ //# sourceMappingURL=src-CzbghKwf.js.map