@rebasepro/server-postgres 0.13.0 → 0.13.1-canary.gcd6689e
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/index.es.js +86 -61
- package/dist/index.es.js.map +1 -1
- package/dist/schema/generated-schema-staleness.d.ts +39 -0
- package/dist/services/FetchService.d.ts +0 -7
- package/dist/src-DlPBctw_.js.map +1 -1
- package/package.json +6 -6
- package/src/cli.ts +79 -0
- package/src/collections/validate-relations.ts +107 -16
- package/src/schema/generated-schema-staleness.ts +164 -0
- package/src/schema/introspect-db-logic.ts +1 -1
- package/src/services/FetchService.ts +0 -93
package/dist/index.es.js
CHANGED
|
@@ -2,7 +2,7 @@ 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 { A as toSnakeCase, C as normalizeToEntityRelation, D as getPolicyNamesForRule, F as Vector, N as hasForeignKeyOnTarget, O as mergeDeep, P as isManyToMany, S as createRelationRefWithData, T as generateForeignKeyName, _ as buildCompositeId, a as getJunctionSecurityRules, b as parseIdValues, c as policyToPostgres, d as findRelation, f as getColumnName, g as resolveCollectionRelations, h as getTableVarName, i as getJunctionCollectionConfig, k as camelCase, 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 updateDateAutoValues, x as createRelationRef, y as isAddressableId } from "./src-DlPBctw_.js";
|
|
5
|
+
import { A as toSnakeCase, C as normalizeToEntityRelation, D as getPolicyNamesForRule, E as legacyForeignKeyName, F as Vector, N as hasForeignKeyOnTarget, O as mergeDeep, P as isManyToMany, S as createRelationRefWithData, T as generateForeignKeyName, _ as buildCompositeId, a as getJunctionSecurityRules, b as parseIdValues, c as policyToPostgres, d as findRelation, f as getColumnName, g as resolveCollectionRelations, h as getTableVarName, i as getJunctionCollectionConfig, k as camelCase, 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 updateDateAutoValues, x as createRelationRef, y as isAddressableId } from "./src-DlPBctw_.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
8
|
import { t as createPostgresWebSocket } from "./websocket-B2LsrINK.js";
|
|
@@ -3796,51 +3796,6 @@ var FetchService = class {
|
|
|
3796
3796
|
return !!this.getQueryBuilder(tableName);
|
|
3797
3797
|
}
|
|
3798
3798
|
/**
|
|
3799
|
-
* Attempt to use Drizzle's relational query API (db.query.<table>.findMany)
|
|
3800
|
-
* for efficient JOIN-based relation loading.
|
|
3801
|
-
* Returns null if the API is not available or the query fails.
|
|
3802
|
-
* Note: Primary path now uses `buildWithConfig` + `buildDrizzleQueryOptions`.
|
|
3803
|
-
*/
|
|
3804
|
-
async fetchWithDrizzleQuery(collectionPath, collection, options, include, idInfo, idInfoArray) {
|
|
3805
|
-
try {
|
|
3806
|
-
const table = getTableForCollection(collection, this.registry);
|
|
3807
|
-
const tableName = getTableName(table);
|
|
3808
|
-
const queryTarget = this.getQueryBuilder(tableName);
|
|
3809
|
-
if (!queryTarget?.findMany) return null;
|
|
3810
|
-
const resolvedRelations = resolveCollectionRelations(collection);
|
|
3811
|
-
const withConfig = {};
|
|
3812
|
-
for (const [key, relation] of Object.entries(resolvedRelations)) if (include[0] === "*" || include.includes(key)) {
|
|
3813
|
-
const drizzleRelName = relation.relationName || key;
|
|
3814
|
-
withConfig[drizzleRelName] = true;
|
|
3815
|
-
}
|
|
3816
|
-
const queryOpts = { with: withConfig };
|
|
3817
|
-
if (options.limit) queryOpts.limit = options.limit;
|
|
3818
|
-
if (options.filter) {
|
|
3819
|
-
const filterConditions = this.buildFilterConditions(options.filter, table, collectionPath);
|
|
3820
|
-
if (filterConditions.length > 0) queryOpts.where = and(...filterConditions);
|
|
3821
|
-
}
|
|
3822
|
-
if (options.orderBy) {
|
|
3823
|
-
const orderByField = this.resolveOrderByField(table, options.orderBy, collection);
|
|
3824
|
-
if (orderByField) queryOpts.orderBy = options.order === "asc" ? asc(orderByField) : desc(orderByField);
|
|
3825
|
-
}
|
|
3826
|
-
return (await queryTarget.findMany(queryOpts)).map((row) => {
|
|
3827
|
-
const flat = {};
|
|
3828
|
-
for (const [k, v] of Object.entries(row)) if (Array.isArray(v)) flat[k] = v.map((item) => {
|
|
3829
|
-
const keys = Object.keys(item);
|
|
3830
|
-
const nestedObj = keys.find((nk) => typeof item[nk] === "object" && item[nk] !== null && !Array.isArray(item[nk]));
|
|
3831
|
-
if (nestedObj && keys.length <= 3) return { ...item[nestedObj] };
|
|
3832
|
-
return { ...item };
|
|
3833
|
-
});
|
|
3834
|
-
else if (typeof v === "object" && v !== null) flat[k] = { ...v };
|
|
3835
|
-
else flat[k] = v;
|
|
3836
|
-
return flat;
|
|
3837
|
-
});
|
|
3838
|
-
} catch (e) {
|
|
3839
|
-
logger.warn(`[include] Drizzle relational query failed for '${collectionPath}', falling back`, { error: e });
|
|
3840
|
-
return null;
|
|
3841
|
-
}
|
|
3842
|
-
}
|
|
3843
|
-
/**
|
|
3844
3799
|
* Fallback path used when db.query is unavailable.
|
|
3845
3800
|
* The primary path uses db.query.findMany with `with` config, which
|
|
3846
3801
|
* loads all relations in a single query.
|
|
@@ -9203,6 +9158,54 @@ var quote = (xs) => Array.from(xs).map((s) => `\`${s}\``).join(", ");
|
|
|
9203
9158
|
/** `on.from` / `on.to` accept a single column or a composite tuple. */
|
|
9204
9159
|
var asColumns = (value) => Array.isArray(value) ? value : [value];
|
|
9205
9160
|
/**
|
|
9161
|
+
* Distinguish "this column name is wrong" from "the generated schema is old".
|
|
9162
|
+
*
|
|
9163
|
+
* They present identically here — a relation asks for a column the registered
|
|
9164
|
+
* table does not have — but they are opposite problems with opposite fixes, and
|
|
9165
|
+
* getting them the wrong way round is how the 0.12 → 0.13 upgrade bricked
|
|
9166
|
+
* projects.
|
|
9167
|
+
*
|
|
9168
|
+
* The registered table is not the database. It comes from the project's
|
|
9169
|
+
* checked-in `backend/src/schema.generated.ts`, and 0.13 changed the rule that
|
|
9170
|
+
* derives foreign-key names: `categories` yields `category_id` where it used to
|
|
9171
|
+
* yield `categorie_id`. Boot-ensure renames the database column to match, so by
|
|
9172
|
+
* the time this runs the *database* is correct and the *generated module* is the
|
|
9173
|
+
* stale one. Reporting "not a column" then points at the wrong artifact, and the
|
|
9174
|
+
* generic fix — "set `through.targetColumn` to one of: …", listing the legacy
|
|
9175
|
+
* name because that is what the stale module still has — talks the reader into
|
|
9176
|
+
* pinning a column that no longer exists.
|
|
9177
|
+
*
|
|
9178
|
+
* So when the wanted name is what the current rule derives, and the table
|
|
9179
|
+
* carries what the *previous* rule would have derived from the same source, say
|
|
9180
|
+
* that instead.
|
|
9181
|
+
*
|
|
9182
|
+
* @param wanted the column the relation asks for
|
|
9183
|
+
* @param available every column the registered table has
|
|
9184
|
+
* @param sources names the default could have been derived from (a slug, a
|
|
9185
|
+
* relation name) — checking against these rather than guessing
|
|
9186
|
+
* backwards from `wanted` keeps the match exact
|
|
9187
|
+
*/
|
|
9188
|
+
function staleCodegenRename(wanted, available, sources) {
|
|
9189
|
+
for (const source of sources) {
|
|
9190
|
+
if (!source) continue;
|
|
9191
|
+
const current = generateForeignKeyName(source);
|
|
9192
|
+
const legacy = legacyForeignKeyName(source);
|
|
9193
|
+
if (current !== wanted || legacy === current) continue;
|
|
9194
|
+
if (available.has(legacy) && !available.has(current)) return {
|
|
9195
|
+
legacy,
|
|
9196
|
+
current
|
|
9197
|
+
};
|
|
9198
|
+
}
|
|
9199
|
+
return null;
|
|
9200
|
+
}
|
|
9201
|
+
/** The shared explanation, so every relation kind reports it identically. */
|
|
9202
|
+
function staleCodegenDefect(table, { legacy, current }) {
|
|
9203
|
+
return {
|
|
9204
|
+
problem: `the generated Drizzle schema still declares \`${legacy}\` on \`${table}\`, but this release derives \`${current}\` — the generated schema predates the foreign-key naming fix and no longer describes the database`,
|
|
9205
|
+
fix: `regenerate it with \`rebase schema generate\` (or \`pnpm run schema:generate\`). The database column has already been renamed for you at boot, so nothing else is needed. To keep \`${legacy}\` instead, name it explicitly on the relation and regenerate.`
|
|
9206
|
+
};
|
|
9207
|
+
}
|
|
9208
|
+
/**
|
|
9206
9209
|
* Relations whose names do not resolve against the registered schema.
|
|
9207
9210
|
*
|
|
9208
9211
|
* Fails open wherever it cannot see enough to be sure — an unregistered source
|
|
@@ -9249,19 +9252,31 @@ function findRelationDefects(collections, registry) {
|
|
|
9249
9252
|
const targetColumns = columnNames(targetTable);
|
|
9250
9253
|
switch (relation.kind) {
|
|
9251
9254
|
case "belongsTo":
|
|
9252
|
-
if (!sourceColumns.has(relation.localKey))
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9255
|
+
if (!sourceColumns.has(relation.localKey)) {
|
|
9256
|
+
const stale = staleCodegenRename(relation.localKey, sourceColumns, [relation.relationName, targetCollection.slug]);
|
|
9257
|
+
defects.push(stale ? {
|
|
9258
|
+
...at,
|
|
9259
|
+
...staleCodegenDefect(sourceTableName, stale)
|
|
9260
|
+
} : {
|
|
9261
|
+
...at,
|
|
9262
|
+
problem: `\`localKey: "${relation.localKey}"\` is not a column on \`${sourceTableName}\``,
|
|
9263
|
+
fix: `add the column, or set \`localKey\` to one of: ${quote(sourceColumns)}`
|
|
9264
|
+
});
|
|
9265
|
+
}
|
|
9257
9266
|
break;
|
|
9258
9267
|
case "hasOne":
|
|
9259
9268
|
case "hasMany":
|
|
9260
|
-
if (!targetColumns.has(relation.foreignKeyOnTarget))
|
|
9261
|
-
|
|
9262
|
-
|
|
9263
|
-
|
|
9264
|
-
|
|
9269
|
+
if (!targetColumns.has(relation.foreignKeyOnTarget)) {
|
|
9270
|
+
const stale = staleCodegenRename(relation.foreignKeyOnTarget, targetColumns, [collection.slug]);
|
|
9271
|
+
defects.push(stale ? {
|
|
9272
|
+
...at,
|
|
9273
|
+
...staleCodegenDefect(targetTableName, stale)
|
|
9274
|
+
} : {
|
|
9275
|
+
...at,
|
|
9276
|
+
problem: `\`foreignKeyOnTarget: "${relation.foreignKeyOnTarget}"\` is not a column on the target table \`${targetTableName}\``,
|
|
9277
|
+
fix: `add the column, or set \`foreignKeyOnTarget\` to one of: ${quote(targetColumns)}`
|
|
9278
|
+
});
|
|
9279
|
+
}
|
|
9265
9280
|
if (relation.sourceKey && !sourceColumns.has(relation.sourceKey)) defects.push({
|
|
9266
9281
|
...at,
|
|
9267
9282
|
problem: `\`sourceKey: "${relation.sourceKey}"\` is not a column on \`${sourceTableName}\``,
|
|
@@ -9280,11 +9295,21 @@ function findRelationDefects(collections, registry) {
|
|
|
9280
9295
|
break;
|
|
9281
9296
|
}
|
|
9282
9297
|
const junctionColumns = columnNames(junction);
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9298
|
+
const derivedFrom = {
|
|
9299
|
+
sourceColumn: [collection.slug],
|
|
9300
|
+
targetColumn: [targetCollection.slug]
|
|
9301
|
+
};
|
|
9302
|
+
for (const [label, column] of [["sourceColumn", sourceColumn], ["targetColumn", targetColumn]]) if (!junctionColumns.has(column)) {
|
|
9303
|
+
const stale = staleCodegenRename(column, junctionColumns, [...derivedFrom[label]]);
|
|
9304
|
+
defects.push(stale ? {
|
|
9305
|
+
...at,
|
|
9306
|
+
...staleCodegenDefect(table, stale)
|
|
9307
|
+
} : {
|
|
9308
|
+
...at,
|
|
9309
|
+
problem: `\`through.${label}: "${column}"\` is not a column on the junction table \`${table}\``,
|
|
9310
|
+
fix: `set \`through.${label}\` to one of: ${quote(junctionColumns)}` + (label === "sourceColumn" ? " — it is the column naming *this* collection" : "")
|
|
9311
|
+
});
|
|
9312
|
+
}
|
|
9288
9313
|
break;
|
|
9289
9314
|
}
|
|
9290
9315
|
case "via": {
|