@rebasepro/server-postgres 0.12.1-canary.g386667d → 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
package/dist/index.es.js
CHANGED
|
@@ -2,11 +2,11 @@ import { createRequire as __createRequire } from "module";
|
|
|
2
2
|
import process from "process";
|
|
3
3
|
__createRequire(import.meta.url);
|
|
4
4
|
import { a as guardPoolAgainstDirtyRelease, i as createReadReplicaConnection, n as createDirectDatabaseConnection, o as pinSearchPath, r as createPostgresDatabaseConnection } from "./connection-BuZ97wsr.js";
|
|
5
|
-
import { C as updateDateAutoValues, D as
|
|
5
|
+
import { C as updateDateAutoValues, D as mergeDeep, E as getPolicyNamesForRule, M as hasForeignKeyOnTarget, N as isManyToMany, O as camelCase, P as Vector, S as normalizeToEntityRelation, _ as buildCompositeId, a as getJunctionSecurityRules, b as createRelationRef, c as policyToPostgres, d as findRelation, f as getColumnName, g as resolveCollectionRelations, h as getTableVarName, i as getJunctionCollectionConfig, k as toSnakeCase, l as securityRuleToConditions, m as getTableName$1, n as CollectionRegistry, o as resolveJunctionSpecs, p as getEnumVarName, r as resolveStringColumnLength, s as getEffectiveSecurityRules, t as buildSdkData, u as findAnonymousGrants, v as getDeclaredPrimaryKeys, w as generateForeignKeyName, x as createRelationRefWithData, y as parseIdValues } from "./src-BkdpiQdw.js";
|
|
6
6
|
import { n as isPostgresCollectionConfig, r as isRelationalCollectionConfig } from "./src-DoU9yPqq.js";
|
|
7
7
|
import { t as ANONYMOUS_USER_ID } from "./policy-CeA1JcxP.js";
|
|
8
|
-
import { t as createPostgresWebSocket } from "./websocket-
|
|
9
|
-
import { A as serverVersionNumToMajor, C as globalsFileForDump, D as parseDbNameFromUrl, E as parseBackupTimestamp, M as withDatabaseName, O as parsePgToolMajor, S as checkToolServerCompatibility, T as parseBackupDestination, _ as buildBackupFilename, a as detectToolMajor, b as buildPgRestoreArgs, c as listBackups, d as resolvePgBinary, f as restoreDump, g as selectBackupsToPrune, h as require_source, i as createDump, j as splitGlobalsStatements, k as resolveConnectionString, l as preflight, m as validateDump, n as applyGlobals, o as ensureDatabaseExists, p as uploadBackup, s as getServerVersionMajor, t as BackupToolError, u as pruneBackups, v as buildPgDumpArgs, w as joinStorageKey, x as buildPgRestoreListArgs, y as buildPgDumpallGlobalsArgs } from "./backup-service-
|
|
8
|
+
import { t as createPostgresWebSocket } from "./websocket-B2LsrINK.js";
|
|
9
|
+
import { A as serverVersionNumToMajor, C as globalsFileForDump, D as parseDbNameFromUrl, E as parseBackupTimestamp, M as withDatabaseName, O as parsePgToolMajor, S as checkToolServerCompatibility, T as parseBackupDestination, _ as buildBackupFilename, a as detectToolMajor, b as buildPgRestoreArgs, c as listBackups, d as resolvePgBinary, f as restoreDump, g as selectBackupsToPrune, h as require_source, i as createDump, j as splitGlobalsStatements, k as resolveConnectionString, l as preflight, m as validateDump, n as applyGlobals, o as ensureDatabaseExists, p as uploadBackup, s as getServerVersionMajor, t as BackupToolError, u as pruneBackups, v as buildPgDumpArgs, w as joinStorageKey, x as buildPgRestoreListArgs, y as buildPgDumpallGlobalsArgs } from "./backup-service-DLb2drIH.js";
|
|
10
10
|
import { Client, Pool } from "pg";
|
|
11
11
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
12
12
|
import { ApiError, createEmailService, loadCollectionsFromDirectory, logger } from "@rebasepro/server";
|
|
@@ -2512,7 +2512,7 @@ function relationTargetAddress(targetRow, targetCollection, registry) {
|
|
|
2512
2512
|
* expects real types. The row's own address is *not* among the columns — it is
|
|
2513
2513
|
* derived by the consumer from the collection's primary keys.
|
|
2514
2514
|
*/
|
|
2515
|
-
function
|
|
2515
|
+
function toFlatRow(row, collection, registry) {
|
|
2516
2516
|
const resolvedRelations = resolveCollectionRelations(collection);
|
|
2517
2517
|
const normalized = normalizeDbValues(row, collection);
|
|
2518
2518
|
for (const [key, relation] of Object.entries(resolvedRelations)) {
|
|
@@ -2709,17 +2709,35 @@ var FetchService = class {
|
|
|
2709
2709
|
const columnAt = (key) => (key in table ? table[key] : void 0) || void 0;
|
|
2710
2710
|
const direct = columnAt(orderBy);
|
|
2711
2711
|
if (direct) return direct;
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
if (foreignKey) return foreignKey;
|
|
2717
|
-
}
|
|
2712
|
+
const declaredRelation = collection ? resolveCollectionRelations(collection)[orderBy] : void 0;
|
|
2713
|
+
if (declaredRelation?.kind === "belongsTo") {
|
|
2714
|
+
const foreignKey = columnAt(declaredRelation.localKey);
|
|
2715
|
+
if (foreignKey) return foreignKey;
|
|
2718
2716
|
}
|
|
2719
2717
|
for (const guess of [`${orderBy}_id`, generateForeignKeyName(orderBy)]) {
|
|
2720
2718
|
const foreignKey = columnAt(guess);
|
|
2721
2719
|
if (foreignKey) return foreignKey;
|
|
2722
2720
|
}
|
|
2721
|
+
const collectionName = collection?.slug ?? collection?.name;
|
|
2722
|
+
const onCollection = collectionName ? ` on collection '${collectionName}'` : "";
|
|
2723
|
+
if (declaredRelation && declaredRelation.kind !== "belongsTo") throw ApiError.badRequest(`Cannot sort by '${orderBy}'${onCollection}: it is a to-many relation (${declaredRelation.kind}), which has no single value per row to order by.`, "ORDER_BY_FIELD_NOT_SORTABLE", {
|
|
2724
|
+
field: orderBy,
|
|
2725
|
+
kind: declaredRelation.kind,
|
|
2726
|
+
...collectionName && { collection: collectionName }
|
|
2727
|
+
});
|
|
2728
|
+
if (getUnknownFilterFieldsMode() === "warn") {
|
|
2729
|
+
logger.warn(`Sorting by field '${orderBy}'${onCollection}, but it does not exist in the table — the ORDER BY was dropped and these rows are unsorted.`);
|
|
2730
|
+
return;
|
|
2731
|
+
}
|
|
2732
|
+
let validFields = [];
|
|
2733
|
+
try {
|
|
2734
|
+
validFields = Object.keys(getTableColumns(table)).sort();
|
|
2735
|
+
} catch {}
|
|
2736
|
+
throw ApiError.badRequest(`Unknown orderBy field '${orderBy}'${onCollection}` + (validFields.length > 0 ? `. Valid fields: ${validFields.join(", ")}` : ""), "UNKNOWN_ORDER_BY_FIELD", {
|
|
2737
|
+
field: orderBy,
|
|
2738
|
+
...collectionName && { collection: collectionName },
|
|
2739
|
+
...validFields.length > 0 && { validFields }
|
|
2740
|
+
});
|
|
2723
2741
|
}
|
|
2724
2742
|
/**
|
|
2725
2743
|
* Build the `with` config for Drizzle's relational query API.
|
|
@@ -2933,7 +2951,7 @@ var FetchService = class {
|
|
|
2933
2951
|
with: withConfig
|
|
2934
2952
|
});
|
|
2935
2953
|
if (!row) return void 0;
|
|
2936
|
-
const flatRow =
|
|
2954
|
+
const flatRow = toFlatRow(row, collection, this.registry);
|
|
2937
2955
|
await this.resolveJoinPathRelations(flatRow, collection, collectionPath, parsedId, databaseId);
|
|
2938
2956
|
return flatRow;
|
|
2939
2957
|
} catch (e) {
|
|
@@ -2988,7 +3006,7 @@ var FetchService = class {
|
|
|
2988
3006
|
const hasRelations = withConfig && Object.keys(withConfig).length > 0;
|
|
2989
3007
|
if (qb && !options.searchString && !hasRelations && !options.vectorSearch) try {
|
|
2990
3008
|
const queryOpts = this.buildDrizzleQueryOptions(table, idField, idInfo, options, collectionPath, void 0, scopeCondition);
|
|
2991
|
-
return (await qb.findMany(queryOpts)).map((row) =>
|
|
3009
|
+
return (await qb.findMany(queryOpts)).map((row) => toFlatRow(row, collection, this.registry));
|
|
2992
3010
|
} catch (e) {
|
|
2993
3011
|
if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
|
|
2994
3012
|
logger.error(`[FetchService] ResolvedRelation inference error for collection '${collectionPath}': ${e.message}`);
|
|
@@ -3051,7 +3069,7 @@ var FetchService = class {
|
|
|
3051
3069
|
/**
|
|
3052
3070
|
* Fallback path used when db.query is unavailable.
|
|
3053
3071
|
*
|
|
3054
|
-
* The primary path runs the results through `
|
|
3072
|
+
* The primary path runs the results through `toFlatRow`, which maps
|
|
3055
3073
|
* relations from what drizzle already nested — no query per row. This one
|
|
3056
3074
|
* has no nesting to read, so it resolves relations itself, in batches.
|
|
3057
3075
|
*
|
|
@@ -3144,6 +3162,10 @@ var FetchService = class {
|
|
|
3144
3162
|
const filterConditions = this.buildFilterConditions(options.filter, table, effectivePath);
|
|
3145
3163
|
if (filterConditions.length > 0) allConditions.push(...filterConditions);
|
|
3146
3164
|
}
|
|
3165
|
+
if (options.logical) {
|
|
3166
|
+
const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, effectivePath, this.filterContext(effectivePath, table));
|
|
3167
|
+
if (logicalCondition) allConditions.push(logicalCondition);
|
|
3168
|
+
}
|
|
3147
3169
|
if (allConditions.length > 0) {
|
|
3148
3170
|
const finalCondition = DrizzleConditionBuilder.combineConditionsWithAnd(allConditions);
|
|
3149
3171
|
if (finalCondition) query = query.where(finalCondition);
|
|
@@ -5056,9 +5078,10 @@ var PostgresBackendDriver = class PostgresBackendDriver {
|
|
|
5056
5078
|
async checkUniqueField(path, name, value, id, collection) {
|
|
5057
5079
|
return this.dataService.checkUniqueField(path, name, value, id, collection?.databaseId);
|
|
5058
5080
|
}
|
|
5059
|
-
async count({ path, collection, filter, searchString }) {
|
|
5081
|
+
async count({ path, collection, filter, logical, searchString }) {
|
|
5060
5082
|
return this.dataService.count(path, {
|
|
5061
5083
|
filter,
|
|
5084
|
+
logical,
|
|
5062
5085
|
searchString
|
|
5063
5086
|
});
|
|
5064
5087
|
}
|
|
@@ -8906,7 +8929,7 @@ function createBackupCron(config) {
|
|
|
8906
8929
|
enabled: config.enabled ?? true,
|
|
8907
8930
|
timeoutSeconds: 3600,
|
|
8908
8931
|
async handler({ log }) {
|
|
8909
|
-
const { createDump, pruneBackups, uploadBackup, validateDump } = await import("./backup-service-
|
|
8932
|
+
const { createDump, pruneBackups, uploadBackup, validateDump } = await import("./backup-service-DLb2drIH.js").then((n) => n.r);
|
|
8910
8933
|
const { destination } = config;
|
|
8911
8934
|
if (destination.kind !== "local" && !config.storage) throw new Error(`Backup destination is ${destination.kind} but no storage controller was provided. Pass the backend's configured StorageController to createBackupCron({ storage }).`);
|
|
8912
8935
|
log(`Starting backup of "${dbName}"…`);
|
|
@@ -11175,6 +11198,36 @@ var IRREGULAR_SINGULARS = {
|
|
|
11175
11198
|
criteria: "criterion",
|
|
11176
11199
|
phenomena: "phenomenon"
|
|
11177
11200
|
};
|
|
11201
|
+
/**
|
|
11202
|
+
* Plurals in "-ves" whose singular really ends in f/fe, and which of the two.
|
|
11203
|
+
*
|
|
11204
|
+
* A blanket "-ves" -> "-f" rule gets `knives` -> `knif`, and mangles every
|
|
11205
|
+
* ordinary "-ive" noun that happens to be plural along with it: `archives` ->
|
|
11206
|
+
* `archif`, `objectives` -> `objectif`. The set of English words that genuinely
|
|
11207
|
+
* swap f/fe for ves is small and closed, so it is listed rather than guessed —
|
|
11208
|
+
* anything else ending in "ves" drops the trailing 's' like any other plural.
|
|
11209
|
+
*
|
|
11210
|
+
* Matched on the whole word, not on the suffix: `olives` ends in `lives`.
|
|
11211
|
+
*/
|
|
11212
|
+
var VES_SINGULAR_ENDINGS = {
|
|
11213
|
+
calves: "f",
|
|
11214
|
+
dwarves: "f",
|
|
11215
|
+
elves: "f",
|
|
11216
|
+
halves: "f",
|
|
11217
|
+
hooves: "f",
|
|
11218
|
+
leaves: "f",
|
|
11219
|
+
loaves: "f",
|
|
11220
|
+
scarves: "f",
|
|
11221
|
+
selves: "f",
|
|
11222
|
+
sheaves: "f",
|
|
11223
|
+
shelves: "f",
|
|
11224
|
+
thieves: "f",
|
|
11225
|
+
wharves: "f",
|
|
11226
|
+
wolves: "f",
|
|
11227
|
+
knives: "fe",
|
|
11228
|
+
lives: "fe",
|
|
11229
|
+
wives: "fe"
|
|
11230
|
+
};
|
|
11178
11231
|
/** Words ending in 's' that are already singular. */
|
|
11179
11232
|
var UNCOUNTABLE = /* @__PURE__ */ new Set([
|
|
11180
11233
|
"status",
|
|
@@ -11213,7 +11266,7 @@ function singularize(word) {
|
|
|
11213
11266
|
if (UNCOUNTABLE.has(lower)) return word;
|
|
11214
11267
|
if (lower.endsWith("ices") && lower.length > 5) return word.slice(0, -4) + "ex";
|
|
11215
11268
|
if (lower.endsWith("ies") && lower.length > 3) return word.slice(0, -3) + "y";
|
|
11216
|
-
if (lower
|
|
11269
|
+
if (VES_SINGULAR_ENDINGS[lower]) return word.slice(0, -3) + VES_SINGULAR_ENDINGS[lower];
|
|
11217
11270
|
if (lower.endsWith("ches") || lower.endsWith("shes") || lower.endsWith("sses") || lower.endsWith("xes") || lower.endsWith("zes")) return word.slice(0, -2);
|
|
11218
11271
|
if (lower.endsWith("ses") && !lower.endsWith("sses")) return word.slice(0, -1);
|
|
11219
11272
|
if (lower.endsWith("s") && !lower.endsWith("ss") && !lower.endsWith("us") && !lower.endsWith("is")) return word.slice(0, -1);
|
|
@@ -12004,7 +12057,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
12004
12057
|
*/
|
|
12005
12058
|
async ensureCollectionSchema(collections, driverResult, log) {
|
|
12006
12059
|
const internals = driverResult.internals;
|
|
12007
|
-
const { ensureCollectionTables } = await import("./ensure-collection-tables-
|
|
12060
|
+
const { ensureCollectionTables } = await import("./ensure-collection-tables-CT6xHB1d.js");
|
|
12008
12061
|
const plan = await ensureCollectionTables({ async query(text) {
|
|
12009
12062
|
const result = await internals.db.execute(sql.raw(text));
|
|
12010
12063
|
return { rows: result.rows ?? (Array.isArray(result) ? result : []) };
|
|
@@ -12029,7 +12082,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
12029
12082
|
*/
|
|
12030
12083
|
async ensureCollectionPolicies(collections, driverResult, log) {
|
|
12031
12084
|
const internals = driverResult.internals;
|
|
12032
|
-
const { ensureCollectionPolicies } = await import("./ensure-collection-policies-
|
|
12085
|
+
const { ensureCollectionPolicies } = await import("./ensure-collection-policies-Dv21KDMJ.js");
|
|
12033
12086
|
const outcome = await ensureCollectionPolicies({ async query(text) {
|
|
12034
12087
|
const result = await internals.db.execute(sql.raw(text));
|
|
12035
12088
|
return { rows: result.rows ?? (Array.isArray(result) ? result : []) };
|
|
@@ -12043,7 +12096,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
12043
12096
|
},
|
|
12044
12097
|
mountRoutes(app, basePath, driverResult) {},
|
|
12045
12098
|
async initializeWebsockets(server, realtimeService, driver, config, adapter) {
|
|
12046
|
-
const { createPostgresWebSocket } = await import("./websocket-
|
|
12099
|
+
const { createPostgresWebSocket } = await import("./websocket-B2LsrINK.js").then((n) => n.n);
|
|
12047
12100
|
createPostgresWebSocket(server, realtimeService, driver, config, adapter);
|
|
12048
12101
|
}
|
|
12049
12102
|
};
|
|
@@ -12069,8 +12122,8 @@ function createPostgresAdapter(pgConfig) {
|
|
|
12069
12122
|
async initializeHistory(config, driverResult) {
|
|
12070
12123
|
if (bootstrapper.initializeHistory) return bootstrapper.initializeHistory(config, driverResult);
|
|
12071
12124
|
},
|
|
12072
|
-
initializeWebsockets(server, realtimeService, driver, config) {
|
|
12073
|
-
if (bootstrapper.initializeWebsockets) return bootstrapper.initializeWebsockets(server, realtimeService, driver, config);
|
|
12125
|
+
initializeWebsockets(server, realtimeService, driver, config, adapter) {
|
|
12126
|
+
if (bootstrapper.initializeWebsockets) return bootstrapper.initializeWebsockets(server, realtimeService, driver, config, adapter);
|
|
12074
12127
|
},
|
|
12075
12128
|
ensureCollectionSchema: bootstrapper.ensureCollectionSchema ? (collections, driverResult, log) => bootstrapper.ensureCollectionSchema(collections, driverResult, log) : void 0,
|
|
12076
12129
|
ensureCollectionPolicies: bootstrapper.ensureCollectionPolicies ? (collections, driverResult, log) => bootstrapper.ensureCollectionPolicies(collections, driverResult, log) : void 0,
|