@rebasepro/server-postgres 0.12.1-canary.gcfa333c → 0.12.1-canary.gdd7172e
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-BrUVgjz3.js} +2 -2
- package/dist/{ensure-collection-policies-Vl3Cv1Q9.js.map → ensure-collection-policies-BrUVgjz3.js.map} +1 -1
- package/dist/{ensure-collection-tables-DsDsNl6o.js → ensure-collection-tables-Da2oGkX2.js} +72 -12
- package/dist/ensure-collection-tables-Da2oGkX2.js.map +1 -0
- package/dist/index.es.js +387 -120
- 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 +24 -1
- package/dist/services/dataService.d.ts +3 -1
- package/dist/services/row-pipeline.d.ts +1 -1
- package/dist/{src-DihrDFuP.js → src-CzbghKwf.js} +189 -101
- package/dist/src-CzbghKwf.js.map +1 -0
- package/dist/utils/connection-string.d.ts +29 -0
- package/dist/utils/drizzle-conditions.d.ts +5 -4
- 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 +16 -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 +76 -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 +40 -1
- 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/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
package/dist/index.es.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { createRequire as __createRequire } from "module";
|
|
2
2
|
import process from "process";
|
|
3
3
|
__createRequire(import.meta.url);
|
|
4
|
-
import { a as guardPoolAgainstDirtyRelease, i as createReadReplicaConnection, n as createDirectDatabaseConnection, r as createPostgresDatabaseConnection } from "./connection-
|
|
5
|
-
import { C as updateDateAutoValues, D as
|
|
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 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-CzbghKwf.js";
|
|
6
6
|
import { n as isPostgresCollectionConfig, r as isRelationalCollectionConfig } from "./src-DoU9yPqq.js";
|
|
7
|
-
import { t as
|
|
8
|
-
import {
|
|
7
|
+
import { t as ANONYMOUS_USER_ID } from "./policy-CeA1JcxP.js";
|
|
8
|
+
import { t as createPostgresWebSocket } from "./websocket-B2LsrINK.js";
|
|
9
|
+
import { A as parsePgToolMajor, C as checkToolServerCompatibility, D as parseBackupDestination, E as joinStorageKey, M as serverVersionNumToMajor, N as splitGlobalsStatements, O as parseBackupTimestamp, P as withDatabaseName, S as buildRowSecurityPgOptions, T as globalsFileForDump, _ 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 resolveConnectionString, k as parseDbNameFromUrl, 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 diagnoseRowSecurityDumpFailure, x as buildPgRestoreListArgs, y as buildPgDumpallGlobalsArgs } from "./backup-service-CD8o_1Sl.js";
|
|
9
10
|
import { Client, Pool } from "pg";
|
|
10
11
|
import { drizzle } from "drizzle-orm/node-postgres";
|
|
11
12
|
import { ApiError, createEmailService, loadCollectionsFromDirectory, logger } from "@rebasepro/server";
|
|
@@ -49,6 +50,39 @@ function resolveClientListLimit(rawLimit, opts = {}) {
|
|
|
49
50
|
return opts.vectorSearch ? opts.vectorDefaultLimit ?? 10 : opts.defaultLimit ?? 50;
|
|
50
51
|
}
|
|
51
52
|
//#endregion
|
|
53
|
+
//#region ../common/src/util/email.ts
|
|
54
|
+
/**
|
|
55
|
+
* Email normalization — one implementation, because the database enforces it.
|
|
56
|
+
*
|
|
57
|
+
* `ensureAuthTablesExist` puts a `UNIQUE INDEX ON users (lower(email))` on the
|
|
58
|
+
* auth table. That index decides what "the same address" means, and it does not
|
|
59
|
+
* trim: to Postgres, `' foo@bar.com'` and `'foo@bar.com'` are two addresses and
|
|
60
|
+
* both may exist. So every write that reaches the column has to agree with
|
|
61
|
+
* every read, exactly, or the two disagree in the one direction that matters —
|
|
62
|
+
* a row that exists and cannot be found.
|
|
63
|
+
*
|
|
64
|
+
* That is not hypothetical. The lookup path trimmed and the admin create paths
|
|
65
|
+
* did not, so a user created through `POST /api/data/users` or
|
|
66
|
+
* `POST /api/auth/admin/users` with a stray space was stored untrimmed,
|
|
67
|
+
* survived the unique index alongside the real address, and was unreachable by
|
|
68
|
+
* login forever after. The HTTP auth routes were unaffected only because Zod's
|
|
69
|
+
* `.email()` happens to reject surrounding whitespace — a guard on a different
|
|
70
|
+
* layer, for a different reason, that the admin paths do not sit behind.
|
|
71
|
+
*
|
|
72
|
+
* It lives in `common` because `server`, `server-postgres` and `server-mongo`
|
|
73
|
+
* all write this column and must agree exactly, and `common` is the only
|
|
74
|
+
* package all three already depend on.
|
|
75
|
+
*/
|
|
76
|
+
/**
|
|
77
|
+
* Canonical form of an email address: trimmed, lower-cased.
|
|
78
|
+
*
|
|
79
|
+
* Non-strings pass through untouched, so this is safe to apply to a value out
|
|
80
|
+
* of a partial update payload whose type is not known yet.
|
|
81
|
+
*/
|
|
82
|
+
function normalizeEmail(email) {
|
|
83
|
+
return typeof email === "string" ? email.trim().toLowerCase() : email;
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
52
86
|
//#region ../common/src/util/callbacks.ts
|
|
53
87
|
/**
|
|
54
88
|
* Helper function to recursively check if there are any callbacks in the properties.
|
|
@@ -281,6 +315,35 @@ function requirePrimaryKeys(collection, registry) {
|
|
|
281
315
|
return keys;
|
|
282
316
|
}
|
|
283
317
|
/**
|
|
318
|
+
* The column on the *source* table that a `hasOne`/`hasMany` link points at.
|
|
319
|
+
*
|
|
320
|
+
* `sourceKey` is authored when the two sides join on a natural key — an
|
|
321
|
+
* external identity id, a SKU — and left off when they join on the row id,
|
|
322
|
+
* which is the overwhelming majority. That makes `undefined` the only optional
|
|
323
|
+
* field on a resolved relation, so it gets exactly one reader: this function.
|
|
324
|
+
* Every consumer that needs the column asks here, and none of them re-derives
|
|
325
|
+
* "or else the primary key" for itself. That is the whole point — the fallback
|
|
326
|
+
* chains this codebase removed from relation resolution were dangerous because
|
|
327
|
+
* they were *duplicated* and could disagree, not because they existed.
|
|
328
|
+
*/
|
|
329
|
+
function sourceKeyField(relation, sourceCollection, registry) {
|
|
330
|
+
if (relation.sourceKey) return relation.sourceKey;
|
|
331
|
+
return requirePrimaryKeys(sourceCollection, registry)[0].fieldName;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Whether this link joins on something other than the source's primary key.
|
|
335
|
+
*
|
|
336
|
+
* Callers that hold a parent *id* — which is most of them, since an id is what
|
|
337
|
+
* a URL carries — must translate it to the source key's value before it can be
|
|
338
|
+
* compared with the target's foreign key. Those that hold the parent *row*, or
|
|
339
|
+
* that build a correlated subquery over the source table, can read the column
|
|
340
|
+
* directly and skip the lookup.
|
|
341
|
+
*/
|
|
342
|
+
function joinsOnNaturalKey(relation, sourceCollection, registry) {
|
|
343
|
+
if (!relation.sourceKey) return false;
|
|
344
|
+
return relation.sourceKey !== requirePrimaryKeys(sourceCollection, registry)[0].fieldName;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
284
347
|
* Collections whose key the *browser* cannot resolve, and what it will do
|
|
285
348
|
* instead.
|
|
286
349
|
*
|
|
@@ -451,7 +514,9 @@ var DrizzleConditionBuilder = class {
|
|
|
451
514
|
case "hasMany": {
|
|
452
515
|
const fkColumn = targetTable[relation.foreignKeyOnTarget];
|
|
453
516
|
if (!fkColumn) throw new Error(`Foreign key column '${relation.foreignKeyOnTarget}' not found in the target table of relation '${relation.relationName}'.`);
|
|
454
|
-
return eq(fkColumn, parentId);
|
|
517
|
+
if (!relation.sourceKey) return eq(fkColumn, parentId);
|
|
518
|
+
const { table, idColumn } = parent();
|
|
519
|
+
return sql`${fkColumn} = (SELECT ${sql.identifier(relation.sourceKey)} FROM ${table} WHERE ${idColumn} = ${parentId})`;
|
|
455
520
|
}
|
|
456
521
|
case "belongsTo": {
|
|
457
522
|
const { table, idColumn } = parent();
|
|
@@ -541,12 +606,16 @@ var DrizzleConditionBuilder = class {
|
|
|
541
606
|
column: foreignKey
|
|
542
607
|
};
|
|
543
608
|
}
|
|
544
|
-
if (relation && (hasForeignKeyOnTarget(relation) || isManyToMany(relation)) && registry && sourceIdColumn)
|
|
545
|
-
|
|
546
|
-
relation,
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
609
|
+
if (relation && (hasForeignKeyOnTarget(relation) || isManyToMany(relation)) && registry && sourceIdColumn) {
|
|
610
|
+
const correlationColumn = hasForeignKeyOnTarget(relation) && relation.sourceKey ? columnAt(relation.sourceKey) : sourceIdColumn;
|
|
611
|
+
if (!correlationColumn) throw new Error(`\`sourceKey: "${relation.sourceKey}"\` on relation '${relation.relationName}' is not a column on '${collectionPath}', so a filter on that relation has nothing to correlate against.`);
|
|
612
|
+
return {
|
|
613
|
+
kind: "relation",
|
|
614
|
+
relation,
|
|
615
|
+
registry,
|
|
616
|
+
sourceIdColumn: correlationColumn
|
|
617
|
+
};
|
|
618
|
+
}
|
|
550
619
|
}
|
|
551
620
|
for (const guess of [`${field}_id`, generateForeignKeyName(field)]) {
|
|
552
621
|
const foreignKey = columnAt(guess);
|
|
@@ -1345,8 +1414,9 @@ async function parseDataFromServer(data, collection, db, registry) {
|
|
|
1345
1414
|
} else if (hasForeignKeyOnTarget(relation) && db && registry) try {
|
|
1346
1415
|
const targetCollection = relation.target();
|
|
1347
1416
|
const targetTable = registry.getTable(getTableName$1(targetCollection));
|
|
1348
|
-
const
|
|
1349
|
-
|
|
1417
|
+
const pks = getPrimaryKeys(collection, registry);
|
|
1418
|
+
const currentId = relation.sourceKey ? data[relation.sourceKey] : buildCompositeId(data, pks);
|
|
1419
|
+
if (targetTable && currentId !== void 0 && currentId !== null && currentId !== "") {
|
|
1350
1420
|
const foreignKeyColumn = targetTable[relation.foreignKeyOnTarget];
|
|
1351
1421
|
if (foreignKeyColumn) {
|
|
1352
1422
|
const relatedRows = await db.select().from(targetTable).where(eq(foreignKeyColumn, currentId)).limit(relation.cardinality === "one" ? 1 : 100);
|
|
@@ -1718,8 +1788,67 @@ var RelationService = class {
|
|
|
1718
1788
|
* reading. Left alone it would silently match on the first key column and
|
|
1719
1789
|
* hand a tenant's rows to its neighbour — say so instead.
|
|
1720
1790
|
*/
|
|
1721
|
-
assertSingleKeyAddressable(parentCollection, parentPks, via) {
|
|
1722
|
-
if (
|
|
1791
|
+
assertSingleKeyAddressable(parentCollection, parentPks, via, relation) {
|
|
1792
|
+
if (relation && hasForeignKeyOnTarget(relation) && relation.sourceKey) return;
|
|
1793
|
+
if (parentPks.length > 1) throw new Error(`Relation on '${parentCollection.slug}' uses '${via}', a single foreign-key column, but '${parentCollection.slug}' is keyed on ${parentPks.map((k) => `'${k.fieldName}'`).join(" + ")}. One column cannot reference a composite key — give the relation a \`sourceKey\` naming a single unique column to point at, or express it with \`joinPath\`, whose \`on.from\`/\`on.to\` take every key column.`);
|
|
1794
|
+
}
|
|
1795
|
+
/**
|
|
1796
|
+
* What the target's foreign key holds, for each of these parent rows.
|
|
1797
|
+
*
|
|
1798
|
+
* Ordinarily the parent's id, and then this is free. When the relation
|
|
1799
|
+
* declares a `sourceKey` the two are different values, and the mapping
|
|
1800
|
+
* between them lives in the source table — so it costs one SELECT, issued
|
|
1801
|
+
* once for the whole batch rather than per parent.
|
|
1802
|
+
*
|
|
1803
|
+
* Both directions come back because both are needed and deriving one from
|
|
1804
|
+
* the other by hand is how a batch loader ends up attributing a child to the
|
|
1805
|
+
* wrong parent: reads translate id → key to build the WHERE, and then
|
|
1806
|
+
* translate key → id to attribute each row that comes back.
|
|
1807
|
+
*/
|
|
1808
|
+
/**
|
|
1809
|
+
* The value a related row's foreign key must hold to belong to this parent.
|
|
1810
|
+
*
|
|
1811
|
+
* `undefined` when the parent's source key is null — which is not an error
|
|
1812
|
+
* here, only in the callers that were about to write it. Exposed for
|
|
1813
|
+
* {@link PersistService}, which stamps this onto a child created under a
|
|
1814
|
+
* nested path and would otherwise write the id and lose the row.
|
|
1815
|
+
*/
|
|
1816
|
+
async parentKeyValue(parentCollection, relation, parentId, db = this.db) {
|
|
1817
|
+
const { keyByParentId } = await this.resolveSourceKeys(parentCollection, relation, [parentId], db);
|
|
1818
|
+
return keyByParentId.get(String(parentId));
|
|
1819
|
+
}
|
|
1820
|
+
async resolveSourceKeys(parentCollection, relation, parentIds, db = this.db) {
|
|
1821
|
+
const keyByParentId = /* @__PURE__ */ new Map();
|
|
1822
|
+
const parentIdByKey = /* @__PURE__ */ new Map();
|
|
1823
|
+
const parentPks = requirePrimaryKeys(parentCollection, this.registry);
|
|
1824
|
+
if (!joinsOnNaturalKey(relation, parentCollection, this.registry)) {
|
|
1825
|
+
for (const id of parentIds) {
|
|
1826
|
+
const value = parseIdValues(id, parentPks)[parentPks[0].fieldName];
|
|
1827
|
+
keyByParentId.set(String(id), value);
|
|
1828
|
+
parentIdByKey.set(String(value), id);
|
|
1829
|
+
}
|
|
1830
|
+
return {
|
|
1831
|
+
keyByParentId,
|
|
1832
|
+
parentIdByKey
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1835
|
+
const field = sourceKeyField(relation, parentCollection, this.registry);
|
|
1836
|
+
const parentTable = getTableForCollection(parentCollection, this.registry);
|
|
1837
|
+
if (!parentTable[field]) throw new Error(`\`sourceKey: "${field}"\` on relation '${relation.relationName}' is not a column on '${parentCollection.slug}'. It names a column on the source table, not on the target.`);
|
|
1838
|
+
const rows = await db.select().from(parentTable).where(this.parentKeyCondition(parentTable, parentPks, parentIds));
|
|
1839
|
+
for (const row of rows) {
|
|
1840
|
+
const keyValue = row[field];
|
|
1841
|
+
if (keyValue === null || keyValue === void 0) continue;
|
|
1842
|
+
const parentId = buildCompositeId(row, parentPks);
|
|
1843
|
+
keyByParentId.set(String(parentId), keyValue);
|
|
1844
|
+
const existing = parentIdByKey.get(String(keyValue));
|
|
1845
|
+
if (existing !== void 0 && String(existing) !== String(parentId)) throw new Error(`\`sourceKey: "${field}"\` on relation '${relation.relationName}' is not unique on '${parentCollection.slug}': rows '${existing}' and '${parentId}' both hold '${keyValue}'. Add a unique constraint — a link that addresses more than one source row cannot say which one a related row belongs to.`);
|
|
1846
|
+
parentIdByKey.set(String(keyValue), parentId);
|
|
1847
|
+
}
|
|
1848
|
+
return {
|
|
1849
|
+
keyByParentId,
|
|
1850
|
+
parentIdByKey
|
|
1851
|
+
};
|
|
1723
1852
|
}
|
|
1724
1853
|
/**
|
|
1725
1854
|
* Fetch rows related to a parent row through a specific relation
|
|
@@ -1778,6 +1907,8 @@ var RelationService = class {
|
|
|
1778
1907
|
}
|
|
1779
1908
|
return rows;
|
|
1780
1909
|
}
|
|
1910
|
+
const matchValue = hasForeignKeyOnTarget(relation) ? (await this.resolveSourceKeys(parentCollection, relation, [parentId])).keyByParentId.get(String(parentId)) : parsedParentId;
|
|
1911
|
+
if (matchValue === void 0) return [];
|
|
1781
1912
|
let query = this.db.select().from(targetTable).$dynamic();
|
|
1782
1913
|
const additionalFilters = [];
|
|
1783
1914
|
if (options.searchString) {
|
|
@@ -1786,7 +1917,7 @@ var RelationService = class {
|
|
|
1786
1917
|
const searchCombined = DrizzleConditionBuilder.combineConditionsWithOr(searchConditions);
|
|
1787
1918
|
if (searchCombined) additionalFilters.push(searchCombined);
|
|
1788
1919
|
}
|
|
1789
|
-
query = applyDynamicRelationQuery(query, query, relation,
|
|
1920
|
+
query = applyDynamicRelationQuery(query, query, relation, matchValue, targetTable, parentTable, parentIdCol, idField, this.registry, additionalFilters);
|
|
1790
1921
|
if (options.limit) query = query.limit(options.limit);
|
|
1791
1922
|
const results = await query;
|
|
1792
1923
|
if (!results || !Array.isArray(results)) return [];
|
|
@@ -1829,8 +1960,10 @@ var RelationService = class {
|
|
|
1829
1960
|
const parentTable = this.registry.getTable(getTableName$1(parentCollection));
|
|
1830
1961
|
if (!parentTable) throw new Error("Parent table not found");
|
|
1831
1962
|
const parentIdCol = parentTable[parentIdInfo.fieldName];
|
|
1963
|
+
const matchValue = hasForeignKeyOnTarget(relation) ? (await this.resolveSourceKeys(parentCollection, relation, [parentId])).keyByParentId.get(String(parentId)) : parsedParentId;
|
|
1964
|
+
if (matchValue === void 0) return 0;
|
|
1832
1965
|
let query = this.db.select({ count: sql`count(distinct ${targetIdField})` }).from(targetTable).$dynamic();
|
|
1833
|
-
query = DrizzleConditionBuilder.buildRelationCountQuery(query, relation,
|
|
1966
|
+
query = DrizzleConditionBuilder.buildRelationCountQuery(query, relation, matchValue, targetTable, parentTable, parentIdCol, targetIdField, this.registry, additionalFilters);
|
|
1834
1967
|
const result = await query;
|
|
1835
1968
|
return Number(result[0]?.count || 0);
|
|
1836
1969
|
}
|
|
@@ -1957,17 +2090,24 @@ var RelationService = class {
|
|
|
1957
2090
|
}
|
|
1958
2091
|
return resultMap;
|
|
1959
2092
|
}
|
|
1960
|
-
this.assertSingleKeyAddressable(parentCollection, parentPks, hasForeignKeyOnTarget(relation) ? relation.foreignKeyOnTarget : relation.relationName);
|
|
2093
|
+
this.assertSingleKeyAddressable(parentCollection, parentPks, hasForeignKeyOnTarget(relation) ? relation.foreignKeyOnTarget : relation.relationName, relation);
|
|
2094
|
+
const { keyByParentId, parentIdByKey } = hasForeignKeyOnTarget(relation) ? await this.resolveSourceKeys(parentCollection, relation, parentIds) : {
|
|
2095
|
+
keyByParentId: /* @__PURE__ */ new Map(),
|
|
2096
|
+
parentIdByKey: /* @__PURE__ */ new Map()
|
|
2097
|
+
};
|
|
2098
|
+
const matchValues = hasForeignKeyOnTarget(relation) ? [...keyByParentId.values()] : parsedParentIds;
|
|
2099
|
+
if (matchValues.length === 0) return /* @__PURE__ */ new Map();
|
|
1961
2100
|
let query = this.db.select().from(targetTable).$dynamic();
|
|
1962
|
-
query = applyDynamicRelationQuery(query, query, relation,
|
|
2101
|
+
query = applyDynamicRelationQuery(query, query, relation, matchValues, targetTable, parentTable, parentIdCol, targetIdField, this.registry, []);
|
|
1963
2102
|
const results = await query;
|
|
1964
2103
|
const resultMap = /* @__PURE__ */ new Map();
|
|
1965
|
-
const parentIdSet = new Set(parsedParentIds.map(String));
|
|
1966
2104
|
for (const row of results) {
|
|
1967
2105
|
const targetRow = row[getTableName$1(targetCollection)] || row;
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
if (
|
|
2106
|
+
if (!hasForeignKeyOnTarget(relation)) continue;
|
|
2107
|
+
const foreignKeyValue = targetRow[relation.foreignKeyOnTarget];
|
|
2108
|
+
if (foreignKeyValue === void 0 || foreignKeyValue === null) continue;
|
|
2109
|
+
const parentId = parentIdByKey.get(String(foreignKeyValue));
|
|
2110
|
+
if (parentId !== void 0) resultMap.set(String(parentId), await this.toRelatedRow(targetRow, targetCollection, targetPks));
|
|
1971
2111
|
}
|
|
1972
2112
|
return resultMap;
|
|
1973
2113
|
}
|
|
@@ -2045,17 +2185,24 @@ var RelationService = class {
|
|
|
2045
2185
|
}
|
|
2046
2186
|
return resultMap;
|
|
2047
2187
|
}
|
|
2048
|
-
this.assertSingleKeyAddressable(parentCollection, parentPks, hasForeignKeyOnTarget(relation) ? relation.foreignKeyOnTarget : relation.relationName);
|
|
2188
|
+
this.assertSingleKeyAddressable(parentCollection, parentPks, hasForeignKeyOnTarget(relation) ? relation.foreignKeyOnTarget : relation.relationName, relation);
|
|
2189
|
+
const { keyByParentId, parentIdByKey } = hasForeignKeyOnTarget(relation) ? await this.resolveSourceKeys(parentCollection, relation, parentIds) : {
|
|
2190
|
+
keyByParentId: /* @__PURE__ */ new Map(),
|
|
2191
|
+
parentIdByKey: /* @__PURE__ */ new Map()
|
|
2192
|
+
};
|
|
2193
|
+
const matchValues = hasForeignKeyOnTarget(relation) ? [...keyByParentId.values()] : parsedParentIds;
|
|
2194
|
+
if (matchValues.length === 0) return /* @__PURE__ */ new Map();
|
|
2049
2195
|
let query = this.db.select().from(targetTable).$dynamic();
|
|
2050
|
-
query = applyDynamicRelationQuery(query, query, relation,
|
|
2196
|
+
query = applyDynamicRelationQuery(query, query, relation, matchValues, targetTable, parentTable, parentIdCol, targetIdField, this.registry, []);
|
|
2051
2197
|
const results = await query;
|
|
2052
2198
|
const resultMap = /* @__PURE__ */ new Map();
|
|
2053
|
-
const parentIdSet = new Set(parsedParentIds.map(String));
|
|
2054
2199
|
for (const row of results) {
|
|
2055
2200
|
const targetRow = row[getTableName$1(targetCollection)] || row;
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
if (
|
|
2201
|
+
if (!hasForeignKeyOnTarget(relation)) continue;
|
|
2202
|
+
const foreignKeyValue = targetRow[relation.foreignKeyOnTarget];
|
|
2203
|
+
if (foreignKeyValue === void 0 || foreignKeyValue === null) continue;
|
|
2204
|
+
const parentId = parentIdByKey.get(String(foreignKeyValue));
|
|
2205
|
+
if (parentId !== void 0) {
|
|
2059
2206
|
const key = String(parentId);
|
|
2060
2207
|
const arr = resultMap.get(key) || [];
|
|
2061
2208
|
arr.push(await this.toRelatedRow(targetRow, targetCollection, targetPks));
|
|
@@ -2154,14 +2301,13 @@ var RelationService = class {
|
|
|
2154
2301
|
logger.warn(`Invalid inverse-many config for relation '${key}' in collection '${collection.slug}'`);
|
|
2155
2302
|
continue;
|
|
2156
2303
|
}
|
|
2157
|
-
const
|
|
2158
|
-
|
|
2159
|
-
const parsedParentId = parseIdValues(id, parentPks)[parentIdInfo.fieldName];
|
|
2304
|
+
const parentKeyValue = (await this.resolveSourceKeys(collection, relation, [id], tx)).keyByParentId.get(String(id));
|
|
2305
|
+
if (parentKeyValue === void 0) throw new Error(`Cannot write relation '${key}' on '${collection.slug}': row '${id}' has no value in \`sourceKey: "${sourceKeyField(relation, collection, this.registry)}"\`, so there is nothing for the related rows to point at.`);
|
|
2160
2306
|
if (targetEntityIds.length > 0) {
|
|
2161
2307
|
const parsedTargetIds = targetEntityIds.map((id) => parseIdValues(id, targetPks)[targetIdInfo.fieldName]);
|
|
2162
|
-
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: null }).where(and(eq(fkCol,
|
|
2163
|
-
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]:
|
|
2164
|
-
} else await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: null }).where(eq(fkCol,
|
|
2308
|
+
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: null }).where(and(eq(fkCol, parentKeyValue), sql`${targetIdCol} NOT IN (${sql.join(parsedTargetIds)})`));
|
|
2309
|
+
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: parentKeyValue }).where(inArray(targetIdCol, parsedTargetIds));
|
|
2310
|
+
} else await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: null }).where(eq(fkCol, parentKeyValue));
|
|
2165
2311
|
} else logger.warn(`Many relation '${key}' in collection '${collection.slug}' lacks write configuration and will be skipped during save.`);
|
|
2166
2312
|
}
|
|
2167
2313
|
}
|
|
@@ -2176,8 +2322,7 @@ var RelationService = class {
|
|
|
2176
2322
|
const targetTable = getTableForCollection(targetCollection, this.registry);
|
|
2177
2323
|
const targetPks = requirePrimaryKeys(targetCollection, this.registry);
|
|
2178
2324
|
const targetIdInfo = targetPks[0];
|
|
2179
|
-
|
|
2180
|
-
const sourceIdInfo = sourcePks[0];
|
|
2325
|
+
requirePrimaryKeys(sourceCollection, this.registry)[0];
|
|
2181
2326
|
if (relation.kind === "via") {
|
|
2182
2327
|
await this.updateInverseJoinPathRelation(tx, sourceCollection, sourceEntityId, targetCollection, relation, newValue);
|
|
2183
2328
|
continue;
|
|
@@ -2199,13 +2344,14 @@ var RelationService = class {
|
|
|
2199
2344
|
logger.warn(`Foreign key column '${relation.foreignKeyOnTarget}' not found in target table for relation '${relation.relationName}'`);
|
|
2200
2345
|
continue;
|
|
2201
2346
|
}
|
|
2202
|
-
const
|
|
2203
|
-
if (
|
|
2347
|
+
const sourceKeyValue = (await this.resolveSourceKeys(sourceCollection, relation, [sourceEntityId], tx)).keyByParentId.get(String(sourceEntityId));
|
|
2348
|
+
if (sourceKeyValue === void 0) throw new Error(`Cannot write relation '${relation.relationName}' on '${sourceCollection.slug}': row '${sourceEntityId}' has no value in \`sourceKey: "${sourceKeyField(relation, sourceCollection, this.registry)}"\`, so there is nothing for the related row to point at.`);
|
|
2349
|
+
if (newValue === null || newValue === void 0) await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: null }).where(eq(foreignKeyColumn, sourceKeyValue));
|
|
2204
2350
|
else {
|
|
2205
2351
|
const parsedNewTargetId = parseIdValues(newValue, targetPks)[targetIdInfo.fieldName];
|
|
2206
2352
|
const targetIdField = targetTable[targetIdInfo.fieldName];
|
|
2207
|
-
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: null }).where(eq(foreignKeyColumn,
|
|
2208
|
-
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]:
|
|
2353
|
+
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: null }).where(eq(foreignKeyColumn, sourceKeyValue));
|
|
2354
|
+
await tx.update(targetTable).set({ [relation.foreignKeyOnTarget]: sourceKeyValue }).where(eq(targetIdField, parsedNewTargetId));
|
|
2209
2355
|
}
|
|
2210
2356
|
} catch (e) {
|
|
2211
2357
|
logger.warn(`Failed to update inverse relation '${relation.relationName}'`, { error: e });
|
|
@@ -2511,7 +2657,7 @@ function relationTargetAddress(targetRow, targetCollection, registry) {
|
|
|
2511
2657
|
* expects real types. The row's own address is *not* among the columns — it is
|
|
2512
2658
|
* derived by the consumer from the collection's primary keys.
|
|
2513
2659
|
*/
|
|
2514
|
-
function
|
|
2660
|
+
function toFlatRow(row, collection, registry) {
|
|
2515
2661
|
const resolvedRelations = resolveCollectionRelations(collection);
|
|
2516
2662
|
const normalized = normalizeDbValues(row, collection);
|
|
2517
2663
|
for (const [key, relation] of Object.entries(resolvedRelations)) {
|
|
@@ -2708,17 +2854,35 @@ var FetchService = class {
|
|
|
2708
2854
|
const columnAt = (key) => (key in table ? table[key] : void 0) || void 0;
|
|
2709
2855
|
const direct = columnAt(orderBy);
|
|
2710
2856
|
if (direct) return direct;
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
if (foreignKey) return foreignKey;
|
|
2716
|
-
}
|
|
2857
|
+
const declaredRelation = collection ? resolveCollectionRelations(collection)[orderBy] : void 0;
|
|
2858
|
+
if (declaredRelation?.kind === "belongsTo") {
|
|
2859
|
+
const foreignKey = columnAt(declaredRelation.localKey);
|
|
2860
|
+
if (foreignKey) return foreignKey;
|
|
2717
2861
|
}
|
|
2718
2862
|
for (const guess of [`${orderBy}_id`, generateForeignKeyName(orderBy)]) {
|
|
2719
2863
|
const foreignKey = columnAt(guess);
|
|
2720
2864
|
if (foreignKey) return foreignKey;
|
|
2721
2865
|
}
|
|
2866
|
+
const collectionName = collection?.slug ?? collection?.name;
|
|
2867
|
+
const onCollection = collectionName ? ` on collection '${collectionName}'` : "";
|
|
2868
|
+
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", {
|
|
2869
|
+
field: orderBy,
|
|
2870
|
+
kind: declaredRelation.kind,
|
|
2871
|
+
...collectionName && { collection: collectionName }
|
|
2872
|
+
});
|
|
2873
|
+
if (getUnknownFilterFieldsMode() === "warn") {
|
|
2874
|
+
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.`);
|
|
2875
|
+
return;
|
|
2876
|
+
}
|
|
2877
|
+
let validFields = [];
|
|
2878
|
+
try {
|
|
2879
|
+
validFields = Object.keys(getTableColumns(table)).sort();
|
|
2880
|
+
} catch {}
|
|
2881
|
+
throw ApiError.badRequest(`Unknown orderBy field '${orderBy}'${onCollection}` + (validFields.length > 0 ? `. Valid fields: ${validFields.join(", ")}` : ""), "UNKNOWN_ORDER_BY_FIELD", {
|
|
2882
|
+
field: orderBy,
|
|
2883
|
+
...collectionName && { collection: collectionName },
|
|
2884
|
+
...validFields.length > 0 && { validFields }
|
|
2885
|
+
});
|
|
2722
2886
|
}
|
|
2723
2887
|
/**
|
|
2724
2888
|
* Build the `with` config for Drizzle's relational query API.
|
|
@@ -2932,7 +3096,7 @@ var FetchService = class {
|
|
|
2932
3096
|
with: withConfig
|
|
2933
3097
|
});
|
|
2934
3098
|
if (!row) return void 0;
|
|
2935
|
-
const flatRow =
|
|
3099
|
+
const flatRow = toFlatRow(row, collection, this.registry);
|
|
2936
3100
|
await this.resolveJoinPathRelations(flatRow, collection, collectionPath, parsedId, databaseId);
|
|
2937
3101
|
return flatRow;
|
|
2938
3102
|
} catch (e) {
|
|
@@ -2987,7 +3151,7 @@ var FetchService = class {
|
|
|
2987
3151
|
const hasRelations = withConfig && Object.keys(withConfig).length > 0;
|
|
2988
3152
|
if (qb && !options.searchString && !hasRelations && !options.vectorSearch) try {
|
|
2989
3153
|
const queryOpts = this.buildDrizzleQueryOptions(table, idField, idInfo, options, collectionPath, void 0, scopeCondition);
|
|
2990
|
-
return (await qb.findMany(queryOpts)).map((row) =>
|
|
3154
|
+
return (await qb.findMany(queryOpts)).map((row) => toFlatRow(row, collection, this.registry));
|
|
2991
3155
|
} catch (e) {
|
|
2992
3156
|
if (e instanceof Error && e.message.includes("not enough information to infer relation")) {
|
|
2993
3157
|
logger.error(`[FetchService] ResolvedRelation inference error for collection '${collectionPath}': ${e.message}`);
|
|
@@ -3050,7 +3214,7 @@ var FetchService = class {
|
|
|
3050
3214
|
/**
|
|
3051
3215
|
* Fallback path used when db.query is unavailable.
|
|
3052
3216
|
*
|
|
3053
|
-
* The primary path runs the results through `
|
|
3217
|
+
* The primary path runs the results through `toFlatRow`, which maps
|
|
3054
3218
|
* relations from what drizzle already nested — no query per row. This one
|
|
3055
3219
|
* has no nesting to read, so it resolves relations itself, in batches.
|
|
3056
3220
|
*
|
|
@@ -3143,6 +3307,10 @@ var FetchService = class {
|
|
|
3143
3307
|
const filterConditions = this.buildFilterConditions(options.filter, table, effectivePath);
|
|
3144
3308
|
if (filterConditions.length > 0) allConditions.push(...filterConditions);
|
|
3145
3309
|
}
|
|
3310
|
+
if (options.logical) {
|
|
3311
|
+
const logicalCondition = DrizzleConditionBuilder.buildLogicalConditions(options.logical, table, effectivePath, this.filterContext(effectivePath, table));
|
|
3312
|
+
if (logicalCondition) allConditions.push(logicalCondition);
|
|
3313
|
+
}
|
|
3146
3314
|
if (allConditions.length > 0) {
|
|
3147
3315
|
const finalCondition = DrizzleConditionBuilder.combineConditionsWithAnd(allConditions);
|
|
3148
3316
|
if (finalCondition) query = query.where(finalCondition);
|
|
@@ -3774,10 +3942,11 @@ var PersistService = class {
|
|
|
3774
3942
|
} else {
|
|
3775
3943
|
const targetColumnName = this.resolveParentForeignKeyColumn(hop);
|
|
3776
3944
|
if (targetColumnName) {
|
|
3777
|
-
const
|
|
3945
|
+
const parentKeyValue = hasForeignKeyOnTarget(hop.relation) ? await this.relationService.parentKeyValue(hop.parentCollection, hop.relation, hop.parentId) : parentIdForWrite();
|
|
3946
|
+
if (parentKeyValue === void 0) throw ApiError.badRequest(`Cannot create under "${collectionPath}": the parent row has no value in \`sourceKey\`, so the new row has nothing to point at.`);
|
|
3778
3947
|
const existingValue = effectiveValues[targetColumnName];
|
|
3779
|
-
if (existingValue !== void 0 && existingValue !== null && existingValue !==
|
|
3780
|
-
effectiveValues[targetColumnName] =
|
|
3948
|
+
if (existingValue !== void 0 && existingValue !== null && existingValue !== parentKeyValue) logger.warn(`Overriding provided value '${existingValue}' for FK '${targetColumnName}' with path parent key '${parentKeyValue}'.`);
|
|
3949
|
+
effectiveValues[targetColumnName] = parentKeyValue;
|
|
3781
3950
|
}
|
|
3782
3951
|
}
|
|
3783
3952
|
}
|
|
@@ -5055,9 +5224,10 @@ var PostgresBackendDriver = class PostgresBackendDriver {
|
|
|
5055
5224
|
async checkUniqueField(path, name, value, id, collection) {
|
|
5056
5225
|
return this.dataService.checkUniqueField(path, name, value, id, collection?.databaseId);
|
|
5057
5226
|
}
|
|
5058
|
-
async count({ path, collection, filter, searchString }) {
|
|
5227
|
+
async count({ path, collection, filter, logical, searchString }) {
|
|
5059
5228
|
return this.dataService.count(path, {
|
|
5060
5229
|
filter,
|
|
5230
|
+
logical,
|
|
5061
5231
|
searchString
|
|
5062
5232
|
});
|
|
5063
5233
|
}
|
|
@@ -5426,7 +5596,7 @@ var DatabasePoolManager = class {
|
|
|
5426
5596
|
const url = new URL(this.rootConnectionString);
|
|
5427
5597
|
url.pathname = `/${databaseName}`;
|
|
5428
5598
|
const pool = new Pool({
|
|
5429
|
-
connectionString: url.toString(),
|
|
5599
|
+
connectionString: pinSearchPath(url.toString()),
|
|
5430
5600
|
max: 10,
|
|
5431
5601
|
idleTimeoutMillis: 1e4,
|
|
5432
5602
|
allowExitOnIdle: true
|
|
@@ -6014,8 +6184,7 @@ var generateSchema = async (collections, stripPolicies = false) => {
|
|
|
6014
6184
|
const tableVarName = getTableVarName(tableName);
|
|
6015
6185
|
if (isJunction && relation && sourceCollection && isManyToMany(relation)) {
|
|
6016
6186
|
const targetCollection = relation.target();
|
|
6017
|
-
const
|
|
6018
|
-
const tableCreator = schema ? `${schema}Schema.table` : "pgTable";
|
|
6187
|
+
const tableCreator = "pgTable";
|
|
6019
6188
|
const baseTableName = tableName.includes(".") ? tableName.split(".").pop() : tableName;
|
|
6020
6189
|
const { sourceColumn, targetColumn } = relation.through;
|
|
6021
6190
|
const refOptions = `{ onDelete: \"${relation.onDelete ?? "cascade"}\" }`;
|
|
@@ -6129,8 +6298,9 @@ var generateSchema = async (collections, stripPolicies = false) => {
|
|
|
6129
6298
|
if (!emittedRelationNames.has(deduplicationKey)) {
|
|
6130
6299
|
const otherTableVar = getTableVarName(getTableName$1(otherCollection));
|
|
6131
6300
|
const drizzleFieldKey = resolvePropertyKeyForColumn(collection, otherRel.foreignKeyOnTarget);
|
|
6301
|
+
const referencedKey = otherRel.sourceKey ? resolvePropertyKeyForColumn(otherCollection, otherRel.sourceKey) : getPrimaryKeyName(otherCollection);
|
|
6132
6302
|
const synthKey = `_synth_${otherTableVar}_${drizzleFieldKey}`;
|
|
6133
|
-
tableRelations.push(` "${synthKey}": one(${otherTableVar}, {\n fields: [${tableVarName}.${drizzleFieldKey}],\n references: [${otherTableVar}.${
|
|
6303
|
+
tableRelations.push(` "${synthKey}": one(${otherTableVar}, {\n fields: [${tableVarName}.${drizzleFieldKey}],\n references: [${otherTableVar}.${referencedKey}],\n relationName: \"${drizzleRelationName}\"\n })`);
|
|
6134
6304
|
emittedRelationNames.add(deduplicationKey);
|
|
6135
6305
|
}
|
|
6136
6306
|
}
|
|
@@ -7288,14 +7458,6 @@ function resolveChannelBusSetting(configured) {
|
|
|
7288
7458
|
return raw === "memory" ? { type: "memory" } : { type: "postgres" };
|
|
7289
7459
|
}
|
|
7290
7460
|
/**
|
|
7291
|
-
* @deprecated Use {@link resolveChannelBusSetting}, which also accepts a
|
|
7292
|
-
* supplied {@link ChannelBus} instance. Kept as a narrow alias so existing
|
|
7293
|
-
* config-only callers keep their exact types.
|
|
7294
|
-
*/
|
|
7295
|
-
function resolveChannelBusConfig(configured) {
|
|
7296
|
-
return resolveChannelBusSetting(configured);
|
|
7297
|
-
}
|
|
7298
|
-
/**
|
|
7299
7461
|
* Produce the bus a setting asks for.
|
|
7300
7462
|
*
|
|
7301
7463
|
* An instance is handed straight back — constructing it was the application's
|
|
@@ -7772,7 +7934,7 @@ var RealtimeService = class RealtimeService extends EventEmitter {
|
|
|
7772
7934
|
if (this.driver) {
|
|
7773
7935
|
const collection = this.registry.getCollectionByPath(notifyPath);
|
|
7774
7936
|
const activeAuth = authContext || {
|
|
7775
|
-
uid: "
|
|
7937
|
+
uid: "anonymous",
|
|
7776
7938
|
roles: ["anon"]
|
|
7777
7939
|
};
|
|
7778
7940
|
return await this.db.transaction(async (tx) => {
|
|
@@ -7901,7 +8063,7 @@ var RealtimeService = class RealtimeService extends EventEmitter {
|
|
|
7901
8063
|
if (this.driver) {
|
|
7902
8064
|
const collection = this.registry.getCollectionByPath(notifyPath);
|
|
7903
8065
|
const activeAuth = authContext || {
|
|
7904
|
-
uid: "
|
|
8066
|
+
uid: "anonymous",
|
|
7905
8067
|
roles: ["anon"]
|
|
7906
8068
|
};
|
|
7907
8069
|
return await this.db.transaction(async (tx) => {
|
|
@@ -8906,7 +9068,7 @@ function createBackupCron(config) {
|
|
|
8906
9068
|
enabled: config.enabled ?? true,
|
|
8907
9069
|
timeoutSeconds: 3600,
|
|
8908
9070
|
async handler({ log }) {
|
|
8909
|
-
const { createDump, pruneBackups, uploadBackup, validateDump } = await import("./backup-service-
|
|
9071
|
+
const { createDump, pruneBackups, uploadBackup, validateDump } = await import("./backup-service-CD8o_1Sl.js").then((n) => n.r);
|
|
8910
9072
|
const { destination } = config;
|
|
8911
9073
|
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
9074
|
log(`Starting backup of "${dbName}"…`);
|
|
@@ -9037,6 +9199,11 @@ function findRelationDefects(collections, registry) {
|
|
|
9037
9199
|
problem: `\`foreignKeyOnTarget: "${relation.foreignKeyOnTarget}"\` is not a column on the target table \`${targetTableName}\``,
|
|
9038
9200
|
fix: `add the column, or set \`foreignKeyOnTarget\` to one of: ${quote(targetColumns)}`
|
|
9039
9201
|
});
|
|
9202
|
+
if (relation.sourceKey && !sourceColumns.has(relation.sourceKey)) defects.push({
|
|
9203
|
+
...at,
|
|
9204
|
+
problem: `\`sourceKey: "${relation.sourceKey}"\` is not a column on \`${sourceTableName}\``,
|
|
9205
|
+
fix: targetColumns.has(relation.sourceKey) ? `it is a column on the *target* table \`${targetTableName}\` — \`sourceKey\` names the column on this collection that the target's foreign key points at, so it must be one of: ${quote(sourceColumns)}` : `add the column, or set \`sourceKey\` to one of: ${quote(sourceColumns)}`
|
|
9206
|
+
});
|
|
9040
9207
|
break;
|
|
9041
9208
|
case "manyToMany": {
|
|
9042
9209
|
const { table, sourceColumn, targetColumn } = relation.through;
|
|
@@ -9788,21 +9955,6 @@ async function ensureAuthTablesExist(db, collection) {
|
|
|
9788
9955
|
}
|
|
9789
9956
|
//#endregion
|
|
9790
9957
|
//#region src/auth/services.ts
|
|
9791
|
-
function getColumnKey(table, ...keys) {
|
|
9792
|
-
if (!table) return void 0;
|
|
9793
|
-
for (const key of keys) {
|
|
9794
|
-
if (key in table) return key;
|
|
9795
|
-
const snake = toSnakeCase(key);
|
|
9796
|
-
if (snake in table) return snake;
|
|
9797
|
-
const camel = camelCase(key);
|
|
9798
|
-
if (camel in table) return camel;
|
|
9799
|
-
}
|
|
9800
|
-
}
|
|
9801
|
-
function getColumn(table, ...keys) {
|
|
9802
|
-
if (!table) return void 0;
|
|
9803
|
-
const key = getColumnKey(table, ...keys);
|
|
9804
|
-
return key ? table[key] : void 0;
|
|
9805
|
-
}
|
|
9806
9958
|
/**
|
|
9807
9959
|
* The single definition of what an email address looks like in storage.
|
|
9808
9960
|
*
|
|
@@ -9817,9 +9969,25 @@ function getColumn(table, ...keys) {
|
|
|
9817
9969
|
*
|
|
9818
9970
|
* Whitespace goes too: a trailing space survives the fold and reproduces the
|
|
9819
9971
|
* problem exactly.
|
|
9972
|
+
*
|
|
9973
|
+
* Re-exported rather than defined here: `@rebasepro/server` and
|
|
9974
|
+
* `@rebasepro/server-mongo` write this column too, and a second copy of this
|
|
9975
|
+
* rule is the defect it exists to prevent.
|
|
9820
9976
|
*/
|
|
9821
|
-
function
|
|
9822
|
-
|
|
9977
|
+
function getColumnKey(table, ...keys) {
|
|
9978
|
+
if (!table) return void 0;
|
|
9979
|
+
for (const key of keys) {
|
|
9980
|
+
if (key in table) return key;
|
|
9981
|
+
const snake = toSnakeCase(key);
|
|
9982
|
+
if (snake in table) return snake;
|
|
9983
|
+
const camel = camelCase(key);
|
|
9984
|
+
if (camel in table) return camel;
|
|
9985
|
+
}
|
|
9986
|
+
}
|
|
9987
|
+
function getColumn(table, ...keys) {
|
|
9988
|
+
if (!table) return void 0;
|
|
9989
|
+
const key = getColumnKey(table, ...keys);
|
|
9990
|
+
return key ? table[key] : void 0;
|
|
9823
9991
|
}
|
|
9824
9992
|
/**
|
|
9825
9993
|
* PostgreSQL implementation of UserRepository.
|
|
@@ -11160,6 +11328,31 @@ function humanize(snakeName) {
|
|
|
11160
11328
|
return snakeName.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
|
|
11161
11329
|
}
|
|
11162
11330
|
//#endregion
|
|
11331
|
+
//#region src/schema/introspect-db-types.ts
|
|
11332
|
+
/**
|
|
11333
|
+
* The PostgreSQL type → Rebase property type mapping.
|
|
11334
|
+
*
|
|
11335
|
+
* Split out of `introspect-db-logic` so that the structural analysis can use it
|
|
11336
|
+
* without importing the generator, which imports the analysis. Re-exported from
|
|
11337
|
+
* `introspect-db-logic` so existing callers keep their import path.
|
|
11338
|
+
*/
|
|
11339
|
+
/**
|
|
11340
|
+
* Map a PostgreSQL data type to a Rebase property type.
|
|
11341
|
+
*/
|
|
11342
|
+
function mapPgType(dataType) {
|
|
11343
|
+
const dt = dataType.toLowerCase();
|
|
11344
|
+
if (dt === "interval") return "string";
|
|
11345
|
+
if (dt === "array" || dt.startsWith("_")) return "array";
|
|
11346
|
+
if (dt.includes("int") || dt.includes("numeric") || dt.includes("decimal") || dt.includes("serial") || dt === "real" || dt === "float4" || dt === "float8" || dt === "double precision" || dt === "money") return "number";
|
|
11347
|
+
if (dt.includes("bool")) return "boolean";
|
|
11348
|
+
if (dt.includes("time") || dt.includes("date")) return "date";
|
|
11349
|
+
if (dt === "json" || dt === "jsonb") return "map";
|
|
11350
|
+
if (dt === "bytea") return "binary";
|
|
11351
|
+
if (dt === "inet" || dt === "cidr" || dt === "macaddr" || dt === "macaddr8") return "string";
|
|
11352
|
+
if (dt === "uuid") return "string";
|
|
11353
|
+
return "string";
|
|
11354
|
+
}
|
|
11355
|
+
//#endregion
|
|
11163
11356
|
//#region src/schema/introspect-db-logic.ts
|
|
11164
11357
|
var IRREGULAR_SINGULARS = {
|
|
11165
11358
|
people: "person",
|
|
@@ -11175,6 +11368,36 @@ var IRREGULAR_SINGULARS = {
|
|
|
11175
11368
|
criteria: "criterion",
|
|
11176
11369
|
phenomena: "phenomenon"
|
|
11177
11370
|
};
|
|
11371
|
+
/**
|
|
11372
|
+
* Plurals in "-ves" whose singular really ends in f/fe, and which of the two.
|
|
11373
|
+
*
|
|
11374
|
+
* A blanket "-ves" -> "-f" rule gets `knives` -> `knif`, and mangles every
|
|
11375
|
+
* ordinary "-ive" noun that happens to be plural along with it: `archives` ->
|
|
11376
|
+
* `archif`, `objectives` -> `objectif`. The set of English words that genuinely
|
|
11377
|
+
* swap f/fe for ves is small and closed, so it is listed rather than guessed —
|
|
11378
|
+
* anything else ending in "ves" drops the trailing 's' like any other plural.
|
|
11379
|
+
*
|
|
11380
|
+
* Matched on the whole word, not on the suffix: `olives` ends in `lives`.
|
|
11381
|
+
*/
|
|
11382
|
+
var VES_SINGULAR_ENDINGS = {
|
|
11383
|
+
calves: "f",
|
|
11384
|
+
dwarves: "f",
|
|
11385
|
+
elves: "f",
|
|
11386
|
+
halves: "f",
|
|
11387
|
+
hooves: "f",
|
|
11388
|
+
leaves: "f",
|
|
11389
|
+
loaves: "f",
|
|
11390
|
+
scarves: "f",
|
|
11391
|
+
selves: "f",
|
|
11392
|
+
sheaves: "f",
|
|
11393
|
+
shelves: "f",
|
|
11394
|
+
thieves: "f",
|
|
11395
|
+
wharves: "f",
|
|
11396
|
+
wolves: "f",
|
|
11397
|
+
knives: "fe",
|
|
11398
|
+
lives: "fe",
|
|
11399
|
+
wives: "fe"
|
|
11400
|
+
};
|
|
11178
11401
|
/** Words ending in 's' that are already singular. */
|
|
11179
11402
|
var UNCOUNTABLE = /* @__PURE__ */ new Set([
|
|
11180
11403
|
"status",
|
|
@@ -11213,7 +11436,7 @@ function singularize(word) {
|
|
|
11213
11436
|
if (UNCOUNTABLE.has(lower)) return word;
|
|
11214
11437
|
if (lower.endsWith("ices") && lower.length > 5) return word.slice(0, -4) + "ex";
|
|
11215
11438
|
if (lower.endsWith("ies") && lower.length > 3) return word.slice(0, -3) + "y";
|
|
11216
|
-
if (lower
|
|
11439
|
+
if (VES_SINGULAR_ENDINGS[lower]) return word.slice(0, -3) + VES_SINGULAR_ENDINGS[lower];
|
|
11217
11440
|
if (lower.endsWith("ches") || lower.endsWith("shes") || lower.endsWith("sses") || lower.endsWith("xes") || lower.endsWith("zes")) return word.slice(0, -2);
|
|
11218
11441
|
if (lower.endsWith("ses") && !lower.endsWith("sses")) return word.slice(0, -1);
|
|
11219
11442
|
if (lower.endsWith("s") && !lower.endsWith("ss") && !lower.endsWith("us") && !lower.endsWith("is")) return word.slice(0, -1);
|
|
@@ -11234,22 +11457,6 @@ function getIconForTable(tableName) {
|
|
|
11234
11457
|
if (table.includes("comment") || table.includes("review") || table.includes("feedback")) return "MessageCircle";
|
|
11235
11458
|
return "Database";
|
|
11236
11459
|
}
|
|
11237
|
-
/**
|
|
11238
|
-
* Map a PostgreSQL data type to a Rebase property type.
|
|
11239
|
-
*/
|
|
11240
|
-
function mapPgType(dataType) {
|
|
11241
|
-
const dt = dataType.toLowerCase();
|
|
11242
|
-
if (dt === "interval") return "string";
|
|
11243
|
-
if (dt === "array" || dt.startsWith("_")) return "array";
|
|
11244
|
-
if (dt.includes("int") || dt.includes("numeric") || dt.includes("decimal") || dt.includes("serial") || dt === "real" || dt === "float4" || dt === "float8" || dt === "double precision" || dt === "money") return "number";
|
|
11245
|
-
if (dt.includes("bool")) return "boolean";
|
|
11246
|
-
if (dt.includes("time") || dt.includes("date")) return "date";
|
|
11247
|
-
if (dt === "json" || dt === "jsonb") return "map";
|
|
11248
|
-
if (dt === "bytea") return "binary";
|
|
11249
|
-
if (dt === "inet" || dt === "cidr" || dt === "macaddr" || dt === "macaddr8") return "string";
|
|
11250
|
-
if (dt === "uuid") return "string";
|
|
11251
|
-
return "string";
|
|
11252
|
-
}
|
|
11253
11460
|
function buildEnumMap(enumValues) {
|
|
11254
11461
|
const enumMap = /* @__PURE__ */ new Map();
|
|
11255
11462
|
for (const ev of enumValues) {
|
|
@@ -11269,6 +11476,20 @@ function buildTablesMap(tables, columns, pks, fks) {
|
|
|
11269
11476
|
});
|
|
11270
11477
|
return tablesMap;
|
|
11271
11478
|
}
|
|
11479
|
+
/**
|
|
11480
|
+
* Join tables, identified by column name.
|
|
11481
|
+
*
|
|
11482
|
+
* Superseded for the CLI by `classifyTables` in `./introspect-db-structure`,
|
|
11483
|
+
* which asks the database instead: two single-column keys, unique together, no
|
|
11484
|
+
* payload column, nothing referencing the table. This rule folds away
|
|
11485
|
+
* `northwind.order_details` — which has the key shape and carries unit price,
|
|
11486
|
+
* quantity and discount — because it recognises `id`, `created_at` and
|
|
11487
|
+
* `updated_at` by name and calls everything else a foreign key.
|
|
11488
|
+
*
|
|
11489
|
+
* Still used by `./introspect-runtime`, which builds collections in memory from
|
|
11490
|
+
* a narrower set of catalog queries and has no unique-constraint or row-count
|
|
11491
|
+
* data to reason with.
|
|
11492
|
+
*/
|
|
11272
11493
|
function identifyJoinTables(tablesMap) {
|
|
11273
11494
|
const joinTables = /* @__PURE__ */ new Set();
|
|
11274
11495
|
for (const [tableName, meta] of tablesMap.entries()) if (meta.fks.length === 2) {
|
|
@@ -11647,6 +11868,32 @@ function isEconnrefused(err) {
|
|
|
11647
11868
|
* Implements the `BackendBootstrapper` interface for PostgreSQL.
|
|
11648
11869
|
*/
|
|
11649
11870
|
/**
|
|
11871
|
+
* Which table name the boot-time drift check should look for, for one collection.
|
|
11872
|
+
*
|
|
11873
|
+
* A declared `table` IS the table name, not a hint to be second-guessed. This
|
|
11874
|
+
* used to ask the registry whether it had indexed the declared name and fall
|
|
11875
|
+
* back to the SLUG when it had not — but "the registry does not know this table"
|
|
11876
|
+
* is exactly the condition the drift check exists to report, so the fallback
|
|
11877
|
+
* fired precisely when it was most harmful.
|
|
11878
|
+
*
|
|
11879
|
+
* A collection with `slug: "usage-daily", table: "usage_daily"` was reported as
|
|
11880
|
+
* missing table `usage-daily`: a name that does not exist, should never exist,
|
|
11881
|
+
* and that nobody can find by looking. Worse, the remediation the caller prints
|
|
11882
|
+
* says to run `rebase db push` — which would then CREATE that invented table
|
|
11883
|
+
* beside the correct one, the same "second copy" hazard the misplaced-schema
|
|
11884
|
+
* branch further down exists to prevent. Seen in production, where a correctly
|
|
11885
|
+
* migrated database reported drift on every boot.
|
|
11886
|
+
*
|
|
11887
|
+
* The slug is used only when nothing was declared, which is the config shape
|
|
11888
|
+
* where the slug genuinely is the table name.
|
|
11889
|
+
*
|
|
11890
|
+
* Exported for its own test: the caller needs a live pool and a real database,
|
|
11891
|
+
* and this is the part that was wrong.
|
|
11892
|
+
*/
|
|
11893
|
+
function resolveDriftCheckName(col, registeredTableNames) {
|
|
11894
|
+
return (isRelationalCollectionConfig(col) ? col.table : void 0) ?? registeredTableNames.find((k) => k === col.slug) ?? col.slug;
|
|
11895
|
+
}
|
|
11896
|
+
/**
|
|
11650
11897
|
* Default PostgreSQL bootstrapper.
|
|
11651
11898
|
*
|
|
11652
11899
|
* Use it to register Postgres with `initializeRebaseBackend()`:
|
|
@@ -11735,7 +11982,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
11735
11982
|
let readDb;
|
|
11736
11983
|
const readUrl = process.env.DATABASE_READ_URL;
|
|
11737
11984
|
if (readUrl && readUrl !== pgConfig.connectionString) try {
|
|
11738
|
-
const { createReadReplicaConnection } = await import("./connection-
|
|
11985
|
+
const { createReadReplicaConnection } = await import("./connection-BuZ97wsr.js").then((n) => n.t);
|
|
11739
11986
|
readDb = createReadReplicaConnection(readUrl, mergedSchema).db;
|
|
11740
11987
|
logger.info("📖 [PostgresBootstrapper] Read replica connection established");
|
|
11741
11988
|
} catch (err) {
|
|
@@ -11837,39 +12084,59 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
11837
12084
|
try {
|
|
11838
12085
|
const registeredCollections = registry.getCollections();
|
|
11839
12086
|
if (registeredCollections.length > 0) {
|
|
11840
|
-
const schemasList = Array.from(new Set(registeredCollections.map((c) => "schema" in c && c.schema ? c.schema : "public"))).map((s) => `'${s}'`).join(",");
|
|
11841
12087
|
const result = await schemaAwareDb.execute(sql.raw(`
|
|
11842
12088
|
SELECT table_name, table_schema
|
|
11843
12089
|
FROM information_schema.tables
|
|
11844
|
-
WHERE table_schema IN (
|
|
12090
|
+
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
|
|
11845
12091
|
AND table_type = 'BASE TABLE'
|
|
11846
12092
|
`));
|
|
12093
|
+
const tablesByName = /* @__PURE__ */ new Map();
|
|
12094
|
+
for (const row of result.rows) {
|
|
12095
|
+
const schemas = tablesByName.get(row.table_name) ?? [];
|
|
12096
|
+
schemas.push(row.table_schema);
|
|
12097
|
+
tablesByName.set(row.table_name, schemas);
|
|
12098
|
+
}
|
|
11847
12099
|
const dbTables = new Set(result.rows.map((r) => r.table_schema === "public" ? r.table_name : `${r.table_schema}.${r.table_name}`));
|
|
11848
12100
|
const missing = [];
|
|
11849
12101
|
for (const col of registeredCollections) {
|
|
11850
12102
|
if (col.auth?.enabled) continue;
|
|
11851
12103
|
const schemaName = "schema" in col && col.schema ? col.schema : "public";
|
|
11852
|
-
const
|
|
11853
|
-
const tableName = registry.hasTableForCollection(declaredTable ?? col.slug) ? declaredTable ?? col.slug : col.slug;
|
|
11854
|
-
const checkName = registry.getTableNames().find((k) => k === tableName || k === col.slug) ?? tableName;
|
|
12104
|
+
const checkName = resolveDriftCheckName(col, registry.getTableNames());
|
|
11855
12105
|
const fullCheckName = schemaName === "public" ? checkName : `${schemaName}.${checkName}`;
|
|
11856
12106
|
if (!dbTables.has(fullCheckName)) missing.push({
|
|
11857
12107
|
slug: col.slug,
|
|
11858
|
-
table: fullCheckName
|
|
12108
|
+
table: fullCheckName,
|
|
12109
|
+
foundIn: (tablesByName.get(checkName) ?? []).filter((s) => s !== schemaName)
|
|
11859
12110
|
});
|
|
11860
12111
|
}
|
|
11861
12112
|
if (missing.length > 0) {
|
|
11862
|
-
const lines = missing.map((m) => ` • collection "${m.slug}" → table "${m.table}"`);
|
|
12113
|
+
const lines = missing.map((m) => ` • collection "${m.slug}" → table "${m.table}"` + (m.foundIn.length > 0 ? ` — but a table of that name exists in ${m.foundIn.map((s) => `"${s}"`).join(", ")}` : ""));
|
|
12114
|
+
const misplaced = missing.filter((m) => m.foundIn.length > 0);
|
|
12115
|
+
const misplacedHelp = misplaced.length === 0 ? [] : [
|
|
12116
|
+
" Those tables exist — in the wrong schema. Unqualified SQL run by a",
|
|
12117
|
+
" role whose name matches a schema (`rebase` is both, by default)",
|
|
12118
|
+
" resolves through search_path's \"$user\" and creates there. Move them:",
|
|
12119
|
+
...misplaced.map((m) => ` ALTER TABLE "${m.foundIn[0]}"."${m.table.split(".").pop()}" SET SCHEMA "${m.table.includes(".") ? m.table.split(".")[0] : "public"}";`),
|
|
12120
|
+
" and qualify the SQL that created them, or pin search_path in DATABASE_URL",
|
|
12121
|
+
" (`?options=-c%20search_path%3Dpublic`).",
|
|
12122
|
+
""
|
|
12123
|
+
];
|
|
11863
12124
|
logger.warn([
|
|
11864
12125
|
"",
|
|
11865
12126
|
"⚠️ SCHEMA DRIFT — the database is missing tables this backend serves:",
|
|
11866
12127
|
...lines,
|
|
11867
12128
|
"",
|
|
12129
|
+
...misplacedHelp,
|
|
11868
12130
|
" This runtime applies the collection schema at boot unless",
|
|
11869
12131
|
" REBASE_MIGRATE_ON_BOOT=none. Check the \"Collection schema\" / \"policies\"",
|
|
11870
12132
|
" log lines above — this drift means that step was off, skipped, or failed.",
|
|
11871
|
-
" • Managed cloud: redeploy
|
|
11872
|
-
"
|
|
12133
|
+
" • Managed cloud: redeploy with REBASE_MIGRATE_ON_BOOT unset or",
|
|
12134
|
+
" \"ensure\"; the runtime applies the schema to the tenant DB.",
|
|
12135
|
+
" If the log above says the driver does not implement collection-table",
|
|
12136
|
+
" creation, THIS driver is too old to do it. A driver is installed from",
|
|
12137
|
+
" your bundle's dependencies, not supplied by the platform image, so a",
|
|
12138
|
+
" newer runtime will not update it: bump \"@rebasepro/server-postgres\"",
|
|
12139
|
+
" in your project's package.json and redeploy.",
|
|
11873
12140
|
" • Self-host: run `rebase db push` (dev) or `rebase db migrate` (prod)",
|
|
11874
12141
|
" against DATABASE_URL.",
|
|
11875
12142
|
""
|
|
@@ -11958,7 +12225,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
11958
12225
|
*/
|
|
11959
12226
|
async ensureCollectionSchema(collections, driverResult, log) {
|
|
11960
12227
|
const internals = driverResult.internals;
|
|
11961
|
-
const { ensureCollectionTables } = await import("./ensure-collection-tables-
|
|
12228
|
+
const { ensureCollectionTables } = await import("./ensure-collection-tables-Da2oGkX2.js");
|
|
11962
12229
|
const plan = await ensureCollectionTables({ async query(text) {
|
|
11963
12230
|
const result = await internals.db.execute(sql.raw(text));
|
|
11964
12231
|
return { rows: result.rows ?? (Array.isArray(result) ? result : []) };
|
|
@@ -11983,7 +12250,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
11983
12250
|
*/
|
|
11984
12251
|
async ensureCollectionPolicies(collections, driverResult, log) {
|
|
11985
12252
|
const internals = driverResult.internals;
|
|
11986
|
-
const { ensureCollectionPolicies } = await import("./ensure-collection-policies-
|
|
12253
|
+
const { ensureCollectionPolicies } = await import("./ensure-collection-policies-BrUVgjz3.js");
|
|
11987
12254
|
const outcome = await ensureCollectionPolicies({ async query(text) {
|
|
11988
12255
|
const result = await internals.db.execute(sql.raw(text));
|
|
11989
12256
|
return { rows: result.rows ?? (Array.isArray(result) ? result : []) };
|
|
@@ -11997,7 +12264,7 @@ function createPostgresBootstrapper(pgConfig) {
|
|
|
11997
12264
|
},
|
|
11998
12265
|
mountRoutes(app, basePath, driverResult) {},
|
|
11999
12266
|
async initializeWebsockets(server, realtimeService, driver, config, adapter) {
|
|
12000
|
-
const { createPostgresWebSocket } = await import("./websocket-
|
|
12267
|
+
const { createPostgresWebSocket } = await import("./websocket-B2LsrINK.js").then((n) => n.n);
|
|
12001
12268
|
createPostgresWebSocket(server, realtimeService, driver, config, adapter);
|
|
12002
12269
|
}
|
|
12003
12270
|
};
|
|
@@ -12023,8 +12290,8 @@ function createPostgresAdapter(pgConfig) {
|
|
|
12023
12290
|
async initializeHistory(config, driverResult) {
|
|
12024
12291
|
if (bootstrapper.initializeHistory) return bootstrapper.initializeHistory(config, driverResult);
|
|
12025
12292
|
},
|
|
12026
|
-
initializeWebsockets(server, realtimeService, driver, config) {
|
|
12027
|
-
if (bootstrapper.initializeWebsockets) return bootstrapper.initializeWebsockets(server, realtimeService, driver, config);
|
|
12293
|
+
initializeWebsockets(server, realtimeService, driver, config, adapter) {
|
|
12294
|
+
if (bootstrapper.initializeWebsockets) return bootstrapper.initializeWebsockets(server, realtimeService, driver, config, adapter);
|
|
12028
12295
|
},
|
|
12029
12296
|
ensureCollectionSchema: bootstrapper.ensureCollectionSchema ? (collections, driverResult, log) => bootstrapper.ensureCollectionSchema(collections, driverResult, log) : void 0,
|
|
12030
12297
|
ensureCollectionPolicies: bootstrapper.ensureCollectionPolicies ? (collections, driverResult, log) => bootstrapper.ensureCollectionPolicies(collections, driverResult, log) : void 0,
|
|
@@ -12037,6 +12304,6 @@ function createPostgresAdapter(pgConfig) {
|
|
|
12037
12304
|
};
|
|
12038
12305
|
}
|
|
12039
12306
|
//#endregion
|
|
12040
|
-
export { AuthenticatedPostgresBackendDriver, BackupToolError, BranchService, CHANNEL_BUS_NOTIFY_CHANNEL, DEFAULT_BATCH_WINDOW_MS, DatabasePoolManager, DrizzleConditionBuilder, MemoryChannelBus, PG_NOTIFY_MAX_PAYLOAD_BYTES, PostgresBackendDriver, PostgresChannelBus, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, applyGlobals, backupCronConfigFromEnv, buildBackupFilename, buildPgDumpArgs, buildPgDumpallGlobalsArgs, buildPgRestoreArgs, buildPgRestoreListArgs, checkToolServerCompatibility, configureUnknownFilterFields, createAuthSchema, createBackupCron, createChannelBus, createDirectDatabaseConnection, createDump, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, detectToolMajor, ensureDatabaseExists, frameByteLength, generateSchema, getServerVersionMajor, getUnknownFilterFieldsMode, globalsFileForDump, guardPoolAgainstDirtyRelease, isChannelBusInstance, joinStorageKey, listBackups, magicLinkTokens, magicLinkTokensRelations, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, parseBackupDestination, parseBackupTimestamp, parseChannelBusFrame, parseChannelBusPayload, parseDbNameFromUrl, parsePgToolMajor, passwordResetTokens, passwordResetTokensRelations, preflight, pruneBackups, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations,
|
|
12307
|
+
export { AuthenticatedPostgresBackendDriver, BackupToolError, BranchService, CHANNEL_BUS_NOTIFY_CHANNEL, DEFAULT_BATCH_WINDOW_MS, DatabasePoolManager, DrizzleConditionBuilder, MemoryChannelBus, PG_NOTIFY_MAX_PAYLOAD_BYTES, PostgresBackendDriver, PostgresChannelBus, PostgresCollectionRegistry, PostgresConditionBuilder, PostgresRealtimeProvider, RealtimeService, appConfig, applyGlobals, backupCronConfigFromEnv, buildBackupFilename, buildPgDumpArgs, buildPgDumpallGlobalsArgs, buildPgRestoreArgs, buildPgRestoreListArgs, buildRowSecurityPgOptions, checkToolServerCompatibility, configureUnknownFilterFields, createAuthSchema, createBackupCron, createChannelBus, createDirectDatabaseConnection, createDump, createPostgresAdapter, createPostgresBootstrapper, createPostgresDatabaseConnection, createPostgresWebSocket, createReadReplicaConnection, detectToolMajor, diagnoseRowSecurityDumpFailure, ensureDatabaseExists, frameByteLength, generateSchema, getServerVersionMajor, getUnknownFilterFieldsMode, globalsFileForDump, guardPoolAgainstDirtyRelease, isChannelBusInstance, joinStorageKey, listBackups, magicLinkTokens, magicLinkTokensRelations, mfaChallenges, mfaChallengesRelations, mfaFactors, mfaFactorsRelations, parseBackupDestination, parseBackupTimestamp, parseChannelBusFrame, parseChannelBusPayload, parseDbNameFromUrl, parsePgToolMajor, passwordResetTokens, passwordResetTokensRelations, pinSearchPath, preflight, pruneBackups, recoveryCodes, recoveryCodesRelations, refreshTokens, refreshTokensRelations, resolveChannelBusSetting, resolveConnectionString, resolveDriftCheckName, resolvePgBinary, restoreDump, selectBackupsToPrune, serverVersionNumToMajor, splitGlobalsStatements, uploadBackup, userIdentities, userIdentitiesRelations, users, usersRelations, usersSchema, validateDump, withDatabaseName };
|
|
12041
12308
|
|
|
12042
12309
|
//# sourceMappingURL=index.es.js.map
|