@rebasepro/server-postgres 0.12.1-canary.g389e9b2 → 0.12.1-canary.g4e7bcbf
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/{backup-service-vAKWJkYL.js → backup-service-DLb2drIH.js} +7 -1
- package/dist/backup-service-DLb2drIH.js.map +1 -0
- package/dist/cli-helpers.d.ts +15 -0
- package/dist/{ensure-collection-policies-BjSwj0FM.js → ensure-collection-policies-Dv21KDMJ.js} +2 -2
- package/dist/{ensure-collection-policies-BjSwj0FM.js.map → ensure-collection-policies-Dv21KDMJ.js.map} +1 -1
- package/dist/{ensure-collection-tables-D6-XhqnQ.js → ensure-collection-tables-CT6xHB1d.js} +72 -12
- package/dist/ensure-collection-tables-CT6xHB1d.js.map +1 -0
- package/dist/index.es.js +74 -21
- package/dist/index.es.js.map +1 -1
- package/dist/schema/ensure-collection-tables.d.ts +21 -1
- package/dist/schema/generate-postgres-ddl-logic.d.ts +19 -0
- package/dist/services/FetchService.d.ts +4 -1
- package/dist/services/dataService.d.ts +3 -1
- package/dist/services/row-pipeline.d.ts +1 -1
- package/dist/{src-C_NHNVW2.js → src-BkdpiQdw.js} +137 -18
- package/dist/src-BkdpiQdw.js.map +1 -0
- package/dist/{websocket-DB7TbFPT.js → websocket-B2LsrINK.js} +4 -3
- 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/cli-helpers.ts +26 -0
- package/src/cli.ts +2 -19
- package/src/schema/ensure-collection-tables.ts +81 -10
- package/src/schema/generate-postgres-ddl-logic.ts +50 -6
- package/src/schema/introspect-db-logic.ts +21 -3
- package/src/security/policy-drift.test.ts +11 -3
- package/src/services/FetchService.ts +76 -14
- package/src/services/dataService.ts +3 -1
- package/src/services/row-pipeline.ts +1 -1
- package/src/websocket.ts +15 -6
- package/dist/backup-service-vAKWJkYL.js.map +0 -1
- package/dist/ensure-collection-tables-D6-XhqnQ.js.map +0 -1
- package/dist/src-C_NHNVW2.js.map +0 -1
- package/dist/websocket-DB7TbFPT.js.map +0 -1
|
@@ -56,7 +56,7 @@ export interface ExistingSchema {
|
|
|
56
56
|
constraints?: Set<string>;
|
|
57
57
|
}
|
|
58
58
|
export interface EnsureAction {
|
|
59
|
-
kind: "create-enum" | "create-table" | "add-column" | "add-constraint";
|
|
59
|
+
kind: "create-enum" | "create-table" | "add-column" | "add-constraint" | "rename-column";
|
|
60
60
|
/** Qualified target, for logging: `public.posts` or `public.posts.title`. */
|
|
61
61
|
target: string;
|
|
62
62
|
sql: string;
|
|
@@ -65,6 +65,26 @@ export interface EnsurePlan {
|
|
|
65
65
|
actions: EnsureAction[];
|
|
66
66
|
/** Every statement, in dependency order. Empty when the schema is current. */
|
|
67
67
|
statements: string[];
|
|
68
|
+
/**
|
|
69
|
+
* Relation columns this plan is about to create where the table already
|
|
70
|
+
* carries the same column under its pre-singularization name.
|
|
71
|
+
*
|
|
72
|
+
* The reason this is reported rather than silently handled: the ensure is
|
|
73
|
+
* additive, so it would add `category_id` beside a populated
|
|
74
|
+
* `categorie_id` and the relation would then read the new, empty one. No
|
|
75
|
+
* statement fails, no table is missing, and the only symptom is relations
|
|
76
|
+
* resolving to nothing — which is indistinguishable from having no data.
|
|
77
|
+
*/
|
|
78
|
+
legacyForeignKeys: LegacyForeignKey[];
|
|
79
|
+
}
|
|
80
|
+
/** A relation column whose old and new spellings both plausibly apply. */
|
|
81
|
+
export interface LegacyForeignKey {
|
|
82
|
+
/** `schema.table`. */
|
|
83
|
+
table: string;
|
|
84
|
+
/** The name the current rule derives, and what this plan would create. */
|
|
85
|
+
expected: string;
|
|
86
|
+
/** The name the old rule derived, which the table already has. */
|
|
87
|
+
legacy: string;
|
|
68
88
|
}
|
|
69
89
|
export interface EnsureOutcome extends EnsurePlan {
|
|
70
90
|
/**
|
|
@@ -19,6 +19,15 @@ type ResolveCollection = (slug: string) => CollectionConfig | undefined;
|
|
|
19
19
|
* multiple commands in one execute), while `db push` writes the joined string.
|
|
20
20
|
*/
|
|
21
21
|
export declare const generatePolicyStatements: (collection: CollectionConfig, rule: SecurityRule, resolveCollection: ResolveCollection) => string[];
|
|
22
|
+
/**
|
|
23
|
+
* Single-quote escaping for a SQL string literal (PostgreSQL doubles the
|
|
24
|
+
* quote). Enum labels come straight from user-authored collection config, so a
|
|
25
|
+
* label like `it's` closes the literal early and the whole generated file stops
|
|
26
|
+
* parsing at the `CREATE TYPE`. Lives here rather than next to its other caller
|
|
27
|
+
* because ensure-collection-tables already imports from this module — the
|
|
28
|
+
* reverse would be a cycle.
|
|
29
|
+
*/
|
|
30
|
+
export declare const quoteSqlLiteral: (value: string) => string;
|
|
22
31
|
export declare const getSqlColumnType: (propName: string, prop: Property, collection: CollectionConfig, collections: CollectionConfig[]) => string;
|
|
23
32
|
export declare const generatePostgresDdl: (collections: CollectionConfig[], options?: {
|
|
24
33
|
includePolicies?: boolean;
|
|
@@ -52,6 +61,15 @@ export interface RelationalColumnPlan {
|
|
|
52
61
|
type: string;
|
|
53
62
|
/** Absent when the target collection is not part of this bundle. */
|
|
54
63
|
foreignKey?: ForeignKeyPlan;
|
|
64
|
+
/**
|
|
65
|
+
* What this column would have been called before `generateForeignKeyName`
|
|
66
|
+
* learned to singularize — set only when the two differ and the column name
|
|
67
|
+
* is the derived default rather than one the author wrote.
|
|
68
|
+
*
|
|
69
|
+
* Carried so the boot-time ensure can notice a database provisioned under
|
|
70
|
+
* the old rule. It is never used to name anything.
|
|
71
|
+
*/
|
|
72
|
+
legacyColumn?: string;
|
|
55
73
|
}
|
|
56
74
|
/** The table behind a many-to-many `through` relation. */
|
|
57
75
|
export interface JunctionTablePlan {
|
|
@@ -61,6 +79,7 @@ export interface JunctionTablePlan {
|
|
|
61
79
|
columns: {
|
|
62
80
|
name: string;
|
|
63
81
|
type: string;
|
|
82
|
+
legacyName?: string;
|
|
64
83
|
}[];
|
|
65
84
|
/** Both endpoint columns plus the composite primary key. */
|
|
66
85
|
createTable: string;
|
|
@@ -141,7 +141,7 @@ export declare class FetchService {
|
|
|
141
141
|
/**
|
|
142
142
|
* Fallback path used when db.query is unavailable.
|
|
143
143
|
*
|
|
144
|
-
* The primary path runs the results through `
|
|
144
|
+
* The primary path runs the results through `toFlatRow`, which maps
|
|
145
145
|
* relations from what drizzle already nested — no query per row. This one
|
|
146
146
|
* has no nesting to read, so it resolves relations itself, in batches.
|
|
147
147
|
*
|
|
@@ -177,6 +177,7 @@ export declare class FetchService {
|
|
|
177
177
|
*/
|
|
178
178
|
count<M extends Record<string, unknown>>(collectionPath: string, options?: {
|
|
179
179
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
180
|
+
logical?: LogicalCondition;
|
|
180
181
|
searchString?: string;
|
|
181
182
|
databaseId?: string;
|
|
182
183
|
}): Promise<number>;
|
|
@@ -198,6 +199,8 @@ export declare class FetchService {
|
|
|
198
199
|
*/
|
|
199
200
|
fetchCollectionForRest<M extends Record<string, unknown>>(collectionPath: string, options?: {
|
|
200
201
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
202
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
203
|
+
logical?: LogicalCondition;
|
|
201
204
|
orderBy?: string;
|
|
202
205
|
order?: "desc" | "asc";
|
|
203
206
|
limit?: number;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FilterValues } from "@rebasepro/types";
|
|
1
|
+
import { FilterValues, LogicalCondition } from "@rebasepro/types";
|
|
2
2
|
import type { VectorSearchParams } from "@rebasepro/types";
|
|
3
3
|
import { FetchService } from "./FetchService";
|
|
4
4
|
import { PersistService } from "./PersistService";
|
|
@@ -60,6 +60,8 @@ export declare class DataService implements DataRepository {
|
|
|
60
60
|
*/
|
|
61
61
|
count<M extends Record<string, unknown>>(collectionPath: string, options?: {
|
|
62
62
|
filter?: FilterValues<Extract<keyof M, string>>;
|
|
63
|
+
/** An `or(...)`/`and(...)` group, applied alongside `filter`. */
|
|
64
|
+
logical?: LogicalCondition;
|
|
63
65
|
searchString?: string;
|
|
64
66
|
databaseId?: string;
|
|
65
67
|
}): Promise<number>;
|
|
@@ -47,7 +47,7 @@ export declare function relationTargetAddress(targetRow: Record<string, unknown>
|
|
|
47
47
|
* expects real types. The row's own address is *not* among the columns — it is
|
|
48
48
|
* derived by the consumer from the collection's primary keys.
|
|
49
49
|
*/
|
|
50
|
-
export declare function
|
|
50
|
+
export declare function toFlatRow(row: Record<string, unknown>, collection: CollectionConfig, registry: PostgresCollectionRegistry): Record<string, unknown>;
|
|
51
51
|
/**
|
|
52
52
|
* The row REST serves: every column under its own name, with the value Postgres
|
|
53
53
|
* returned, and relations inlined as the target's columns.
|
|
@@ -1067,16 +1067,12 @@ function removeFunctions(o) {
|
|
|
1067
1067
|
if (o === void 0) return void 0;
|
|
1068
1068
|
if (o === null) return null;
|
|
1069
1069
|
if (typeof o === "object") {
|
|
1070
|
-
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));
|
|
1071
1071
|
if (!isPlainObject(o)) return o;
|
|
1072
|
-
return Object.entries(o).filter(([_, value]) => typeof value !== "function").
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
}).reduce((a, b) => ({
|
|
1077
|
-
...a,
|
|
1078
|
-
...b
|
|
1079
|
-
}), {});
|
|
1072
|
+
return Object.entries(o).filter(([_, value]) => typeof value !== "function").reduce((acc, [key, value]) => {
|
|
1073
|
+
acc[key] = removeFunctions(value);
|
|
1074
|
+
return acc;
|
|
1075
|
+
}, {});
|
|
1080
1076
|
}
|
|
1081
1077
|
return o;
|
|
1082
1078
|
}
|
|
@@ -1215,11 +1211,101 @@ function getPolicyNamesForRule(rule, tableName) {
|
|
|
1215
1211
|
return ops.map((op, opIdx) => rule.name ? ops.length > 1 ? `${rule.name}_${op}` : rule.name : `${tableName}_${op}_${ruleHash}${ops.length > 1 ? `_${opIdx}` : ""}`);
|
|
1216
1212
|
}
|
|
1217
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
|
|
1218
1302
|
//#region ../utils/src/names.ts
|
|
1219
1303
|
/**
|
|
1220
1304
|
* Generates a foreign key column name from a given string, typically a collection slug or name.
|
|
1221
|
-
* It
|
|
1222
|
-
*
|
|
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".
|
|
1223
1309
|
*
|
|
1224
1310
|
* @param name The base name to convert to a foreign key.
|
|
1225
1311
|
* @returns A foreign key name in the format 'singular_name_id'.
|
|
@@ -1229,8 +1315,8 @@ function getPolicyNamesForRule(rule, tableName) {
|
|
|
1229
1315
|
* generateForeignKeyName("users")
|
|
1230
1316
|
*
|
|
1231
1317
|
* @example
|
|
1232
|
-
* // returns "
|
|
1233
|
-
* generateForeignKeyName("
|
|
1318
|
+
* // returns "category_id"
|
|
1319
|
+
* generateForeignKeyName("categories")
|
|
1234
1320
|
*
|
|
1235
1321
|
* @example
|
|
1236
1322
|
* // returns "product_id"
|
|
@@ -1238,8 +1324,41 @@ function getPolicyNamesForRule(rule, tableName) {
|
|
|
1238
1324
|
*
|
|
1239
1325
|
*/
|
|
1240
1326
|
function generateForeignKeyName(name) {
|
|
1241
|
-
|
|
1242
|
-
|
|
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`;
|
|
1243
1362
|
}
|
|
1244
1363
|
//#endregion
|
|
1245
1364
|
//#region ../common/src/util/entities.ts
|
|
@@ -3731,7 +3850,7 @@ function rowToEntity(row, slug, primaryKeys = []) {
|
|
|
3731
3850
|
};
|
|
3732
3851
|
}
|
|
3733
3852
|
/**
|
|
3734
|
-
* The relation envelope `
|
|
3853
|
+
* The relation envelope `toFlatRow` writes where a relation was:
|
|
3735
3854
|
* `{ id, path, __type: "relation", data: { id, path, values } }`. It is the
|
|
3736
3855
|
* admin's view-model, and the only pipeline that produces one is postgres'.
|
|
3737
3856
|
*/
|
|
@@ -4109,6 +4228,6 @@ function buildSdkData(driver) {
|
|
|
4109
4228
|
return wrapAsSdkData(buildRebaseData(driver));
|
|
4110
4229
|
}
|
|
4111
4230
|
//#endregion
|
|
4112
|
-
export {
|
|
4231
|
+
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 };
|
|
4113
4232
|
|
|
4114
|
-
//# sourceMappingURL=src-
|
|
4233
|
+
//# sourceMappingURL=src-BkdpiQdw.js.map
|