@rebasepro/server-postgres 0.12.1-canary.gcfa333c → 0.12.1-canary.gddbd489
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.
- package/dist/PostgresBackendDriver.d.ts +1 -1
- package/dist/PostgresBootstrapper.d.ts +25 -1
- package/dist/auth/services.d.ts +6 -1
- package/dist/backup/backup-service.d.ts +10 -1
- package/dist/backup/pg-tools.d.ts +47 -0
- package/dist/{backup-service-DH9kPg-E.js → backup-service-CD8o_1Sl.js} +144 -11
- package/dist/backup-service-CD8o_1Sl.js.map +1 -0
- package/dist/cli-helpers.d.ts +39 -0
- package/dist/{connection-B5Wndbr1.js → connection-BuZ97wsr.js} +57 -3
- package/dist/connection-BuZ97wsr.js.map +1 -0
- package/dist/connection.d.ts +42 -0
- package/dist/{ensure-collection-policies-Vl3Cv1Q9.js → ensure-collection-policies-ViG8XiPn.js} +2 -2
- package/dist/{ensure-collection-policies-Vl3Cv1Q9.js.map → ensure-collection-policies-ViG8XiPn.js.map} +1 -1
- package/dist/{ensure-collection-tables-DsDsNl6o.js → ensure-collection-tables-CBQdOETu.js} +72 -12
- package/dist/ensure-collection-tables-CBQdOETu.js.map +1 -0
- package/dist/index.es.js +990 -677
- package/dist/index.es.js.map +1 -1
- package/dist/policy-CeA1JcxP.js +105 -0
- package/dist/policy-CeA1JcxP.js.map +1 -0
- package/dist/schema/ensure-collection-tables.d.ts +21 -1
- package/dist/schema/generate-postgres-ddl-logic.d.ts +19 -0
- package/dist/schema/introspect-db-constraints.d.ts +57 -0
- package/dist/schema/introspect-db-logic.d.ts +94 -5
- package/dist/schema/introspect-db-queries.d.ts +119 -0
- package/dist/schema/introspect-db-structure.d.ts +263 -0
- package/dist/schema/introspect-db-types.d.ts +11 -0
- package/dist/services/FetchService.d.ts +4 -1
- package/dist/services/RelationService.d.ts +24 -1
- package/dist/services/channel-bus/index.d.ts +1 -7
- package/dist/services/collection-helpers.d.ts +36 -2
- package/dist/services/dataService.d.ts +3 -1
- package/dist/services/row-pipeline.d.ts +1 -1
- package/dist/{src-DihrDFuP.js → src-DlPBctw_.js} +217 -101
- package/dist/src-DlPBctw_.js.map +1 -0
- package/dist/utils/connection-string.d.ts +29 -0
- package/dist/utils/drizzle-conditions.d.ts +5 -4
- package/dist/utils/pg-error-utils.d.ts +16 -0
- package/dist/{websocket-BKcGvILX.js → websocket-B2LsrINK.js} +8 -6
- package/dist/websocket-B2LsrINK.js.map +1 -0
- package/package.json +6 -6
- package/src/PostgresAdapter.ts +7 -2
- package/src/PostgresBackendDriver.ts +4 -0
- package/src/PostgresBootstrapper.ts +83 -22
- package/src/auth/services.ts +6 -3
- package/src/backup/backup-cli.ts +41 -2
- package/src/backup/backup-service.ts +38 -5
- package/src/backup/pg-tools.ts +96 -3
- package/src/cli-helpers.ts +70 -0
- package/src/cli.ts +44 -26
- package/src/collections/validate-relations.ts +15 -0
- package/src/connection.ts +73 -0
- package/src/data-transformer.ts +9 -3
- package/src/databasePoolManager.ts +5 -2
- package/src/schema/ensure-collection-tables.ts +81 -10
- package/src/schema/generate-drizzle-schema-logic.ts +35 -3
- package/src/schema/generate-postgres-ddl-logic.ts +50 -6
- package/src/schema/introspect-db-constraints.ts +385 -0
- package/src/schema/introspect-db-inference.ts +18 -8
- package/src/schema/introspect-db-logic.ts +385 -71
- package/src/schema/introspect-db-queries.ts +326 -0
- package/src/schema/introspect-db-structure.ts +670 -0
- package/src/schema/introspect-db-types.ts +56 -0
- package/src/schema/introspect-db.ts +37 -80
- package/src/security/policy-drift.test.ts +11 -3
- package/src/services/FetchService.ts +90 -14
- package/src/services/PersistService.ts +20 -6
- package/src/services/RelationService.ts +211 -45
- package/src/services/channel-bus/index.ts +0 -9
- package/src/services/collection-helpers.ts +69 -3
- package/src/services/dataService.ts +3 -1
- package/src/services/realtimeService.ts +3 -3
- package/src/services/row-pipeline.ts +1 -1
- package/src/utils/connection-string.ts +58 -0
- package/src/utils/drizzle-conditions.ts +31 -6
- package/src/utils/pg-error-utils.ts +19 -0
- package/src/websocket.ts +18 -9
- package/dist/backup-service-DH9kPg-E.js.map +0 -1
- package/dist/connection-B5Wndbr1.js.map +0 -1
- package/dist/ensure-collection-tables-DsDsNl6o.js.map +0 -1
- package/dist/src-DihrDFuP.js.map +0 -1
- 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 {
|
|
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").
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
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
|
|
1296
|
-
*
|
|
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 "
|
|
1307
|
-
* generateForeignKeyName("
|
|
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
|
-
|
|
1316
|
-
|
|
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
|
|
@@ -1413,6 +1458,34 @@ function createRelationRefWithData(id, path, data) {
|
|
|
1413
1458
|
data
|
|
1414
1459
|
};
|
|
1415
1460
|
}
|
|
1461
|
+
/** The eight-four-four-four-twelve shape of a UUID, any version. */
|
|
1462
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1463
|
+
/** Whether one address part can be a value of the column it addresses. */
|
|
1464
|
+
function partIsAddressable(part, pk) {
|
|
1465
|
+
if (pk.isUUID) return UUID_PATTERN.test(String(part));
|
|
1466
|
+
if (pk.type === "number") return typeof part === "number" ? Number.isFinite(part) : !isNaN(parseInt(String(part), 10));
|
|
1467
|
+
return true;
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Whether an address could name a row at all, before asking the database.
|
|
1471
|
+
*
|
|
1472
|
+
* A `uuid` column cannot hold `"new"`, and an `integer` column cannot hold
|
|
1473
|
+
* `"abc"` — so the answer to "which row is this" is "none", and that is a 404,
|
|
1474
|
+
* not a failure. Postgres cannot say so politely: the comparison never runs, it
|
|
1475
|
+
* raises `22P02` and aborts the enclosing transaction, after which every
|
|
1476
|
+
* further statement returns the far less helpful `25P02`.
|
|
1477
|
+
*
|
|
1478
|
+
* `isUUID` must come from the column, not from `isId: "uuid"` in a config: the
|
|
1479
|
+
* config is a claim about a key, and a `text` column that holds ids of some
|
|
1480
|
+
* other shape is a working app this must not start rejecting.
|
|
1481
|
+
*/
|
|
1482
|
+
function isAddressableId(idValue, primaryKeys) {
|
|
1483
|
+
if (primaryKeys.length === 0) return false;
|
|
1484
|
+
if (primaryKeys.length === 1) return partIsAddressable(idValue, primaryKeys[0]);
|
|
1485
|
+
const parts = String(idValue).split(":::");
|
|
1486
|
+
if (parts.length !== primaryKeys.length) return false;
|
|
1487
|
+
return parts.every((part, i) => partIsAddressable(part, primaryKeys[i]));
|
|
1488
|
+
}
|
|
1416
1489
|
/**
|
|
1417
1490
|
* Derive a row's address from its key columns.
|
|
1418
1491
|
*
|
|
@@ -1553,8 +1626,7 @@ function enumToObjectEntries(enumValues) {
|
|
|
1553
1626
|
function resolveRelation(relation, sourceCollection, propertyKey) {
|
|
1554
1627
|
const target = relation.target;
|
|
1555
1628
|
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.`);
|
|
1629
|
+
const targetCollection = callTarget(relation, sourceCollection, propertyKey, target);
|
|
1558
1630
|
const relationName = relation.relationName ?? propertyKey ?? toSnakeCase(targetCollection.slug);
|
|
1559
1631
|
const shared = {
|
|
1560
1632
|
relationName,
|
|
@@ -1581,7 +1653,8 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
|
|
|
1581
1653
|
cardinality: "one",
|
|
1582
1654
|
writable: true,
|
|
1583
1655
|
shared: false,
|
|
1584
|
-
foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName)
|
|
1656
|
+
foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),
|
|
1657
|
+
sourceKey: relation.sourceKey
|
|
1585
1658
|
};
|
|
1586
1659
|
case "hasMany": return {
|
|
1587
1660
|
...shared,
|
|
@@ -1589,7 +1662,8 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
|
|
|
1589
1662
|
cardinality: "many",
|
|
1590
1663
|
writable: true,
|
|
1591
1664
|
shared: false,
|
|
1592
|
-
foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName)
|
|
1665
|
+
foreignKeyOnTarget: relation.foreignKeyOnTarget ?? generateForeignKeyName(sourceName),
|
|
1666
|
+
sourceKey: relation.sourceKey
|
|
1593
1667
|
};
|
|
1594
1668
|
case "manyToMany": {
|
|
1595
1669
|
const sourceTable = getTableName(sourceCollection);
|
|
@@ -1618,6 +1692,43 @@ function resolveRelation(relation, sourceCollection, propertyKey) {
|
|
|
1618
1692
|
default: throw new Error(`Unknown relation kind: ${JSON.stringify(relation)}`);
|
|
1619
1693
|
}
|
|
1620
1694
|
}
|
|
1695
|
+
/** How this relation is addressed in an error message, before it has a resolved name. */
|
|
1696
|
+
function describe(relation, sourceCollection, propertyKey) {
|
|
1697
|
+
const name = relation.relationName ?? propertyKey;
|
|
1698
|
+
return `Relation${name ? ` '${name}'` : ""} on '${sourceCollection.slug}'`;
|
|
1699
|
+
}
|
|
1700
|
+
/**
|
|
1701
|
+
* Call the `target` thunk, and translate the two ways an import cycle breaks it
|
|
1702
|
+
* into an error that names the cause.
|
|
1703
|
+
*
|
|
1704
|
+
* The thunk exists to defer the reference until every module has finished
|
|
1705
|
+
* evaluating, and for a cycle that closes at import time it does. What it cannot
|
|
1706
|
+
* defer is a cycle that leaves the binding permanently unusable, and there are
|
|
1707
|
+
* two shapes of that:
|
|
1708
|
+
*
|
|
1709
|
+
* - **ESM/TDZ.** `const` and `class` bindings in a not-yet-evaluated module are
|
|
1710
|
+
* in the temporal dead zone, so reading one throws `ReferenceError: x is not
|
|
1711
|
+
* defined`. The stack points at the thunk — a one-line arrow function that is
|
|
1712
|
+
* obviously fine — and says nothing about the cycle that made it throw.
|
|
1713
|
+
* - **CJS interop.** The half-initialised module object has no `default` yet,
|
|
1714
|
+
* the import resolves to `undefined`, and the thunk returns it without
|
|
1715
|
+
* complaint. That one used to surface here as "did not resolve to a
|
|
1716
|
+
* collection", which is true and unhelpful.
|
|
1717
|
+
*
|
|
1718
|
+
* Both mean the same thing, and the fix for both is the same: break the cycle,
|
|
1719
|
+
* or move the relation into the collection that does not close it.
|
|
1720
|
+
*/
|
|
1721
|
+
function callTarget(relation, sourceCollection, propertyKey, target) {
|
|
1722
|
+
let targetCollection;
|
|
1723
|
+
try {
|
|
1724
|
+
targetCollection = target();
|
|
1725
|
+
} catch (error) {
|
|
1726
|
+
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 });
|
|
1727
|
+
throw error;
|
|
1728
|
+
}
|
|
1729
|
+
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`."));
|
|
1730
|
+
return targetCollection;
|
|
1731
|
+
}
|
|
1621
1732
|
//#endregion
|
|
1622
1733
|
//#region ../common/src/util/relations.ts
|
|
1623
1734
|
/**
|
|
@@ -1938,7 +2049,12 @@ var UID_NOT_NULL = /auth\.uid\(\)\s+IS\s+NOT\s+NULL/i;
|
|
|
1938
2049
|
* is how the trusted *server* context is recognised), so:
|
|
1939
2050
|
*
|
|
1940
2051
|
* - `auth.uid() IS NOT NULL` is a tautology on the user path, and
|
|
1941
|
-
* - `auth.uid() != 'anon'`
|
|
2052
|
+
* - `auth.uid() != 'anon'` excludes one spelling of anonymous and admits the
|
|
2053
|
+
* other. This one is not hypothetical and was not only a foreign habit:
|
|
2054
|
+
* rebase's own request path reported `'anon'` while everything that compiled
|
|
2055
|
+
* or checked a policy used `'anonymous'`, so whichever literal an author
|
|
2056
|
+
* picked, half the anonymous callers walked through. See
|
|
2057
|
+
* {@link ANONYMOUS_USER_IDS}.
|
|
1942
2058
|
*
|
|
1943
2059
|
* Either one turns a lockdown into a full grant, and neither looks wrong. No
|
|
1944
2060
|
* real user id is ever one of these literals, and a user-context request is
|
|
@@ -1976,7 +2092,7 @@ function findAnonymousGrants(expr) {
|
|
|
1976
2092
|
found.push({
|
|
1977
2093
|
pattern: "foreign-uid-literal",
|
|
1978
2094
|
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".`
|
|
2095
|
+
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
2096
|
});
|
|
1981
2097
|
return;
|
|
1982
2098
|
}
|
|
@@ -2071,7 +2187,7 @@ function compile(expr, scope) {
|
|
|
2071
2187
|
}
|
|
2072
2188
|
case "rolesOverlap": return `string_to_array(auth.roles(), ',') && ${rolesArraySql(expr.roles)}`;
|
|
2073
2189
|
case "rolesContain": return `string_to_array(auth.roles(), ',') @> ${rolesArraySql(expr.roles)}`;
|
|
2074
|
-
case "authenticated": return `auth.uid() IS NOT NULL AND auth.uid()
|
|
2190
|
+
case "authenticated": return `auth.uid() IS NOT NULL AND auth.uid() NOT IN (${ANONYMOUS_USER_IDS.map(quoteLiteral).join(", ")})`;
|
|
2075
2191
|
case "serverContext": return "auth.uid() IS NULL";
|
|
2076
2192
|
case "existsIn": return compileExistsIn(expr, scope);
|
|
2077
2193
|
case "raw": return expr.sql.replace(/\{(\w+)\}/g, (_, col) => `${outerQualifier(scope)}${resolveColumnName(col, scope.outerCollection)}`);
|
|
@@ -3800,7 +3916,7 @@ function rowToEntity(row, slug, primaryKeys = []) {
|
|
|
3800
3916
|
};
|
|
3801
3917
|
}
|
|
3802
3918
|
/**
|
|
3803
|
-
* The relation envelope `
|
|
3919
|
+
* The relation envelope `toFlatRow` writes where a relation was:
|
|
3804
3920
|
* `{ id, path, __type: "relation", data: { id, path, values } }`. It is the
|
|
3805
3921
|
* admin's view-model, and the only pipeline that produces one is postgres'.
|
|
3806
3922
|
*/
|
|
@@ -4178,6 +4294,6 @@ function buildSdkData(driver) {
|
|
|
4178
4294
|
return wrapAsSdkData(buildRebaseData(driver));
|
|
4179
4295
|
}
|
|
4180
4296
|
//#endregion
|
|
4181
|
-
export {
|
|
4297
|
+
export { toSnakeCase as A, normalizeToEntityRelation as C, getPolicyNamesForRule as D, legacyForeignKeyName as E, Vector as F, DEFAULT_ONE_OF_VALUE as M, hasForeignKeyOnTarget as N, mergeDeep as O, isManyToMany as P, createRelationRefWithData as S, generateForeignKeyName as T, buildCompositeId as _, getJunctionSecurityRules as a, parseIdValues as b, policyToPostgres as c, findRelation as d, getColumnName as f, resolveCollectionRelations as g, getTableVarName as h, getJunctionCollectionConfig as i, DEFAULT_ONE_OF_TYPE as j, camelCase 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, updateDateAutoValues as w, createRelationRef as x, isAddressableId as y };
|
|
4182
4298
|
|
|
4183
|
-
//# sourceMappingURL=src-
|
|
4299
|
+
//# sourceMappingURL=src-DlPBctw_.js.map
|