@depup/typeorm-extension 4.0.0-depup.0 → 4.2.0-depup.0
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/README.MD +35 -0
- package/README.md +2 -2
- package/bin/cli.mjs +11 -11
- package/bin/cli.mjs.map +1 -1
- package/changes.json +1 -1
- package/dist/index.d.mts +265 -170
- package/dist/index.mjs +873 -69
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -20
package/dist/index.mjs
CHANGED
|
@@ -5,13 +5,27 @@ import fs from "node:fs";
|
|
|
5
5
|
import { Brackets, DataSource, InstanceChecker, MigrationExecutor, MssqlParameter, Repository, Table } from "typeorm";
|
|
6
6
|
import { oneOf, read as read$1, readArray, readBool, readInt, toArray, toBool } from "envix";
|
|
7
7
|
import { createMerger } from "smob";
|
|
8
|
+
import { PlatformTools } from "typeorm/platform/PlatformTools.js";
|
|
8
9
|
import { DriverFactory } from "typeorm/driver/DriverFactory.js";
|
|
9
|
-
import {
|
|
10
|
-
import process$1 from "node:process";
|
|
10
|
+
import { CommandUtils } from "typeorm/commands/CommandUtils.js";
|
|
11
11
|
import { MigrationGenerateCommand } from "typeorm/commands/MigrationGenerateCommand.js";
|
|
12
|
+
import { pascalCase } from "pascal-case";
|
|
12
13
|
//#region src/errors/base.ts
|
|
13
14
|
var TypeormExtensionError = class extends Error {};
|
|
14
15
|
//#endregion
|
|
16
|
+
//#region src/errors/database-lock.ts
|
|
17
|
+
var DatabaseLockError = class DatabaseLockError extends TypeormExtensionError {
|
|
18
|
+
static timeout(name, timeout) {
|
|
19
|
+
return new DatabaseLockError(`The database lock ${name} could not be acquired within ${timeout}ms.`);
|
|
20
|
+
}
|
|
21
|
+
static transactionActive(name) {
|
|
22
|
+
return new DatabaseLockError(`The database lock ${name} can not be taken inside a transaction, it would outlive a rollback.`);
|
|
23
|
+
}
|
|
24
|
+
static transactionLeftOpen(name) {
|
|
25
|
+
return new DatabaseLockError(`The callback of the database lock ${name} left a transaction open, which has been rolled back.`);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
//#endregion
|
|
15
29
|
//#region src/errors/driver.ts
|
|
16
30
|
var DriverError = class DriverError extends TypeormExtensionError {
|
|
17
31
|
constructor(message) {
|
|
@@ -26,6 +40,9 @@ var DriverError = class DriverError extends TypeormExtensionError {
|
|
|
26
40
|
static schemaAlterationNotSupported(driverName) {
|
|
27
41
|
return new DriverError(`Schema alterations are not supported for the driver ${driverName}.`);
|
|
28
42
|
}
|
|
43
|
+
static lockNotSupported(driverName) {
|
|
44
|
+
return new DriverError(`Database locks are not supported for the driver ${driverName}.`);
|
|
45
|
+
}
|
|
29
46
|
static columnConversionExpressionNotSupported(driverName) {
|
|
30
47
|
return new DriverError(`A conversion expression (\`using\`) can not be expressed for the driver ${driverName}, which would convert the values on its own terms instead.`);
|
|
31
48
|
}
|
|
@@ -92,6 +109,12 @@ var OptionsError = class OptionsError extends TypeormExtensionError {
|
|
|
92
109
|
static notFound() {
|
|
93
110
|
return new OptionsError("The database options could not be located/loaded.");
|
|
94
111
|
}
|
|
112
|
+
static timezoneUnsupported(timezone) {
|
|
113
|
+
return new OptionsError(`The database timezone ${String(timezone)} is not supported, only UTC is.`);
|
|
114
|
+
}
|
|
115
|
+
static timezoneConflict(detail) {
|
|
116
|
+
return new OptionsError(`The database timezone can not be pinned: ${detail}`);
|
|
117
|
+
}
|
|
95
118
|
static databaseNotDefined() {
|
|
96
119
|
return new OptionsError("The database name to connect to is not defined.");
|
|
97
120
|
}
|
|
@@ -925,6 +948,8 @@ let EnvironmentVariableName = /* @__PURE__ */ function(EnvironmentVariableName)
|
|
|
925
948
|
EnvironmentVariableName["MAX_QUERY_EXECUTION_TIME_ALT"] = "TYPEORM_MAX_QUERY_EXECUTION_TIME";
|
|
926
949
|
EnvironmentVariableName["DEBUG"] = "DB_DEBUG";
|
|
927
950
|
EnvironmentVariableName["DEBUG_ALT"] = "TYPEORM_DEBUG";
|
|
951
|
+
EnvironmentVariableName["PIN_TIMEZONE"] = "DB_PIN_TIMEZONE";
|
|
952
|
+
EnvironmentVariableName["PIN_TIMEZONE_ALT"] = "TYPEORM_PIN_TIMEZONE";
|
|
928
953
|
EnvironmentVariableName["UUID_EXTENSION"] = "DB_UUID_EXTENSION";
|
|
929
954
|
EnvironmentVariableName["UUID_EXTENSION_ALT"] = "TYPEORM_UUID_EXTENSION";
|
|
930
955
|
EnvironmentVariableName["CACHE"] = "DB_CACHE";
|
|
@@ -1062,7 +1087,8 @@ function useEnv(key) {
|
|
|
1062
1087
|
maxQueryExecutionTime: oneOf([readInt("DB_MAX_QUERY_EXECUTION_TIME"), readInt("TYPEORM_MAX_QUERY_EXECUTION_TIME")]),
|
|
1063
1088
|
debug: oneOf([read$1("DB_DEBUG"), read$1("TYPEORM_DEBUG")]),
|
|
1064
1089
|
cache: transformCache(oneOf([read$1("DB_CACHE"), read$1("TYPEORM_CACHE")])),
|
|
1065
|
-
uuidExtension: oneOf([read$1("DB_UUID_EXTENSION"), read$1("TYPEORM_UUID_EXTENSION")])
|
|
1090
|
+
uuidExtension: oneOf([read$1("DB_UUID_EXTENSION"), read$1("TYPEORM_UUID_EXTENSION")]),
|
|
1091
|
+
pinTimezone: oneOf([read$1("DB_PIN_TIMEZONE"), read$1("TYPEORM_PIN_TIMEZONE")])
|
|
1066
1092
|
};
|
|
1067
1093
|
if (output.extra) output.extra = JSON.parse(output.extra);
|
|
1068
1094
|
let type;
|
|
@@ -1093,12 +1119,726 @@ function mergeDataSourceOptions(target, source) {
|
|
|
1093
1119
|
return merge(target, source);
|
|
1094
1120
|
}
|
|
1095
1121
|
//#endregion
|
|
1122
|
+
//#region src/data-source/options/timezone/utils.ts
|
|
1123
|
+
function isDataSourceTimezone(input) {
|
|
1124
|
+
return typeof input === "string" && input.toUpperCase() === "UTC";
|
|
1125
|
+
}
|
|
1126
|
+
function isDate(input) {
|
|
1127
|
+
return input instanceof Date || Object.prototype.toString.call(input) === "[object Date]";
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Everything this module installs (wrapped drivers, clients, parsers,
|
|
1131
|
+
* session callbacks) is recorded here, so a second application recognises
|
|
1132
|
+
* the first one exactly instead of guessing from option values.
|
|
1133
|
+
*/
|
|
1134
|
+
const installed = /* @__PURE__ */ new WeakSet();
|
|
1135
|
+
function markInstalled(value) {
|
|
1136
|
+
installed.add(value);
|
|
1137
|
+
return value;
|
|
1138
|
+
}
|
|
1139
|
+
function isInstalled(value) {
|
|
1140
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && installed.has(value);
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* Whether the options carry a pin applied earlier, in any of the places a
|
|
1144
|
+
* dialect installs one.
|
|
1145
|
+
*/
|
|
1146
|
+
function hasInstalledTimezone(options) {
|
|
1147
|
+
if (isInstalled(options.driver)) return true;
|
|
1148
|
+
const extra = options.extra && typeof options.extra === "object" ? options.extra : {};
|
|
1149
|
+
return isInstalled(extra.types) || isInstalled(extra.Client) || isInstalled(extra.sessionCallback);
|
|
1150
|
+
}
|
|
1151
|
+
//#endregion
|
|
1152
|
+
//#region src/data-source/options/timezone/mysql.ts
|
|
1153
|
+
const MYSQL_SESSION_TIMEZONE_SQL = "SET time_zone = '+00:00'";
|
|
1154
|
+
/**
|
|
1155
|
+
* The mysql2 `timezone` values meaning UTC.
|
|
1156
|
+
*/
|
|
1157
|
+
const MYSQL_UTC_TIMEZONE = /^(?:z|[+-]00:00)$/i;
|
|
1158
|
+
/**
|
|
1159
|
+
* Wrap a `mysql2` module so every pool pins its connections to UTC. The pool
|
|
1160
|
+
* emits `connection` synchronously, before it hands a new connection out, and
|
|
1161
|
+
* a connection runs its queries in order, so the `SET` is the first statement
|
|
1162
|
+
* every connection executes. A connection whose `SET` fails is destroyed
|
|
1163
|
+
* rather than left running in another zone.
|
|
1164
|
+
*/
|
|
1165
|
+
function createMysqlUTCDriver(driver) {
|
|
1166
|
+
return markInstalled({
|
|
1167
|
+
...driver,
|
|
1168
|
+
createPool(...args) {
|
|
1169
|
+
const pool = driver.createPool(...args);
|
|
1170
|
+
pool.on("connection", (connection) => {
|
|
1171
|
+
connection.query(MYSQL_SESSION_TIMEZONE_SQL, (err) => {
|
|
1172
|
+
if (err) connection.destroy();
|
|
1173
|
+
});
|
|
1174
|
+
});
|
|
1175
|
+
return pool;
|
|
1176
|
+
}
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
function readSetting(options, key) {
|
|
1180
|
+
const extra = options.extra ?? {};
|
|
1181
|
+
return typeof extra[key] !== "undefined" ? extra[key] : options[key];
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Pin a mysql / mariadb data source: the session runs `SET time_zone` on
|
|
1185
|
+
* every new pool connection, and mysql2 reads and writes DATETIME and
|
|
1186
|
+
* TIMESTAMP values as UTC (`timezone: 'Z'`). DATE values are returned as
|
|
1187
|
+
* strings (`dateStrings: ['DATE']`): a calendar date has no instant, and
|
|
1188
|
+
* mysql2 would otherwise hand it over as UTC midnight, which typeorm then
|
|
1189
|
+
* formats back in the process zone, a day early west of UTC.
|
|
1190
|
+
*
|
|
1191
|
+
* A `timezone` naming UTC and `dateStrings: ['DATE']` are accepted as they
|
|
1192
|
+
* are. Another `timezone`, other `dateStrings`, a custom `typeCast` or a
|
|
1193
|
+
* replication setup (a pool cluster has no per-connection hook) is a
|
|
1194
|
+
* conflict, and so is a pin altered after it was applied.
|
|
1195
|
+
*/
|
|
1196
|
+
function applyMysqlTimezone(options) {
|
|
1197
|
+
if (isInstalled(options.driver)) {
|
|
1198
|
+
const timezone = readSetting(options, "timezone");
|
|
1199
|
+
const dateStrings = readSetting(options, "dateStrings");
|
|
1200
|
+
if (timezone === "Z" && Array.isArray(dateStrings) && dateStrings.length === 1 && dateStrings[0] === "DATE" && typeof readSetting(options, "typeCast") === "undefined") return options;
|
|
1201
|
+
throw OptionsError.timezoneConflict("the mysql pin was altered after it was applied.");
|
|
1202
|
+
}
|
|
1203
|
+
if (typeof options.replication !== "undefined") throw OptionsError.timezoneConflict("a mysql replication setup has no per-connection hook to pin the session with.");
|
|
1204
|
+
const timezone = readSetting(options, "timezone");
|
|
1205
|
+
if (typeof timezone !== "undefined" && !(typeof timezone === "string" && MYSQL_UTC_TIMEZONE.test(timezone))) throw OptionsError.timezoneConflict(`the mysql timezone is set to ${String(timezone)}.`);
|
|
1206
|
+
const dateStrings = readSetting(options, "dateStrings");
|
|
1207
|
+
if (typeof dateStrings !== "undefined" && dateStrings !== false && !(Array.isArray(dateStrings) && dateStrings.every((type) => type === "DATE"))) throw OptionsError.timezoneConflict("mysql dateStrings would hand DATETIME or TIMESTAMP values over as zone-less strings.");
|
|
1208
|
+
if (typeof readSetting(options, "typeCast") !== "undefined") throw OptionsError.timezoneConflict("a custom mysql typeCast decides how date values are read.");
|
|
1209
|
+
const driver = options.driver ?? PlatformTools.load("mysql2");
|
|
1210
|
+
const extra = { ...options.extra ?? {} };
|
|
1211
|
+
delete extra.timezone;
|
|
1212
|
+
delete extra.dateStrings;
|
|
1213
|
+
return {
|
|
1214
|
+
...options,
|
|
1215
|
+
extra,
|
|
1216
|
+
timezone: "Z",
|
|
1217
|
+
dateStrings: ["DATE"],
|
|
1218
|
+
driver: createMysqlUTCDriver(driver)
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
//#endregion
|
|
1222
|
+
//#region src/data-source/options/timezone/oracle.ts
|
|
1223
|
+
const ORACLE_SESSION_TIMEZONE_SQL = "ALTER SESSION SET TIME_ZONE = '+00:00'";
|
|
1224
|
+
/**
|
|
1225
|
+
* Re-read the local fields of a Date as UTC. node-oracledb builds a zone-less
|
|
1226
|
+
* `TIMESTAMP` as a local Date from its fields, and offers no switch for it.
|
|
1227
|
+
*/
|
|
1228
|
+
function readLocalDateAsUTC(date) {
|
|
1229
|
+
const output = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), date.getMilliseconds()));
|
|
1230
|
+
const year = date.getFullYear();
|
|
1231
|
+
if (year >= 0 && year < 100) output.setUTCFullYear(year);
|
|
1232
|
+
return output;
|
|
1233
|
+
}
|
|
1234
|
+
/**
|
|
1235
|
+
* The reverse: a local Date whose fields are the UTC fields of the given one.
|
|
1236
|
+
* node-oracledb binds a Date as a zone-less `TIMESTAMP` from its local
|
|
1237
|
+
* fields, so this sends the UTC wall clock with the column's own type, which
|
|
1238
|
+
* keeps an index on the column usable (a `TIMESTAMP WITH TIME ZONE` bind
|
|
1239
|
+
* would convert the column side of every comparison).
|
|
1240
|
+
*/
|
|
1241
|
+
function writeUTCAsLocalDate(date) {
|
|
1242
|
+
const output = new Date(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), date.getUTCHours(), date.getUTCMinutes(), date.getUTCSeconds(), date.getUTCMilliseconds());
|
|
1243
|
+
const year = date.getUTCFullYear();
|
|
1244
|
+
if (year >= 0 && year < 100) output.setFullYear(year);
|
|
1245
|
+
return output;
|
|
1246
|
+
}
|
|
1247
|
+
/**
|
|
1248
|
+
* Proxy an object, replacing the named methods. Every function is bound to
|
|
1249
|
+
* the object itself, so its internals never re-enter the proxy, and cached,
|
|
1250
|
+
* so reading it twice gives the same function. A member carrying a
|
|
1251
|
+
* `prototype` which is not replaced (an exported class) passes through
|
|
1252
|
+
* untouched, which keeps `instanceof` and statics working.
|
|
1253
|
+
*/
|
|
1254
|
+
function proxyMethods(target, methods) {
|
|
1255
|
+
const cache = /* @__PURE__ */ new Map();
|
|
1256
|
+
return new Proxy(target, { get(object, property) {
|
|
1257
|
+
const value = Reflect.get(object, property, object);
|
|
1258
|
+
if (typeof value !== "function") return value;
|
|
1259
|
+
const cached = cache.get(property);
|
|
1260
|
+
if (cached && cached.source === value) return cached.value;
|
|
1261
|
+
const replace = typeof property === "string" ? methods[property] : void 0;
|
|
1262
|
+
let output;
|
|
1263
|
+
if (replace) output = replace(value.bind(object));
|
|
1264
|
+
else if (Object.prototype.hasOwnProperty.call(value, "prototype")) output = value;
|
|
1265
|
+
else output = value.bind(object);
|
|
1266
|
+
cache.set(property, {
|
|
1267
|
+
source: value,
|
|
1268
|
+
value: output
|
|
1269
|
+
});
|
|
1270
|
+
return output;
|
|
1271
|
+
} });
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Wrap a `node-oracledb` module so the connections it hands out (pooled or
|
|
1275
|
+
* standalone, the latter with the session pinned too) read a zone-less
|
|
1276
|
+
* `TIMESTAMP` as UTC, return `TIMESTAMP`
|
|
1277
|
+
* out-binds as UTC and send Date parameters as UTC wall clock. The module
|
|
1278
|
+
* exposes the read conversion only process-wide (`oracledb.fetchTypeHandler`)
|
|
1279
|
+
* or per `execute()` call, so the handler is added to every call a wrapped
|
|
1280
|
+
* connection makes. A handler the caller passes, or the process-wide one,
|
|
1281
|
+
* still decides first.
|
|
1282
|
+
*
|
|
1283
|
+
* In a process zone with daylight saving, a UTC wall clock falling into the
|
|
1284
|
+
* local spring-forward hour has no local Date to travel as: such a value is
|
|
1285
|
+
* read, and written, an hour late. A process running in UTC is exact.
|
|
1286
|
+
*/
|
|
1287
|
+
function createOracleUTCDriver(driver) {
|
|
1288
|
+
const handleTimestamp = (metadata) => {
|
|
1289
|
+
if (metadata.dbType !== driver.DB_TYPE_TIMESTAMP) return;
|
|
1290
|
+
return { converter: (value) => isDate(value) ? readLocalDateAsUTC(value) : value };
|
|
1291
|
+
};
|
|
1292
|
+
const withHandler = (options) => {
|
|
1293
|
+
const custom = options?.fetchTypeHandler ?? driver.fetchTypeHandler;
|
|
1294
|
+
return {
|
|
1295
|
+
...options ?? {},
|
|
1296
|
+
fetchTypeHandler: (metadata) => {
|
|
1297
|
+
const result = typeof custom === "function" ? custom(metadata) : void 0;
|
|
1298
|
+
return typeof result === "undefined" ? handleTimestamp(metadata) : result;
|
|
1299
|
+
}
|
|
1300
|
+
};
|
|
1301
|
+
};
|
|
1302
|
+
const toBindValue = (value) => {
|
|
1303
|
+
if (isDate(value)) return writeUTCAsLocalDate(value);
|
|
1304
|
+
if (Array.isArray(value)) return value.map((element) => isDate(element) ? writeUTCAsLocalDate(element) : element);
|
|
1305
|
+
return value;
|
|
1306
|
+
};
|
|
1307
|
+
const isZonelessType = (type) => typeof type === "undefined" || type === driver.DB_TYPE_TIMESTAMP;
|
|
1308
|
+
const toBind = (value) => {
|
|
1309
|
+
if (value && typeof value === "object" && !Array.isArray(value) && !isDate(value) && "val" in value) {
|
|
1310
|
+
const definition = value;
|
|
1311
|
+
return isZonelessType(definition.type) ? {
|
|
1312
|
+
...definition,
|
|
1313
|
+
val: toBindValue(definition.val)
|
|
1314
|
+
} : definition;
|
|
1315
|
+
}
|
|
1316
|
+
return toBindValue(value);
|
|
1317
|
+
};
|
|
1318
|
+
/**
|
|
1319
|
+
* A row of `executeMany` holds plain values whose types, if any, come from
|
|
1320
|
+
* `bindDefs`: only a Date without one, or typed TIMESTAMP, is shifted.
|
|
1321
|
+
*/
|
|
1322
|
+
const toRow = (row, definitions) => {
|
|
1323
|
+
const shift = (value, definition) => {
|
|
1324
|
+
const type = definition && typeof definition === "object" ? definition.type : void 0;
|
|
1325
|
+
return isZonelessType(type) ? toBindValue(value) : value;
|
|
1326
|
+
};
|
|
1327
|
+
if (Array.isArray(row)) return row.map((value, index) => shift(value, Array.isArray(definitions) ? definitions[index] : void 0));
|
|
1328
|
+
if (row && typeof row === "object") {
|
|
1329
|
+
const output = {};
|
|
1330
|
+
const keys = Object.keys(row);
|
|
1331
|
+
for (const key of keys) {
|
|
1332
|
+
const definition = definitions && typeof definitions === "object" && !Array.isArray(definitions) ? definitions[key] : void 0;
|
|
1333
|
+
output[key] = shift(row[key], definition);
|
|
1334
|
+
}
|
|
1335
|
+
return output;
|
|
1336
|
+
}
|
|
1337
|
+
return row;
|
|
1338
|
+
};
|
|
1339
|
+
const toBinds = (binds) => {
|
|
1340
|
+
if (Array.isArray(binds)) return binds.map((value) => toBind(value));
|
|
1341
|
+
if (binds && typeof binds === "object") {
|
|
1342
|
+
const output = {};
|
|
1343
|
+
const keys = Object.keys(binds);
|
|
1344
|
+
for (const key of keys) output[key] = toBind(binds[key]);
|
|
1345
|
+
return output;
|
|
1346
|
+
}
|
|
1347
|
+
return binds;
|
|
1348
|
+
};
|
|
1349
|
+
const isTimestampOut = (definition) => {
|
|
1350
|
+
if (!definition || typeof definition !== "object" || isDate(definition)) return false;
|
|
1351
|
+
const { type, dir, val } = definition;
|
|
1352
|
+
if (dir !== driver.BIND_OUT && dir !== driver.BIND_INOUT) return false;
|
|
1353
|
+
return type === driver.DB_TYPE_TIMESTAMP || typeof type === "undefined" && dir === driver.BIND_INOUT && isDate(val);
|
|
1354
|
+
};
|
|
1355
|
+
const readOut = (value) => {
|
|
1356
|
+
if (isDate(value)) return readLocalDateAsUTC(value);
|
|
1357
|
+
if (Array.isArray(value)) return value.map((element) => isDate(element) ? readLocalDateAsUTC(element) : element);
|
|
1358
|
+
return value;
|
|
1359
|
+
};
|
|
1360
|
+
const convertOutBinds = (outBinds, definitions) => {
|
|
1361
|
+
if (!outBinds || typeof outBinds !== "object" || !definitions || typeof definitions !== "object") return outBinds;
|
|
1362
|
+
if (Array.isArray(definitions)) {
|
|
1363
|
+
if (!Array.isArray(outBinds)) return outBinds;
|
|
1364
|
+
const outs = definitions.filter((definition) => !!definition && typeof definition === "object" && !isDate(definition) && (definition.dir === driver.BIND_OUT || definition.dir === driver.BIND_INOUT));
|
|
1365
|
+
return outBinds.map((value, index) => isTimestampOut(outs[index]) ? readOut(value) : value);
|
|
1366
|
+
}
|
|
1367
|
+
const output = { ...outBinds };
|
|
1368
|
+
const keys = Object.keys(output);
|
|
1369
|
+
for (const key of keys) if (isTimestampOut(definitions[key])) output[key] = readOut(output[key]);
|
|
1370
|
+
return output;
|
|
1371
|
+
};
|
|
1372
|
+
const convertResult = (result, definitions) => {
|
|
1373
|
+
if (result && typeof result === "object" && typeof result.outBinds !== "undefined") result.outBinds = convertOutBinds(result.outBinds, definitions);
|
|
1374
|
+
return result;
|
|
1375
|
+
};
|
|
1376
|
+
const convertManyResult = (result, definitions) => {
|
|
1377
|
+
if (result && typeof result === "object" && Array.isArray(result.outBinds)) result.outBinds = result.outBinds.map((row) => convertOutBinds(row, definitions));
|
|
1378
|
+
return result;
|
|
1379
|
+
};
|
|
1380
|
+
const settle = (outcome, definitions, callback, convert) => {
|
|
1381
|
+
if (typeof callback === "function") return outcome;
|
|
1382
|
+
return outcome && typeof outcome.then === "function" ? outcome.then((result) => convert(result, definitions)) : outcome;
|
|
1383
|
+
};
|
|
1384
|
+
const wrapConnection = (connection) => proxyMethods(connection, {
|
|
1385
|
+
execute: (original) => (sql, binds, options, callback) => {
|
|
1386
|
+
if (typeof binds === "function") return original(sql, {}, withHandler(), binds);
|
|
1387
|
+
const input = binds ?? {};
|
|
1388
|
+
const optionsOrCallback = typeof options === "function" ? void 0 : options;
|
|
1389
|
+
const done = typeof options === "function" ? options : callback;
|
|
1390
|
+
if (typeof done === "function") return original(sql, toBinds(input), withHandler(optionsOrCallback), (err, result) => {
|
|
1391
|
+
done(err, err ? result : convertResult(result, input));
|
|
1392
|
+
});
|
|
1393
|
+
return settle(original(sql, toBinds(input), withHandler(optionsOrCallback)), input, void 0, convertResult);
|
|
1394
|
+
},
|
|
1395
|
+
executeMany: (original) => (sql, binds, options, callback) => {
|
|
1396
|
+
const optionsOrCallback = typeof options === "function" ? void 0 : options;
|
|
1397
|
+
const done = typeof options === "function" ? options : callback;
|
|
1398
|
+
const definitions = optionsOrCallback?.bindDefs;
|
|
1399
|
+
const rows = Array.isArray(binds) ? binds.map((row) => toRow(row, definitions)) : binds;
|
|
1400
|
+
if (typeof done === "function") return original(sql, rows, optionsOrCallback ?? {}, (err, result) => {
|
|
1401
|
+
done(err, err ? result : convertManyResult(result, definitions));
|
|
1402
|
+
});
|
|
1403
|
+
return settle(original(sql, rows, optionsOrCallback ?? {}), definitions, void 0, convertManyResult);
|
|
1404
|
+
},
|
|
1405
|
+
queryStream: (original) => (sql, binds, options) => original(sql, toBinds(binds ?? {}), withHandler(options))
|
|
1406
|
+
});
|
|
1407
|
+
const wrapPool = (pool) => proxyMethods(pool, { getConnection: (original) => (...args) => {
|
|
1408
|
+
const callback = args[args.length - 1];
|
|
1409
|
+
if (typeof callback === "function") return original(...args.slice(0, -1), (err, connection, ...rest) => {
|
|
1410
|
+
callback(err, connection ? wrapConnection(connection) : connection, ...rest);
|
|
1411
|
+
});
|
|
1412
|
+
return original(...args).then((connection) => wrapConnection(connection));
|
|
1413
|
+
} });
|
|
1414
|
+
const wrapFactory = (wrap) => (original) => (...args) => {
|
|
1415
|
+
const callback = args[args.length - 1];
|
|
1416
|
+
if (typeof callback === "function") return original(...args.slice(0, -1), (err, value) => {
|
|
1417
|
+
callback(err, value ? wrap(value) : value);
|
|
1418
|
+
});
|
|
1419
|
+
const output = original(...args);
|
|
1420
|
+
if (output && typeof output.then === "function") return output.then((value) => wrap(value));
|
|
1421
|
+
return output ? wrap(output) : output;
|
|
1422
|
+
};
|
|
1423
|
+
const pinConnection = (connection) => Promise.resolve(connection.execute(ORACLE_SESSION_TIMEZONE_SQL)).then(() => wrapConnection(connection), (error) => Promise.resolve(connection.close()).catch(() => void 0).then(() => Promise.reject(error)));
|
|
1424
|
+
return markInstalled(proxyMethods(driver, {
|
|
1425
|
+
createPool: wrapFactory(wrapPool),
|
|
1426
|
+
getPool: (original) => (...args) => {
|
|
1427
|
+
const pool = original(...args);
|
|
1428
|
+
return pool ? wrapPool(pool) : pool;
|
|
1429
|
+
},
|
|
1430
|
+
getConnection: (original) => (...args) => {
|
|
1431
|
+
const callback = args[args.length - 1];
|
|
1432
|
+
if (typeof callback === "function") return original(...args.slice(0, -1), (err, connection) => {
|
|
1433
|
+
if (err || !connection) {
|
|
1434
|
+
callback(err, connection);
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
pinConnection(connection).then((value) => callback(null, value), (error) => callback(error));
|
|
1438
|
+
});
|
|
1439
|
+
return original(...args).then((connection) => pinConnection(connection));
|
|
1440
|
+
}
|
|
1441
|
+
}));
|
|
1442
|
+
}
|
|
1443
|
+
/**
|
|
1444
|
+
* Pin an oracle data source: a pool `sessionCallback` running
|
|
1445
|
+
* `ALTER SESSION SET TIME_ZONE`, and a driver whose connections read a
|
|
1446
|
+
* zone-less `TIMESTAMP` as UTC and send Date parameters as UTC wall clock.
|
|
1447
|
+
* A `sessionCallback` function the caller set runs after the pin; one naming
|
|
1448
|
+
* a PL/SQL procedure can not be combined and is a conflict.
|
|
1449
|
+
*/
|
|
1450
|
+
function applyOracleTimezone(options) {
|
|
1451
|
+
if (isInstalled(options.driver)) {
|
|
1452
|
+
if (isInstalled(options.extra?.sessionCallback)) return options;
|
|
1453
|
+
throw OptionsError.timezoneConflict("the oracle pin was altered after it was applied.");
|
|
1454
|
+
}
|
|
1455
|
+
const existing = options.extra?.sessionCallback;
|
|
1456
|
+
if (typeof existing !== "undefined" && typeof existing !== "function") throw OptionsError.timezoneConflict("the oracle sessionCallback names a PL/SQL procedure, which can not run after the session pin.");
|
|
1457
|
+
const sessionCallback = markInstalled((connection, requestedTag, callback) => {
|
|
1458
|
+
connection.execute(ORACLE_SESSION_TIMEZONE_SQL, (err) => {
|
|
1459
|
+
if (err || typeof existing !== "function") {
|
|
1460
|
+
callback(err ?? void 0);
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
existing(connection, requestedTag, callback);
|
|
1464
|
+
});
|
|
1465
|
+
});
|
|
1466
|
+
const driver = options.driver ?? PlatformTools.load("oracledb");
|
|
1467
|
+
return {
|
|
1468
|
+
...options,
|
|
1469
|
+
driver: createOracleUTCDriver(driver),
|
|
1470
|
+
extra: {
|
|
1471
|
+
...options.extra ?? {},
|
|
1472
|
+
sessionCallback
|
|
1473
|
+
}
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
//#endregion
|
|
1477
|
+
//#region src/data-source/options/timezone/postgres.ts
|
|
1478
|
+
/**
|
|
1479
|
+
* `timestamp without time zone` and its array.
|
|
1480
|
+
*/
|
|
1481
|
+
const POSTGRES_TIMESTAMP_OID = 1114;
|
|
1482
|
+
const POSTGRES_TIMESTAMP_ARRAY_OID = 1115;
|
|
1483
|
+
/**
|
|
1484
|
+
* A `TimeZone` assignment among the postgres startup options, in either
|
|
1485
|
+
* spelling the server accepts (`-c TimeZone=...`, `--TimeZone=...`). The
|
|
1486
|
+
* name is matched whole: `log_timezone` sets something else entirely.
|
|
1487
|
+
*/
|
|
1488
|
+
const POSTGRES_TIMEZONE_OPTION = /(?:^|\s)(?:-c\s*|--)timezone=(\S+)/gi;
|
|
1489
|
+
const POSTGRES_UTC_ZONE = /^(?:utc|uct|etc\/utc|etc\/uct|universal|etc\/universal|zulu|etc\/zulu|z|gmt|gmt0|etc\/gmt|etc\/gmt0|etc\/gmt[+-]0|greenwich|etc\/greenwich|[+-]?0{1,2}(?::?00)?)$/i;
|
|
1490
|
+
/**
|
|
1491
|
+
* The text form of a `timestamp without time zone` (ISO DateStyle).
|
|
1492
|
+
*/
|
|
1493
|
+
const POSTGRES_TIMESTAMP = /^(\d{4,})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})(\.\d+)?( BC)?$/;
|
|
1494
|
+
/**
|
|
1495
|
+
* The exact form pg's `prepareValue` gives a Date (the process offset
|
|
1496
|
+
* included), which pg-cursor applies in its constructor, before a stream
|
|
1497
|
+
* reaches the client.
|
|
1498
|
+
*/
|
|
1499
|
+
const POSTGRES_LOCAL_DATE = /^(\d{4,})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})\.(\d{3})([+-])(\d{2}):(\d{2})( BC)?$/;
|
|
1500
|
+
/**
|
|
1501
|
+
* Serialize a Date as pg does with `parseInputDatesAsUTC`, which is only
|
|
1502
|
+
* available process-wide there: UTC fields, an explicit `+00:00`, and a
|
|
1503
|
+
* ` BC` suffix for years before 1.
|
|
1504
|
+
*/
|
|
1505
|
+
function serializePostgresDateAsUTC(date) {
|
|
1506
|
+
let year = date.getUTCFullYear();
|
|
1507
|
+
const isBCYear = year < 1;
|
|
1508
|
+
if (isBCYear) year = Math.abs(year) + 1;
|
|
1509
|
+
const pad = (value, length = 2) => String(value).padStart(length, "0");
|
|
1510
|
+
let output = `${pad(year, 4)}-${pad(date.getUTCMonth() + 1)}-${pad(date.getUTCDate())}T${pad(date.getUTCHours())}:${pad(date.getUTCMinutes())}:${pad(date.getUTCSeconds())}.${pad(date.getUTCMilliseconds(), 3)}+00:00`;
|
|
1511
|
+
if (isBCYear) output += " BC";
|
|
1512
|
+
return output;
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Re-serialize a Date which pg already turned into its local form (the
|
|
1516
|
+
* process offset included) as UTC. Any other value is returned unchanged.
|
|
1517
|
+
*/
|
|
1518
|
+
function reserializePostgresLocalDateAsUTC(value) {
|
|
1519
|
+
if (typeof value !== "string") return value;
|
|
1520
|
+
const match = POSTGRES_LOCAL_DATE.exec(value);
|
|
1521
|
+
if (!match) return value;
|
|
1522
|
+
const [, year, month, day, hour, minute, second, millisecond, sign, offsetHours, offsetMinutes, bc] = match;
|
|
1523
|
+
let fullYear = Number(year);
|
|
1524
|
+
if (bc) fullYear = 1 - fullYear;
|
|
1525
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
1526
|
+
date.setUTCFullYear(fullYear, Number(month) - 1, Number(day));
|
|
1527
|
+
date.setUTCHours(Number(hour), Number(minute), Number(second), Number(millisecond));
|
|
1528
|
+
const offset = (Number(offsetHours) * 60 + Number(offsetMinutes)) * (sign === "-" ? -1 : 1);
|
|
1529
|
+
date.setTime(date.getTime() - offset * 6e4);
|
|
1530
|
+
return serializePostgresDateAsUTC(date);
|
|
1531
|
+
}
|
|
1532
|
+
function toPostgresUTCParameter(value) {
|
|
1533
|
+
if (isDate(value)) return serializePostgresDateAsUTC(value);
|
|
1534
|
+
if (Array.isArray(value)) return value.map((element) => toPostgresUTCParameter(element));
|
|
1535
|
+
return value;
|
|
1536
|
+
}
|
|
1537
|
+
/**
|
|
1538
|
+
* A submittable (pg Query, pg-cursor, pg-query-stream) may already hold its
|
|
1539
|
+
* values in pg's prepared form: pg-cursor applies pg's `prepareValue` in its
|
|
1540
|
+
* constructor, before the client sees it.
|
|
1541
|
+
*/
|
|
1542
|
+
function toPostgresUTCSubmittedParameter(value) {
|
|
1543
|
+
if (isDate(value)) return serializePostgresDateAsUTC(value);
|
|
1544
|
+
return reserializePostgresLocalDateAsUTC(value);
|
|
1545
|
+
}
|
|
1546
|
+
/**
|
|
1547
|
+
* Parse the text form of a `timestamp without time zone` as UTC: the same
|
|
1548
|
+
* fields, BC years and `infinity` pg's own parser handles, read in UTC rather
|
|
1549
|
+
* than in the zone of the process. Another text form (a non-ISO DateStyle)
|
|
1550
|
+
* gives `null`, as it does there.
|
|
1551
|
+
*/
|
|
1552
|
+
function parsePostgresTimestampAsUTC(value) {
|
|
1553
|
+
if (value === "infinity") return Infinity;
|
|
1554
|
+
if (value === "-infinity") return -Infinity;
|
|
1555
|
+
const match = POSTGRES_TIMESTAMP.exec(value);
|
|
1556
|
+
if (!match) return null;
|
|
1557
|
+
const [, year, month, day, hour, minute, second, fraction, bc] = match;
|
|
1558
|
+
let fullYear = Number(year);
|
|
1559
|
+
if (bc) fullYear = 1 - fullYear;
|
|
1560
|
+
const date = /* @__PURE__ */ new Date(0);
|
|
1561
|
+
date.setUTCFullYear(fullYear, Number(month) - 1, Number(day));
|
|
1562
|
+
date.setUTCHours(Number(hour), Number(minute), Number(second), fraction ? Math.floor(Number(fraction) * 1e3) : 0);
|
|
1563
|
+
return date;
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Parse the text form of a postgres array, handing each element to the given
|
|
1567
|
+
* parser. Covers what pg emits for a `timestamp[]`: quoted and unquoted
|
|
1568
|
+
* elements, `NULL` and nested arrays.
|
|
1569
|
+
*/
|
|
1570
|
+
function parsePostgresArray(value, parse) {
|
|
1571
|
+
let position = 0;
|
|
1572
|
+
const parseLevel = () => {
|
|
1573
|
+
const output = [];
|
|
1574
|
+
position++;
|
|
1575
|
+
while (position < value.length) {
|
|
1576
|
+
const char = value[position];
|
|
1577
|
+
if (char === "}") {
|
|
1578
|
+
position++;
|
|
1579
|
+
return output;
|
|
1580
|
+
}
|
|
1581
|
+
if (char === ",") position++;
|
|
1582
|
+
else if (char === "{") output.push(parseLevel());
|
|
1583
|
+
else if (char === "\"") {
|
|
1584
|
+
let element = "";
|
|
1585
|
+
position++;
|
|
1586
|
+
while (position < value.length && value[position] !== "\"") {
|
|
1587
|
+
if (value[position] === "\\") position++;
|
|
1588
|
+
element += value[position];
|
|
1589
|
+
position++;
|
|
1590
|
+
}
|
|
1591
|
+
position++;
|
|
1592
|
+
output.push(parse(element));
|
|
1593
|
+
} else {
|
|
1594
|
+
let element = "";
|
|
1595
|
+
while (position < value.length && value[position] !== "," && value[position] !== "}") {
|
|
1596
|
+
element += value[position];
|
|
1597
|
+
position++;
|
|
1598
|
+
}
|
|
1599
|
+
output.push(element === "NULL" ? null : parse(element));
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
return output;
|
|
1603
|
+
};
|
|
1604
|
+
const brace = value.indexOf("{");
|
|
1605
|
+
if (brace === -1) return [];
|
|
1606
|
+
position = brace;
|
|
1607
|
+
return parseLevel();
|
|
1608
|
+
}
|
|
1609
|
+
const parsePostgresTimestampArrayAsUTC = (value) => parsePostgresArray(value, parsePostgresTimestampAsUTC);
|
|
1610
|
+
/**
|
|
1611
|
+
* Build postgres type parsers which read `timestamp without time zone` (and
|
|
1612
|
+
* its array) as UTC and delegate every other type to the given registry.
|
|
1613
|
+
*/
|
|
1614
|
+
function createPostgresUTCTypes(types) {
|
|
1615
|
+
return markInstalled({ getTypeParser(oid, format) {
|
|
1616
|
+
if (format !== "binary") {
|
|
1617
|
+
if (oid === POSTGRES_TIMESTAMP_OID) return parsePostgresTimestampAsUTC;
|
|
1618
|
+
if (oid === POSTGRES_TIMESTAMP_ARRAY_OID) return parsePostgresTimestampArrayAsUTC;
|
|
1619
|
+
}
|
|
1620
|
+
return types.getTypeParser(oid, format);
|
|
1621
|
+
} });
|
|
1622
|
+
}
|
|
1623
|
+
/**
|
|
1624
|
+
* Whether a registry still parses zone-less timestamps the way pg does by
|
|
1625
|
+
* default (a local Date), judged by behaviour: a custom parser registered for
|
|
1626
|
+
* them (strings, another date library) is a choice the pin would override.
|
|
1627
|
+
*/
|
|
1628
|
+
function isDefaultTimestampParsing(types) {
|
|
1629
|
+
const expected = new Date(2e3, 0, 2, 3, 4, 5).getTime();
|
|
1630
|
+
try {
|
|
1631
|
+
const single = types.getTypeParser(POSTGRES_TIMESTAMP_OID)("2000-01-02 03:04:05");
|
|
1632
|
+
const many = types.getTypeParser(POSTGRES_TIMESTAMP_ARRAY_OID)("{\"2000-01-02 03:04:05\"}");
|
|
1633
|
+
return single instanceof Date && single.getTime() === expected && Array.isArray(many) && many[0] instanceof Date && many[0].getTime() === expected;
|
|
1634
|
+
} catch {
|
|
1635
|
+
return false;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
/**
|
|
1639
|
+
* Derive a pg `Client` class which sends Date parameters as UTC. Left to pg,
|
|
1640
|
+
* a Date is sent with the offset of the Node process, which postgres drops
|
|
1641
|
+
* for a `timestamp without time zone`, storing the process's wall clock.
|
|
1642
|
+
* pg-pool takes the class through its `Client` option, so this stays scoped
|
|
1643
|
+
* to one pool.
|
|
1644
|
+
*
|
|
1645
|
+
* A stream (pg-query-stream / pg-cursor) prepares its values before the
|
|
1646
|
+
* client sees them, so those arrive in pg's local form and are re-serialized;
|
|
1647
|
+
* a Date inside an array value of a stream arrives as an array literal and is
|
|
1648
|
+
* not.
|
|
1649
|
+
*/
|
|
1650
|
+
function createPostgresUTCClient(Base) {
|
|
1651
|
+
const Client = class extends Base {
|
|
1652
|
+
query(config, values, callback) {
|
|
1653
|
+
if (Array.isArray(values)) return super.query(config, values.map((value) => toPostgresUTCParameter(value)), callback);
|
|
1654
|
+
if (config && typeof config === "object") {
|
|
1655
|
+
if (config.cursor && Array.isArray(config.cursor.values)) config.cursor.values = config.cursor.values.map((value) => toPostgresUTCSubmittedParameter(value));
|
|
1656
|
+
else if (Array.isArray(config.values)) {
|
|
1657
|
+
if (typeof config.submit === "function") config.values = config.values.map((value) => toPostgresUTCSubmittedParameter(value));
|
|
1658
|
+
else return super.query({
|
|
1659
|
+
...config,
|
|
1660
|
+
values: config.values.map((value) => toPostgresUTCParameter(value))
|
|
1661
|
+
}, values, callback);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
return super.query(config, values, callback);
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
return markInstalled(Client);
|
|
1668
|
+
}
|
|
1669
|
+
/**
|
|
1670
|
+
* The TimeZone a set of startup options assigns. Postgres applies them in
|
|
1671
|
+
* order, so the last assignment is the one in effect.
|
|
1672
|
+
*/
|
|
1673
|
+
function readTimezoneOption(options) {
|
|
1674
|
+
if (typeof options !== "string") return;
|
|
1675
|
+
let zone;
|
|
1676
|
+
for (const match of options.matchAll(POSTGRES_TIMEZONE_OPTION)) [, zone] = match;
|
|
1677
|
+
return typeof zone === "string" ? zone.replace(/^'(.*)'$/, "$1") : void 0;
|
|
1678
|
+
}
|
|
1679
|
+
function isUTCZone(zone) {
|
|
1680
|
+
return POSTGRES_UTC_ZONE.test(zone);
|
|
1681
|
+
}
|
|
1682
|
+
/**
|
|
1683
|
+
* Add `-c TimeZone=UTC` to a set of startup options, or accept a TimeZone
|
|
1684
|
+
* already in effect there when it names UTC. Any other zone is a conflict.
|
|
1685
|
+
*/
|
|
1686
|
+
function pinTimezoneOption(options, source) {
|
|
1687
|
+
const zone = readTimezoneOption(options);
|
|
1688
|
+
if (typeof zone === "undefined") return `${options} -c TimeZone=UTC`.trim();
|
|
1689
|
+
if (isUTCZone(zone)) return options;
|
|
1690
|
+
throw OptionsError.timezoneConflict(`${source} sets the postgres TimeZone to ${zone}.`);
|
|
1691
|
+
}
|
|
1692
|
+
/**
|
|
1693
|
+
* Take the `options` parameter out of a connection string, leaving every
|
|
1694
|
+
* other byte of it as it was (re-encoding it can break a string pg would
|
|
1695
|
+
* have accepted). Postgres uses the last occurrence, and so does this.
|
|
1696
|
+
*/
|
|
1697
|
+
function extractPostgresUrlOptions(url) {
|
|
1698
|
+
const index = url.indexOf("?");
|
|
1699
|
+
if (index === -1) return { url };
|
|
1700
|
+
const hash = url.indexOf("#", index);
|
|
1701
|
+
const query = url.slice(index + 1, hash === -1 ? void 0 : hash);
|
|
1702
|
+
const rest = hash === -1 ? "" : url.slice(hash);
|
|
1703
|
+
let options;
|
|
1704
|
+
const kept = [];
|
|
1705
|
+
for (const segment of query.split("&")) {
|
|
1706
|
+
const params = new URLSearchParams(segment);
|
|
1707
|
+
if (params.has("options")) options = params.get("options") ?? "";
|
|
1708
|
+
else if (segment.length > 0) kept.push(segment);
|
|
1709
|
+
}
|
|
1710
|
+
return {
|
|
1711
|
+
url: url.slice(0, index) + (kept.length > 0 ? `?${kept.join("&")}` : "") + rest,
|
|
1712
|
+
options
|
|
1713
|
+
};
|
|
1714
|
+
}
|
|
1715
|
+
function isPostgresNativeInUse(nativeDriver, driver) {
|
|
1716
|
+
try {
|
|
1717
|
+
let native = nativeDriver;
|
|
1718
|
+
if (native === null || typeof native === "undefined") native = PlatformTools.load("pg-native");
|
|
1719
|
+
return !!(native && driver.native);
|
|
1720
|
+
} catch {
|
|
1721
|
+
return false;
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
/**
|
|
1725
|
+
* Pin a postgres data source: `-c TimeZone=UTC` for the session, parsers
|
|
1726
|
+
* reading `timestamp without time zone` (and its array) as UTC, and a client
|
|
1727
|
+
* sending Date parameters as UTC.
|
|
1728
|
+
*
|
|
1729
|
+
* pg takes the startup options from the connection string first, then
|
|
1730
|
+
* `extra.options`, then `PGOPTIONS`; the options in effect are moved into
|
|
1731
|
+
* `extra.options` (out of the connection string, byte for byte otherwise)
|
|
1732
|
+
* and the pin is added there. A user's `extra.types` (for other types) and
|
|
1733
|
+
* `extra.Client` are built upon. A foreign TimeZone in effect, timestamp
|
|
1734
|
+
* parsing customised process-wide or in `extra.types`, or a replication node
|
|
1735
|
+
* url carrying options (shared `extra` can not hold per-node options) is a
|
|
1736
|
+
* conflict, and so is a pin altered after it was applied.
|
|
1737
|
+
*/
|
|
1738
|
+
function applyPostgresTimezone(options) {
|
|
1739
|
+
const extra = { ...options.extra ?? {} };
|
|
1740
|
+
if (isInstalled(extra.types) || isInstalled(extra.Client)) {
|
|
1741
|
+
const zone = readTimezoneOption(extra.options);
|
|
1742
|
+
const nodes = options.replication ? [options.replication.master, ...options.replication.slaves] : [];
|
|
1743
|
+
const urls = [
|
|
1744
|
+
options.url,
|
|
1745
|
+
extra.connectionString,
|
|
1746
|
+
...nodes.map((node) => node.url)
|
|
1747
|
+
].filter((url) => typeof url === "string");
|
|
1748
|
+
if (isInstalled(extra.types) && isInstalled(extra.Client) && typeof zone === "string" && isUTCZone(zone) && urls.every((url) => typeof extractPostgresUrlOptions(url).options === "undefined")) return options;
|
|
1749
|
+
throw OptionsError.timezoneConflict("the postgres pin was altered after it was applied.");
|
|
1750
|
+
}
|
|
1751
|
+
const driver = options.driver ?? PlatformTools.load("pg");
|
|
1752
|
+
if (!isDefaultTimestampParsing(driver.types)) throw OptionsError.timezoneConflict("timestamp parsing is customised process-wide (pg.types.setTypeParser).");
|
|
1753
|
+
const own = extra.types;
|
|
1754
|
+
if (own && !isDefaultTimestampParsing(own)) throw OptionsError.timezoneConflict("extra.types customises timestamp parsing.");
|
|
1755
|
+
const output = { ...options };
|
|
1756
|
+
let fromUrl;
|
|
1757
|
+
if (typeof options.url === "string") {
|
|
1758
|
+
const extracted = extractPostgresUrlOptions(options.url);
|
|
1759
|
+
output.url = extracted.url;
|
|
1760
|
+
fromUrl = extracted.options;
|
|
1761
|
+
}
|
|
1762
|
+
if (typeof extra.connectionString === "string") {
|
|
1763
|
+
const extracted = extractPostgresUrlOptions(extra.connectionString);
|
|
1764
|
+
extra.connectionString = extracted.url;
|
|
1765
|
+
fromUrl = extracted.options;
|
|
1766
|
+
}
|
|
1767
|
+
if (options.replication) {
|
|
1768
|
+
if ([options.replication.master, ...options.replication.slaves].some((node) => typeof node.url === "string" && typeof extractPostgresUrlOptions(node.url).options !== "undefined")) throw OptionsError.timezoneConflict("a replication node url carries startup options, which can not be combined with the pin.");
|
|
1769
|
+
}
|
|
1770
|
+
let source = "PGOPTIONS";
|
|
1771
|
+
let base = process.env.PGOPTIONS ?? "";
|
|
1772
|
+
if (typeof fromUrl === "string") {
|
|
1773
|
+
source = "the connection url";
|
|
1774
|
+
base = fromUrl;
|
|
1775
|
+
} else if (typeof extra.options === "string") {
|
|
1776
|
+
source = "extra.options";
|
|
1777
|
+
base = extra.options;
|
|
1778
|
+
}
|
|
1779
|
+
extra.options = pinTimezoneOption(base, source);
|
|
1780
|
+
extra.types = createPostgresUTCTypes(own ?? driver.types);
|
|
1781
|
+
extra.Client = createPostgresUTCClient(extra.Client ?? (isPostgresNativeInUse(options.nativeDriver, driver) ? driver.native.Client : driver.Client));
|
|
1782
|
+
output.extra = extra;
|
|
1783
|
+
return output;
|
|
1784
|
+
}
|
|
1785
|
+
//#endregion
|
|
1786
|
+
//#region src/data-source/options/timezone/module.ts
|
|
1787
|
+
/**
|
|
1788
|
+
* Pin a data source to a timezone (only UTC) on EVERY side of a zone-less
|
|
1789
|
+
* date column: the database session that stamps it (`now()`,
|
|
1790
|
+
* `CURRENT_TIMESTAMP`), and the driver that reads it and serializes Date
|
|
1791
|
+
* parameters into it. Left alone, the database stamps in its own session
|
|
1792
|
+
* zone while the driver reads and writes in the zone of the Node process,
|
|
1793
|
+
* which agree only while both clocks do.
|
|
1794
|
+
*
|
|
1795
|
+
* Each driver lives in its own file (`postgres.ts`, `mysql.ts`, `oracle.ts`);
|
|
1796
|
+
* every other driver needs nothing and is returned unchanged. A setting the
|
|
1797
|
+
* caller already made is kept when it agrees with the pin (a UTC `timezone`,
|
|
1798
|
+
* a UTC `TimeZone`) or can be built upon (a pg `Client`, pg `types` leaving
|
|
1799
|
+
* timestamps alone, an oracle `sessionCallback` function), and throws an
|
|
1800
|
+
* `OptionsError` when it contradicts it: half a pin shifts values instead of
|
|
1801
|
+
* fixing them. Applying it twice returns the pinned options unchanged.
|
|
1802
|
+
*
|
|
1803
|
+
* Only values written from then on are affected: rows a database stamped in
|
|
1804
|
+
* another zone before keep that wall clock.
|
|
1805
|
+
*/
|
|
1806
|
+
function pinTimezone(options, timezone) {
|
|
1807
|
+
if (!isDataSourceTimezone(timezone)) throw OptionsError.timezoneUnsupported(timezone);
|
|
1808
|
+
switch (options.type) {
|
|
1809
|
+
case "mysql":
|
|
1810
|
+
case "mariadb": return applyMysqlTimezone(options);
|
|
1811
|
+
case "postgres": return applyPostgresTimezone(options);
|
|
1812
|
+
case "oracle": return applyOracleTimezone(options);
|
|
1813
|
+
default: return options;
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
//#endregion
|
|
1096
1817
|
//#region src/data-source/options/utils/env.ts
|
|
1097
1818
|
function hasEnvDataSourceOptions() {
|
|
1098
1819
|
return !!useEnv("type");
|
|
1099
1820
|
}
|
|
1100
|
-
|
|
1821
|
+
/**
|
|
1822
|
+
* Apply `DB_PIN_TIMEZONE`, when set, once the options are complete: applied
|
|
1823
|
+
* before a merge, a deep merge would reach into the wrapped driver module.
|
|
1824
|
+
* Options pinned in code are re-checked after the merge as well, so an
|
|
1825
|
+
* environment value undoing part of the pin (a `DB_DRIVER_EXTRA`
|
|
1826
|
+
* timezone, say)
|
|
1827
|
+
* fails instead of leaving it half applied.
|
|
1828
|
+
*/
|
|
1829
|
+
function applyEnvTimezone(options) {
|
|
1830
|
+
const timezone = useEnv("pinTimezone");
|
|
1831
|
+
if (typeof timezone === "undefined" || timezone === "") return hasInstalledTimezone(options) ? pinTimezone(options, "UTC") : options;
|
|
1832
|
+
if (!isDataSourceTimezone(timezone)) throw OptionsError.timezoneUnsupported(timezone);
|
|
1833
|
+
return pinTimezone(options, "UTC");
|
|
1834
|
+
}
|
|
1101
1835
|
function readDataSourceOptionsFromEnv() {
|
|
1836
|
+
const options = readRawDataSourceOptionsFromEnv();
|
|
1837
|
+
if (!options) return;
|
|
1838
|
+
return applyEnvTimezone(options);
|
|
1839
|
+
}
|
|
1840
|
+
/* istanbul ignore next */
|
|
1841
|
+
function readRawDataSourceOptionsFromEnv() {
|
|
1102
1842
|
if (!hasEnvDataSourceOptions()) return;
|
|
1103
1843
|
const base = {
|
|
1104
1844
|
type: useEnv("type"),
|
|
@@ -1167,9 +1907,9 @@ function readDataSourceOptionsFromEnv() {
|
|
|
1167
1907
|
};
|
|
1168
1908
|
}
|
|
1169
1909
|
function mergeDataSourceOptionsWithEnv(options) {
|
|
1170
|
-
const env =
|
|
1171
|
-
if (!env) return options;
|
|
1172
|
-
return mergeDataSourceOptions(env, options);
|
|
1910
|
+
const env = readRawDataSourceOptionsFromEnv();
|
|
1911
|
+
if (!env) return applyEnvTimezone(options);
|
|
1912
|
+
return applyEnvTimezone(mergeDataSourceOptions(env, options));
|
|
1173
1913
|
}
|
|
1174
1914
|
//#endregion
|
|
1175
1915
|
//#region src/data-source/options/module.ts
|
|
@@ -1232,7 +1972,8 @@ async function useDataSource(alias) {
|
|
|
1232
1972
|
if (!existing.isInitialized) await existing.initialize();
|
|
1233
1973
|
return existing;
|
|
1234
1974
|
}
|
|
1235
|
-
const
|
|
1975
|
+
const options = await useDataSourceOptions(key);
|
|
1976
|
+
const dataSource = new DataSource(options);
|
|
1236
1977
|
await dataSource.initialize();
|
|
1237
1978
|
return dataSource;
|
|
1238
1979
|
});
|
|
@@ -1814,7 +2555,7 @@ function matchesColumnType(column, input, describer) {
|
|
|
1814
2555
|
const { normalizeType, describeType } = describer;
|
|
1815
2556
|
if (normalizeType(input.type).toLowerCase() !== normalizeType(column.type).toLowerCase()) return false;
|
|
1816
2557
|
if (typeof input.length !== "undefined" && describeType(input.type, input.length).toLowerCase() !== describeType(column.type, column.length).toLowerCase()) return false;
|
|
1817
|
-
return
|
|
2558
|
+
return typeof input.nullable !== "boolean" || input.nullable === column.isNullable;
|
|
1818
2559
|
}
|
|
1819
2560
|
//#endregion
|
|
1820
2561
|
//#region src/database/schema/alter/checks.ts
|
|
@@ -2434,73 +3175,44 @@ async function dropDatabase(input = {}) {
|
|
|
2434
3175
|
}
|
|
2435
3176
|
//#endregion
|
|
2436
3177
|
//#region src/database/utils/migration.ts
|
|
3178
|
+
/**
|
|
3179
|
+
* typeorm keeps the statement escaping, the query-parameter formatting and both file
|
|
3180
|
+
* templates as protected statics on its migration:generate command. Subclassing is the
|
|
3181
|
+
* only way to reach them, and reusing them keeps the generated file byte-identical to
|
|
3182
|
+
* the output of `typeorm migration:generate`.
|
|
3183
|
+
*/
|
|
2437
3184
|
var GenerateCommand = class extends MigrationGenerateCommand {
|
|
2438
|
-
static
|
|
2439
|
-
|
|
3185
|
+
static buildStatement(query, parameters, prettify) {
|
|
3186
|
+
const statement = prettify ? this.prettifyQuery(query) : query;
|
|
3187
|
+
return `await queryRunner.query(\`${this.escapeTemplateLiteral(statement)}\`${this.queryParams(parameters)});`;
|
|
3188
|
+
}
|
|
3189
|
+
static buildContent(context, up, down) {
|
|
3190
|
+
const upStatements = up.map((statement) => ` ${statement}`);
|
|
3191
|
+
const downStatements = down.map((statement) => ` ${statement}`);
|
|
3192
|
+
if (context.language === "js") return this.getJavascriptTemplate(context.name, context.timestamp, upStatements, downStatements, context.esm);
|
|
3193
|
+
return this.getTemplate(context.name, context.timestamp, upStatements, downStatements);
|
|
2440
3194
|
}
|
|
2441
3195
|
};
|
|
2442
|
-
function queryParams(parameters) {
|
|
2443
|
-
if (!parameters || !parameters.length) return "";
|
|
2444
|
-
return `, ${JSON.stringify(parameters)}`;
|
|
2445
|
-
}
|
|
2446
|
-
function buildTemplate(name, timestamp, upStatements, downStatements) {
|
|
2447
|
-
const migrationName = `${pascalCase(name)}${timestamp}`;
|
|
2448
|
-
const up = upStatements.map((statement) => ` ${statement}`);
|
|
2449
|
-
const down = downStatements.map((statement) => ` ${statement}`);
|
|
2450
|
-
return `import type { MigrationInterface, QueryRunner } from 'typeorm';
|
|
2451
|
-
|
|
2452
|
-
export class ${migrationName} implements MigrationInterface {
|
|
2453
|
-
name = '${migrationName}';
|
|
2454
|
-
|
|
2455
|
-
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
2456
|
-
${up.join(`
|
|
2457
|
-
`)}
|
|
2458
|
-
}
|
|
2459
|
-
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
2460
|
-
${down.join(`
|
|
2461
|
-
`)}
|
|
2462
|
-
}
|
|
2463
|
-
}
|
|
2464
|
-
`;
|
|
2465
|
-
}
|
|
2466
3196
|
async function generateMigration(context) {
|
|
2467
|
-
|
|
2468
|
-
const timestamp = context.timestamp
|
|
2469
|
-
const
|
|
2470
|
-
const
|
|
2471
|
-
const up =
|
|
2472
|
-
const down =
|
|
2473
|
-
const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
|
|
2474
|
-
if (context.prettify) {
|
|
2475
|
-
sqlInMemory.upQueries.forEach((upQuery) => {
|
|
2476
|
-
upQuery.query = GenerateCommand.prettify(upQuery.query);
|
|
2477
|
-
});
|
|
2478
|
-
sqlInMemory.downQueries.forEach((downQuery) => {
|
|
2479
|
-
downQuery.query = GenerateCommand.prettify(downQuery.query);
|
|
2480
|
-
});
|
|
2481
|
-
}
|
|
2482
|
-
sqlInMemory.upQueries.forEach((upQuery) => {
|
|
2483
|
-
up.push(`await queryRunner.query(\`${upQuery.query.replace(/`/g, "\\`")}\`${queryParams(upQuery.parameters)});`);
|
|
2484
|
-
});
|
|
2485
|
-
sqlInMemory.downQueries.forEach((downQuery) => {
|
|
2486
|
-
down.push(`await queryRunner.query(\`${downQuery.query.replace(/`/g, "\\`")}\`${queryParams(downQuery.parameters)});`);
|
|
2487
|
-
});
|
|
3197
|
+
const name = context.name || "Default";
|
|
3198
|
+
const timestamp = context.timestamp ?? Date.now();
|
|
3199
|
+
const language = context.language || "ts";
|
|
3200
|
+
const sqlInMemory = await context.dataSource.driver.createSchemaBuilder().log();
|
|
3201
|
+
const up = sqlInMemory.upQueries.map((query) => GenerateCommand.buildStatement(query.query, query.parameters, context.prettify));
|
|
3202
|
+
const down = sqlInMemory.downQueries.map((query) => GenerateCommand.buildStatement(query.query, query.parameters, context.prettify)).reverse();
|
|
2488
3203
|
if (up.length === 0 && down.length === 0) return {
|
|
2489
3204
|
up,
|
|
2490
3205
|
down
|
|
2491
3206
|
};
|
|
2492
|
-
const content =
|
|
3207
|
+
const content = GenerateCommand.buildContent({
|
|
3208
|
+
name,
|
|
3209
|
+
timestamp,
|
|
3210
|
+
language,
|
|
3211
|
+
esm: context.esm || false
|
|
3212
|
+
}, up, down);
|
|
2493
3213
|
if (!context.preview) {
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
else directoryPath = context.directoryPath;
|
|
2497
|
-
else directoryPath = path.join(process$1.cwd(), "migrations");
|
|
2498
|
-
try {
|
|
2499
|
-
await fs.promises.access(directoryPath, fs.constants.R_OK | fs.constants.W_OK);
|
|
2500
|
-
} catch {
|
|
2501
|
-
await fs.promises.mkdir(directoryPath, { recursive: true });
|
|
2502
|
-
}
|
|
2503
|
-
await fs.promises.writeFile(path.join(directoryPath, fileName), content, { encoding: "utf-8" });
|
|
3214
|
+
const directoryPath = resolveFilePath(context.directoryPath || "migrations");
|
|
3215
|
+
await CommandUtils.createFile(path.join(directoryPath, `${timestamp}-${name}.${language}`), content);
|
|
2504
3216
|
}
|
|
2505
3217
|
return {
|
|
2506
3218
|
up,
|
|
@@ -2612,6 +3324,95 @@ async function dropSQLiteDatabase(input = {}) {
|
|
|
2612
3324
|
return executeDatabaseDrop("better-sqlite3", await buildDatabaseDropContext(input));
|
|
2613
3325
|
}
|
|
2614
3326
|
//#endregion
|
|
3327
|
+
//#region src/database/lock/module.ts
|
|
3328
|
+
const MYSQL_LOCK_STATEMENTS = {
|
|
3329
|
+
acquire: "SELECT GET_LOCK(SHA2(CONCAT(COALESCE(DATABASE(), ''), ':', ?), 256), 0) AS `acquired`",
|
|
3330
|
+
release: "SELECT RELEASE_LOCK(SHA2(CONCAT(COALESCE(DATABASE(), ''), ':', ?), 256)) AS `released`"
|
|
3331
|
+
};
|
|
3332
|
+
/**
|
|
3333
|
+
* cockroachdb accepts the postgres advisory lock functions, but they do not
|
|
3334
|
+
* lock anything, so it is deliberately missing.
|
|
3335
|
+
*/
|
|
3336
|
+
const statements = /* @__PURE__ */ new Map([
|
|
3337
|
+
["postgres", {
|
|
3338
|
+
acquire: "SELECT pg_try_advisory_lock(hashtextextended($1, 0)) AS \"acquired\"",
|
|
3339
|
+
release: "SELECT pg_advisory_unlock(hashtextextended($1, 0)) AS \"released\""
|
|
3340
|
+
}],
|
|
3341
|
+
["mysql", MYSQL_LOCK_STATEMENTS],
|
|
3342
|
+
["mariadb", MYSQL_LOCK_STATEMENTS]
|
|
3343
|
+
]);
|
|
3344
|
+
const POLL_INTERVAL = 100;
|
|
3345
|
+
/**
|
|
3346
|
+
* pg answers with a boolean, mysql with the string '1' / '0', mariadb with
|
|
3347
|
+
* the number 1 / 0 (either may be NULL). A truthiness check would read '0'
|
|
3348
|
+
* as acquired.
|
|
3349
|
+
*/
|
|
3350
|
+
function isDatabaseLockAcquired(rows) {
|
|
3351
|
+
if (!Array.isArray(rows) || rows.length === 0) return false;
|
|
3352
|
+
const value = Object.values(rows[0] ?? {})[0];
|
|
3353
|
+
return value === true || value === 1 || value === "1";
|
|
3354
|
+
}
|
|
3355
|
+
/**
|
|
3356
|
+
* Run a callback while holding a named advisory lock, so that only one
|
|
3357
|
+
* session at a time (across processes) runs it. The lock is released
|
|
3358
|
+
* afterwards, also when the callback throws. A transaction the callback
|
|
3359
|
+
* leaves open on the query runner is rolled back first.
|
|
3360
|
+
*
|
|
3361
|
+
* The lock is session scoped: the query runner must stay on one connection
|
|
3362
|
+
* for the whole call, which a typeorm QueryRunner does until it is released.
|
|
3363
|
+
* Nested calls with the same name on the same query runner do not block.
|
|
3364
|
+
*
|
|
3365
|
+
* Supported for postgres, mysql and mariadb.
|
|
3366
|
+
*
|
|
3367
|
+
* @throws DriverError on any other driver, unless `strict` is false.
|
|
3368
|
+
* @throws DatabaseLockError if the lock can not be acquired within `timeout`,
|
|
3369
|
+
* the query runner is in a transaction, or the callback left one open.
|
|
3370
|
+
*/
|
|
3371
|
+
async function withDatabaseLock(queryRunner, name, fn, options = {}) {
|
|
3372
|
+
const { type } = queryRunner.dataSource.options;
|
|
3373
|
+
const statement = statements.get(type);
|
|
3374
|
+
if (!statement) {
|
|
3375
|
+
if (options.strict === false) return fn();
|
|
3376
|
+
throw DriverError.lockNotSupported(type);
|
|
3377
|
+
}
|
|
3378
|
+
if (queryRunner.isTransactionActive) throw DatabaseLockError.transactionActive(name);
|
|
3379
|
+
const timeout = options.timeout ?? Infinity;
|
|
3380
|
+
const deadline = Date.now() + timeout;
|
|
3381
|
+
while (!isDatabaseLockAcquired(await queryRunner.query(statement.acquire, [name]))) {
|
|
3382
|
+
const remaining = deadline - Date.now();
|
|
3383
|
+
if (!(remaining > 0)) throw DatabaseLockError.timeout(name, timeout);
|
|
3384
|
+
await new Promise((resolve) => {
|
|
3385
|
+
setTimeout(resolve, Math.min(POLL_INTERVAL, remaining));
|
|
3386
|
+
});
|
|
3387
|
+
}
|
|
3388
|
+
let output;
|
|
3389
|
+
try {
|
|
3390
|
+
output = await fn();
|
|
3391
|
+
} catch (e) {
|
|
3392
|
+
await rollbackTransaction(queryRunner);
|
|
3393
|
+
await queryRunner.query(statement.release, [name]).catch(() => void 0);
|
|
3394
|
+
throw e;
|
|
3395
|
+
}
|
|
3396
|
+
const transactionLeftOpen = await rollbackTransaction(queryRunner);
|
|
3397
|
+
await queryRunner.query(statement.release, [name]);
|
|
3398
|
+
if (transactionLeftOpen) throw DatabaseLockError.transactionLeftOpen(name);
|
|
3399
|
+
return output;
|
|
3400
|
+
}
|
|
3401
|
+
/**
|
|
3402
|
+
* A transaction the callback left open (or postgres aborted) would make
|
|
3403
|
+
* postgres refuse the release, and the lock would go back to the pool held.
|
|
3404
|
+
*
|
|
3405
|
+
* Nested transactions are savepoints in typeorm: one rollback only unwinds
|
|
3406
|
+
* the innermost, so repeat until the outer one is gone (or a rollback fails).
|
|
3407
|
+
*/
|
|
3408
|
+
async function rollbackTransaction(queryRunner) {
|
|
3409
|
+
if (!queryRunner.isTransactionActive) return false;
|
|
3410
|
+
try {
|
|
3411
|
+
while (queryRunner.isTransactionActive) await queryRunner.rollbackTransaction();
|
|
3412
|
+
} catch {}
|
|
3413
|
+
return true;
|
|
3414
|
+
}
|
|
3415
|
+
//#endregion
|
|
2615
3416
|
//#region src/helpers/entity/name.ts
|
|
2616
3417
|
function getEntityName(entity) {
|
|
2617
3418
|
if (typeof entity === "string") return entity;
|
|
@@ -2889,7 +3690,10 @@ var SeederEntity = class {
|
|
|
2889
3690
|
this.id = ctx.id;
|
|
2890
3691
|
this.timestamp = ctx.timestamp;
|
|
2891
3692
|
this.name = ctx.name;
|
|
2892
|
-
if (ctx.constructor)
|
|
3693
|
+
if (ctx.constructor) {
|
|
3694
|
+
const SeederClass = ctx.constructor;
|
|
3695
|
+
this.instance = new SeederClass();
|
|
3696
|
+
}
|
|
2893
3697
|
this.fileName = ctx.fileName;
|
|
2894
3698
|
this.filePath = ctx.filePath;
|
|
2895
3699
|
}
|
|
@@ -3282,6 +4086,6 @@ async function runSeeders(dataSource, options) {
|
|
|
3282
4086
|
return new SeederExecutor(dataSource).execute(options);
|
|
3283
4087
|
}
|
|
3284
4088
|
//#endregion
|
|
3285
|
-
export { CockroachDBDialect, CodeTransformation, DriverError, EntityMetadataError, EntityRelationLookupError, EnvironmentName, EnvironmentVariableName, MYSQL_FOREIGN_KEY_CHECKS_OFF, MYSQL_FOREIGN_KEY_CHECKS_ON, MYSQL_FOREIGN_KEY_CHECKS_SELECT, MongoDBDialect, MsSQLDialect, MySQLDialect, OptionsError, OracleDialect, PathResolver, PathResolverMode, PostgresDialect, SQLiteDialect, SchemaAlterationError, SchemaDriftError, SeederEntity, SeederExecutor, SeederFactory, SeederFactoryManager, TypeormExtensionError, adjustFilePath, adjustFilePaths, assertSchemaMatchesMetadata, buildCockroachDBCreateDatabaseQuery, buildCockroachDBDropDatabaseQuery, buildConnectionParams, buildDataSourceOptions, buildDatabaseCreateContext, buildDatabaseDropContext, buildMongoDBConnectionUri, buildMongoDBDropDatabaseCommand, buildMsSQLCreateDatabaseQuery, buildMsSQLDropDatabaseQuery, buildMySQLCreateDatabaseQuery, buildMySQLDropDatabaseQuery, buildOracleConnectString, buildOracleCreateDatabaseQuery, buildPostgresCreateDatabaseQuery, buildPostgresCreateSchemaQuery, buildPostgresDatabaseExistsQuery, buildPostgresDropDatabaseQuery, buildSeederFileName, buildSeederFileTemplate, canReplaceWindowsSeparator, changeColumnType, checkDatabase, createCockroachDBDatabase, createDatabase, createMongoDBDatabase, createMsSQLDatabase, createMySQLDatabase, createOracleDatabase, createPathResolver, createPostgresDatabase, createSQLiteDatabase, deriveMySQLCharacterSet, detectCodeTransformation, dropCockroachDBDatabase, dropDatabase, dropMongoDBDatabase, dropMsSQLDatabase, dropMySQLDatabase, dropOracleDatabase, dropPostgresDatabase, dropSQLiteDatabase, existsQuery, extendObject, findDataSource, generateMigration, getEntityMetadata, getEntityName, getEntityPropertyNames, getSchemaDrift, hasDataSource, hasDataSourceOptions, hasEnvDataSourceOptions, hasOwnProperty, hasResultRows, hasStringProperty, hasTrailingSlash, isCodeTransformation, isDirectory, isEntityUnique, isObject, isPromise, mergeDataSourceOptions, mergeDataSourceOptionsWithEnv, parseFilePath, pickRecord, prepareSeederFactories, prepareSeederSeeds, readDataSourceOptionsFromEnv, readTSConfig, renameForeignKey, renameIndex, replaceWindowSeparator, resetEnv, resetSeederFactoryManager, resolveFilePath, resolveFilePaths, resolveFilePatterns, resolveSQLiteDatabaseDirectory, resolveSQLiteDatabasePath, resolveSeederConfig, runSeeder, runSeeders, safeReplaceWindowsSeparator, setDataSource, setDataSourceOptions, setSeederFactory, synchronizeDatabaseSchema, transformFilePath, unsetDataSource, useDataSource, useDataSourceOptions, useEnv, useSeederFactory, useSeederFactoryManager, validateEntityJoinColumns, withForeignKeyChecksDisabled, withoutTrailingSlash };
|
|
4089
|
+
export { CockroachDBDialect, CodeTransformation, DatabaseLockError, DriverError, EntityMetadataError, EntityRelationLookupError, EnvironmentName, EnvironmentVariableName, MYSQL_FOREIGN_KEY_CHECKS_OFF, MYSQL_FOREIGN_KEY_CHECKS_ON, MYSQL_FOREIGN_KEY_CHECKS_SELECT, MongoDBDialect, MsSQLDialect, MySQLDialect, OptionsError, OracleDialect, PathResolver, PathResolverMode, PostgresDialect, SQLiteDialect, SchemaAlterationError, SchemaDriftError, SeederEntity, SeederExecutor, SeederFactory, SeederFactoryManager, TypeormExtensionError, adjustFilePath, adjustFilePaths, assertSchemaMatchesMetadata, buildCockroachDBCreateDatabaseQuery, buildCockroachDBDropDatabaseQuery, buildConnectionParams, buildDataSourceOptions, buildDatabaseCreateContext, buildDatabaseDropContext, buildMongoDBConnectionUri, buildMongoDBDropDatabaseCommand, buildMsSQLCreateDatabaseQuery, buildMsSQLDropDatabaseQuery, buildMySQLCreateDatabaseQuery, buildMySQLDropDatabaseQuery, buildOracleConnectString, buildOracleCreateDatabaseQuery, buildPostgresCreateDatabaseQuery, buildPostgresCreateSchemaQuery, buildPostgresDatabaseExistsQuery, buildPostgresDropDatabaseQuery, buildSeederFileName, buildSeederFileTemplate, canReplaceWindowsSeparator, changeColumnType, checkDatabase, createCockroachDBDatabase, createDatabase, createMongoDBDatabase, createMsSQLDatabase, createMySQLDatabase, createOracleDatabase, createPathResolver, createPostgresDatabase, createSQLiteDatabase, deriveMySQLCharacterSet, detectCodeTransformation, dropCockroachDBDatabase, dropDatabase, dropMongoDBDatabase, dropMsSQLDatabase, dropMySQLDatabase, dropOracleDatabase, dropPostgresDatabase, dropSQLiteDatabase, existsQuery, extendObject, findDataSource, generateMigration, getEntityMetadata, getEntityName, getEntityPropertyNames, getSchemaDrift, hasDataSource, hasDataSourceOptions, hasEnvDataSourceOptions, hasOwnProperty, hasResultRows, hasStringProperty, hasTrailingSlash, isCodeTransformation, isDirectory, isEntityUnique, isObject, isPromise, mergeDataSourceOptions, mergeDataSourceOptionsWithEnv, parseFilePath, pickRecord, pinTimezone, prepareSeederFactories, prepareSeederSeeds, readDataSourceOptionsFromEnv, readTSConfig, renameForeignKey, renameIndex, replaceWindowSeparator, resetEnv, resetSeederFactoryManager, resolveFilePath, resolveFilePaths, resolveFilePatterns, resolveSQLiteDatabaseDirectory, resolveSQLiteDatabasePath, resolveSeederConfig, runSeeder, runSeeders, safeReplaceWindowsSeparator, setDataSource, setDataSourceOptions, setSeederFactory, synchronizeDatabaseSchema, transformFilePath, unsetDataSource, useDataSource, useDataSourceOptions, useEnv, useSeederFactory, useSeederFactoryManager, validateEntityJoinColumns, withDatabaseLock, withForeignKeyChecksDisabled, withoutTrailingSlash };
|
|
3286
4090
|
|
|
3287
4091
|
//# sourceMappingURL=index.mjs.map
|