UNPKG

typeorm-extension

Version:

A library to create/drop database, simple seeding data sets, ...

1,500 lines (1,462 loc) 111 kB
import { parseQueryFields, FilterComparisonOperator, parseQueryFilters, parseQueryPagination, parseQueryRelations, parseQuerySort, parseQuery } from 'rapiq'; import process$1 from 'node:process'; import { InstanceChecker, Brackets, DataSource, MigrationExecutor, Table, MssqlParameter, Repository } from 'typeorm'; import { load, isObject, removeFileNameExtension, locate, locateMany, getFileNameExtension } from 'locter'; import path from 'node:path'; import fs from 'node:fs'; import { consola } from 'consola'; import { toBool, oneOf, read, readInt, readBool, toArray, readArray } from 'envix'; import { createMerger } from 'smob'; import { DriverUtils } from 'typeorm/driver/DriverUtils.js'; import { DriverFactory } from 'typeorm/driver/DriverFactory.js'; import { pascalCase } from 'pascal-case'; import { MigrationGenerateCommand } from 'typeorm/commands/MigrationGenerateCommand.js'; import { Faker } from '@faker-js/faker'; class TypeormExtensionError extends Error { } class DriverError extends TypeormExtensionError { static undeterminable() { return new DriverError('The driver could not be determined.'); } static notSupported(driverName) { return new DriverError(`The driver ${driverName} is not supported yet.`); } constructor(message){ super(message || 'A database driver related error has occurred.'); } } class OptionsError extends TypeormExtensionError { static undeterminable() { return new OptionsError('The database options could not be determined.'); } static notFound() { return new OptionsError('The database options could not be located/loaded.'); } static databaseNotDefined() { return new OptionsError('The database name to connect to is not defined.'); } constructor(message){ super(message || 'A database options related error has occurred'); } } function getAliasForPath(items, path) { if (typeof path === 'undefined' || typeof items === 'undefined') { return undefined; } for(let i = 0; i < items.length; i++){ if (items[i].key === path) { return items[i].value; } } return undefined; } function buildKeyWithPrefix(name, prefix) { if (prefix) { return `${prefix}.${name}`; } return name; } var CodeTransformation = /*#__PURE__*/ function(CodeTransformation) { CodeTransformation["JUST_IN_TIME"] = "jit"; CodeTransformation["NONE"] = "none"; return CodeTransformation; }({}); function detectCodeTransformation() { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore if (process$1[Symbol.for('ts-node.register.instance')]) { return CodeTransformation.JUST_IN_TIME; } return CodeTransformation.NONE; } function isCodeTransformation(input) { return detectCodeTransformation() === input; } function getEntityName(entity) { if (typeof entity === 'function') { return entity.name; } if (InstanceChecker.isEntitySchema(entity)) { return entity.options.name; } return new entity().constructor.name; } function canReplaceWindowsSeparator(input) { // https://superuser.com/questions/176388/why-does-windows-use-backslashes-for-paths-and-unix-forward-slashes/176395#176395 if (input.startsWith('\\\\?\\')) { return false; } let characterIndex; const specialCharacters = [ '[', '{', '(', '^', '$', '.', '|', '?', '*', '+' ]; for(let i = 0; i < specialCharacters.length; i++){ characterIndex = input.indexOf(specialCharacters[i]); if (characterIndex !== -1) { // special character is prefixed with \, no transformation allowed if (characterIndex !== 0 && input[characterIndex - 1] === '\\') { return false; } } } return true; } function replaceWindowSeparator(input) { return input.replace(/\\/g, '/'); } function safeReplaceWindowsSeparator(input) { if (input.indexOf('\\') === -1 || !canReplaceWindowsSeparator(input)) { return input; } return replaceWindowSeparator(input); } const TRAILING_SLASH_RE = /\/$|\/\?/; function hasTrailingSlash(input = '', queryParams = false) { if (!queryParams) { return input.endsWith('/'); } return TRAILING_SLASH_RE.test(input); } function withoutTrailingSlash(input = '', queryParams = false) { if (!queryParams) { return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || '/'; } if (!hasTrailingSlash(input, true)) { return input || '/'; } const [s0, ...s] = input.split('?'); return (s0.slice(0, -1) || '/') + (s.length ? `?${s.join('?')}` : ''); } async function readTSConfig(input) { input = input || process.cwd(); input = path.isAbsolute(input) ? input : path.resolve(process.cwd(), input); const filePath = input.indexOf('.json') === -1 ? path.join(input, 'tsconfig.json') : input; try { const tsConfig = await load(filePath); if (isObject(tsConfig)) { return tsConfig; } } catch (e) { // don't do anything ;) } return {}; } const stripLeadingModifier = (text)=>{ if (text.startsWith('./')) { text = text.substring(2); } return text; }; function transformFilePath(input, dist, src) { let separator = path.sep; const windowsSeparatorReplaceable = canReplaceWindowsSeparator(input); if (windowsSeparatorReplaceable) { separator = '/'; input = replaceWindowSeparator(input); } let base = input; let baseIndex = input.lastIndexOf(separator); if (baseIndex !== -1) { base = base.substring(baseIndex + 1); } if (src) { if (windowsSeparatorReplaceable) { src = replaceWindowSeparator(src); } src = withoutTrailingSlash(stripLeadingModifier(src)); } src = src || 'src'; if (dist) { if (windowsSeparatorReplaceable) { dist = replaceWindowSeparator(dist); } dist = withoutTrailingSlash(stripLeadingModifier(dist)); } dist = dist || 'dist'; if (input.indexOf(src) !== -1 && input.indexOf(dist) === -1) { const lastIndex = input.lastIndexOf(src); const prevCharacter = input.substring(lastIndex - 1, lastIndex); if (!prevCharacter || prevCharacter === separator) { input = input.substring(0, lastIndex) + dist + input.substring(lastIndex + src.length); baseIndex = input.lastIndexOf(separator); } } // if the path already contains a js file extension, we are done const jsExtensions = [ 'js', 'cjs', 'mjs' ]; for(let i = 0; i < jsExtensions.length; i++){ if (base.indexOf(jsExtensions[i]) !== -1) { return input; } } const tsExtensions = [ 'ts', 'cts', 'mts' ]; for(let i = 0; i < tsExtensions.length; i++){ const regex = new RegExp(`(\\.${tsExtensions[i]}|${tsExtensions[i]})`, 'g'); let matchesSum; const matches = base.match(regex); if (Array.isArray(matches)) { matchesSum = matches.length; } let matchesCounter = 0; const bracketIndex = base.lastIndexOf('{'); base = base.replace(regex, (...args)=>{ matchesCounter++; // if the file extension name comes after the last bracket index, // we can be pretty sure that the extension name is not part of a filename if (args[2] >= bracketIndex && bracketIndex !== -1 || bracketIndex === -1 && matchesCounter === matchesSum) { return args[0].startsWith('.') ? `.${jsExtensions[i]}` : jsExtensions[i]; } return args[0]; }); } if (baseIndex !== -1) { base = input.substring(0, baseIndex + 1) + base; } return stripLeadingModifier(base); } async function adjustFilePath(input, tsconfig) { if (isCodeTransformation(CodeTransformation.JUST_IN_TIME)) { return input; } if (!isObject(tsconfig)) { tsconfig = await readTSConfig(tsconfig); } const { compilerOptions } = tsconfig; if (typeof input === 'string') { return transformFilePath(input, compilerOptions?.outDir); } if (Array.isArray(input)) { for(let i = 0; i < input.length; i++){ if (typeof input[i] === 'string') { input[i] = transformFilePath(input[i], compilerOptions?.outDir); } } } return input; } async function adjustFilePaths(input, keys, tsconfig) { if (isCodeTransformation(CodeTransformation.JUST_IN_TIME)) { return input; } if (!isObject(tsconfig)) { tsconfig = await readTSConfig(tsconfig); } keys = keys || Object.keys(input); for(let i = 0; i < keys.length; i++){ input[keys[i]] = await adjustFilePath(input[keys[i]], tsconfig); } return input; } function resolveFilePath(filePath, root) { if (path.isAbsolute(filePath)) { return filePath; } return filePath.startsWith('/') ? filePath : path.resolve(root || process.cwd(), filePath); } function parseFilePath(filePath, root) { const fullPath = resolveFilePath(filePath, root); const directory = path.dirname(fullPath); const name = path.basename(fullPath); return { directory, name }; } async function isDirectory(input) { try { const stat = await fs.promises.stat(input); return stat.isDirectory(); } catch (e) { return false; } } function hasOwnProperty(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); } function hasStringProperty(obj, prop) { return hasOwnProperty(obj, prop) && typeof obj[prop] === 'string'; } function pickRecord(data, keys) { const output = {}; for(let i = 0; i < keys.length; i++){ output[keys[i]] = data[keys[i]]; } return output; } function isPromise(p) { return isObject(p) && (p instanceof Promise || // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore typeof p.then === 'function'); } function isQueryOptionDefined(input, option) { if (typeof input === 'boolean') { return false; } const options = Array.isArray(option) ? option : [ option ]; for(let i = 0; i < options.length; i++){ if (hasOwnProperty(input, options[i])) { return true; } } return false; } /** * Apply parsed fields parameter data on the db query. * * @param query * @param data */ /* istanbul ignore next */ function applyQueryFieldsParseOutput(query, data, options = {}) { if (data.length === 0) { return data; } query.select(data.map((field)=>{ const alias = getAliasForPath(options.relations, field.path) || options.defaultAlias || options.defaultPath; return buildKeyWithPrefix(field.key, alias); })); return data; } /** * Apply raw fields parameter data on the db query. * * @param query * @param data * @param options */ function applyQueryFields(query, data, options) { options = options || {}; if (options.defaultAlias) { options.defaultPath = options.defaultAlias; } return applyQueryFieldsParseOutput(query, parseQueryFields(data, options), options); } /** * Apply raw fields parameter data on the db query. * * @param query * @param data * @param options */ function applyFields(query, data, options) { return applyQueryFields(query, data, options); } // -------------------------------------------------- function transformParsedFilters(data, options = {}) { const items = []; for(let i = 0; i < data.length; i++){ const alias = getAliasForPath(options.relations, data[i].path) || options.defaultAlias || options.defaultPath; const fullKey = buildKeyWithPrefix(data[i].key, alias); const filter = data[i]; const statement = [ fullKey ]; let bindingKey; if (options.bindingKey) { bindingKey = options.bindingKey(fullKey).replace('.', '_'); } else { bindingKey = `filter_${fullKey.replace('.', '_')}`; } if (filter.value === null || typeof filter.value === 'undefined') { statement.push('IS'); if (filter.operator === FilterComparisonOperator.NOT_EQUAL) { statement.push('NOT'); } statement.push('NULL'); items.push({ statement: statement.join(' '), binding: {} }); continue; } switch(filter.operator){ case FilterComparisonOperator.EQUAL: case FilterComparisonOperator.NOT_EQUAL: { if (filter.operator === FilterComparisonOperator.EQUAL) { statement.push('='); } else { statement.push('!='); } statement.push(`:${bindingKey}`); break; } case FilterComparisonOperator.LIKE: case FilterComparisonOperator.NOT_LIKE: { if (filter.operator === FilterComparisonOperator.NOT_LIKE) { statement.push('NOT'); } statement.push('LIKE'); statement.push(`:${bindingKey}`); filter.value += '%'; break; } case FilterComparisonOperator.IN: case FilterComparisonOperator.NOT_IN: { if (filter.operator === FilterComparisonOperator.NOT_IN) { statement.push('NOT'); } statement.push('IN'); statement.push(`(:...${bindingKey})`); if (Array.isArray(filter.value)) { const nullIndex = filter.value.indexOf(null); if (nullIndex !== -1) { filter.value.splice(nullIndex, 1); statement.unshift('('); if (filter.operator === FilterComparisonOperator.NOT_IN) { statement.push('AND'); } else { statement.push('OR'); } statement.push(fullKey); statement.push('IS'); if (filter.operator === FilterComparisonOperator.NOT_IN) { statement.push('NOT'); } statement.push('NULL'); statement.push(')'); } } break; } case FilterComparisonOperator.LESS_THAN: case FilterComparisonOperator.LESS_THAN_EQUAL: case FilterComparisonOperator.GREATER_THAN: case FilterComparisonOperator.GREATER_THAN_EQUAL: { if (filter.operator === FilterComparisonOperator.LESS_THAN) { statement.push('<'); } else if (filter.operator === FilterComparisonOperator.LESS_THAN_EQUAL) { statement.push('<='); } else if (filter.operator === FilterComparisonOperator.GREATER_THAN) { statement.push('>'); } else { statement.push('>='); } statement.push(`:${bindingKey}`); break; } } items.push({ statement: statement.join(' '), binding: { [bindingKey]: filter.value } }); } return items; } /** * Apply transformed filter[s] parameter data on the db query. * * @param query * @param data */ function applyFiltersTransformed(query, data) { if (data.length === 0) { return data; } /* istanbul ignore next */ query.andWhere(new Brackets((qb)=>{ for(let i = 0; i < data.length; i++){ if (i === 0) { qb.where(data[i].statement, data[i].binding); } else { qb.andWhere(data[i].statement, data[i].binding); } } })); return data; } /** * Apply parsed filter[s] parameter data on the db query. * * @param query * @param data * @param options */ function applyQueryFiltersParseOutput(query, data, options) { applyFiltersTransformed(query, transformParsedFilters(data, options)); return data; } // -------------------------------------------------- /** * Apply raw filter[s] parameter data on the db query. * * @param query * @param data * @param options */ function applyQueryFilters(query, data, options) { options = options || {}; if (options.defaultAlias) { options.defaultPath = options.defaultAlias; } return applyQueryFiltersParseOutput(query, parseQueryFilters(data, options), options); } /** * Apply raw filter[s] parameter data on the db query. * * @param query * @param data * @param options */ function applyFilters(query, data, options) { return applyQueryFilters(query, data, options); } /** * Apply parsed page/pagination parameter data on the db query. * * @param query * @param data */ function applyQueryPaginationParseOutput(query, data) { /* istanbul ignore next */ if (typeof data.limit !== 'undefined') { query.take(data.limit); if (typeof data.offset === 'undefined') { query.skip(0); } } /* istanbul ignore next */ if (typeof data.offset !== 'undefined') { query.skip(data.offset); } return data; } /** * Apply raw page/pagination parameter data on the db query. * * @param query * @param data * @param options */ function applyQueryPagination(query, data, options) { return applyQueryPaginationParseOutput(query, parseQueryPagination(data, options)); } /** * Apply raw page/pagination parameter data on the db query. * * @param query * @param data * @param options */ function applyPagination(query, data, options) { return applyQueryPagination(query, data, options); } /** * Apply parsed include/relation parameter data on the db query. * * @param query * @param data * @param options */ function applyQueryRelationsParseOutput(query, data, options) { options = options || {}; for(let i = 0; i < data.length; i++){ const parts = data[i].key.split('.'); let key; if (parts.length > 1) { key = parts.slice(-2).join('.'); } else { key = buildKeyWithPrefix(data[i].key, options.defaultAlias); } data[i].key = key; /* istanbul ignore next */ query.leftJoinAndSelect(key, data[i].value); } return data; } /** * Apply raw include/relations parameter data on the db query. * * @param query * @param data * @param options */ function applyQueryRelations(query, data, options) { return applyQueryRelationsParseOutput(query, parseQueryRelations(data, options), options); } /** * Apply raw include/relations parameter data on the db query. * * @param query * @param data * @param options */ function applyRelations(query, data, options) { return applyQueryRelations(query, data, options); } // -------------------------------------------------- /** * Apply parsed sort parameter data on the db query. * * @param query * @param data */ function applyQuerySortParseOutput(query, data) { if (data.length === 0) { return data; } const sort = {}; for(let i = 0; i < data.length; i++){ const key = buildKeyWithPrefix(data[i].key, data[i].path); sort[key] = data[i].value; } query.orderBy(sort); return data; } /** * Apply raw sort parameter data on the db query. * * @param query * @param data * @param options */ function applyQuerySort(query, data, options) { options = options || {}; if (options.defaultAlias) { options.defaultPath = options.defaultAlias; } return applyQuerySortParseOutput(query, parseQuerySort(data, options)); } /** * Apply raw sort parameter data on the db query. * * @param query * @param data * @param options */ function applySort(query, data, options) { return applyQuerySort(query, data, options); } function applyQueryParseOutput(query, context) { if (context.fields) { applyQueryFieldsParseOutput(query, context.fields, { defaultAlias: context.defaultPath, relations: context.relations }); } if (context.filters) { applyQueryFiltersParseOutput(query, context.filters, { defaultAlias: context.defaultPath, relations: context.relations }); } if (context.pagination) { applyQueryPaginationParseOutput(query, context.pagination); } if (context.relations) { applyQueryRelationsParseOutput(query, context.relations, { defaultAlias: context.defaultPath }); } if (context.sort) { applyQuerySortParseOutput(query, context.sort); } return context; } function applyQuery(query, input, options) { options = options || {}; if (options.defaultAlias) { options.defaultPath = options.defaultAlias; } if (typeof options.fields === 'undefined' || !isQueryOptionDefined(options.fields, [ 'allowed', 'default' ])) { options.fields = false; } if (typeof options.filters === 'undefined' || !isQueryOptionDefined(options.filters, [ 'allowed', 'default' ])) { options.filters = false; } if (typeof options.pagination === 'undefined') { options.pagination = false; } if (typeof options.relations === 'undefined' || !isQueryOptionDefined(options.relations, [ 'allowed' ])) { options.relations = false; } if (typeof options.sort === 'undefined' || !isQueryOptionDefined(options.sort, [ 'allowed', 'default' ])) { options.sort = false; } const output = applyQueryParseOutput(query, parseQuery(input, options)); return { ...output, ...options.defaultAlias ? { defaultAlias: options.defaultAlias } : {} }; } async function findDataSource(context = {}) { let tsconfig; if (!context.preserveFilePaths) { if (isObject(context.tsconfig)) { tsconfig = context.tsconfig; } else { tsconfig = await readTSConfig(context.tsconfig); } } const files = [ 'data-source' ]; if (context.fileName) { context.fileName = removeFileNameExtension(context.fileName, [ '.ts', '.mts', '.cts', '.js', '.mjs', '.cjs' ]); if (context.fileName !== 'data-source') { files.unshift(context.fileName); } } let { directory } = context; let directoryIsPattern = false; if (context.directory) { if (path.isAbsolute(context.directory)) { directory = context.directory; } else { directoryIsPattern = true; directory = safeReplaceWindowsSeparator(context.directory); } if (!context.preserveFilePaths) { directory = await adjustFilePath(directory, tsconfig); } } const lookupPaths = []; for(let j = 0; j < files.length; j++){ if (directory && directoryIsPattern) { lookupPaths.push(path.posix.join(directory, files[j])); } lookupPaths.push(...[ path.posix.join('src', files[j]), path.posix.join('src/{db,database}', files[j]) ]); } files.push(...lookupPaths); if (!context.preserveFilePaths) { for(let j = 0; j < files.length; j++){ files[j] = await adjustFilePath(files[j], tsconfig); } } for(let i = 0; i < files.length; i++){ const info = await locate(`${files[i]}.{js,cjs,mjs,ts,cts,mts}`, { path: [ process.cwd(), ...directory && !directoryIsPattern ? [ directory ] : [] ], ignore: [ '**/*.d.ts' ] }); if (info) { let moduleRecord = await load(info); if (isPromise(moduleRecord)) { moduleRecord = await moduleRecord; } if (InstanceChecker.isDataSource(moduleRecord)) { return moduleRecord; } if (!isObject(moduleRecord)) { continue; } const keys = Object.keys(moduleRecord); for(let j = 0; j < keys.length; j++){ let value = moduleRecord[keys[j]]; if (isPromise(value)) { value = await value; } if (InstanceChecker.isDataSource(value)) { return value; } } } } return undefined; } /* * Copyright (c) 2023-2023. * Author Peter Placzek (tada5hi) * For the full copyright and license information, * view the LICENSE file that was distributed with this source code. */ var EnvironmentName = /*#__PURE__*/ function(EnvironmentName) { EnvironmentName["DEVELOPMENT"] = "development"; EnvironmentName["PRODUCTION"] = "production"; EnvironmentName["TEST"] = "test"; return EnvironmentName; }({}); var EnvironmentVariableName = /*#__PURE__*/ function(EnvironmentVariableName) { EnvironmentVariableName["ENV"] = "NODE_ENV"; // Seeder EnvironmentVariableName["SEEDS"] = "DB_SEEDS"; EnvironmentVariableName["SEEDS_ALT"] = "TYPEORM_SEEDING_SEEDS"; EnvironmentVariableName["FACTORIES"] = "DB_FACTORIES"; EnvironmentVariableName["FACTORIES_ALT"] = "TYPEORM_SEEDING_FACTORIES"; // Database EnvironmentVariableName["TYPE"] = "DB_TYPE"; EnvironmentVariableName["TYPE_ALT"] = "TYPEORM_CONNECTION"; EnvironmentVariableName["URL"] = "DB_URL"; EnvironmentVariableName["URL_ALT"] = "TYPEORM_URL"; EnvironmentVariableName["HOST"] = "DB_HOST"; EnvironmentVariableName["HOST_ALT"] = "TYPEORM_HOST"; EnvironmentVariableName["PORT"] = "DB_PORT"; EnvironmentVariableName["PORT_ALT"] = "TYPEORM_PORT"; EnvironmentVariableName["USERNAME"] = "DB_USERNAME"; EnvironmentVariableName["USERNAME_ALT"] = "TYPEORM_USERNAME"; EnvironmentVariableName["PASSWORD"] = "DB_PASSWORD"; EnvironmentVariableName["PASSWORD_ALT"] = "TYPEORM_PASSWORD"; EnvironmentVariableName["DATABASE"] = "DB_DATABASE"; EnvironmentVariableName["DATABASE_ALT"] = "TYPEORM_DATABASE"; EnvironmentVariableName["SID"] = "DB_SID"; EnvironmentVariableName["SID_ALT"] = "TYPEORM_SID"; EnvironmentVariableName["SCHEMA"] = "DB_SCHEMA"; EnvironmentVariableName["SCHEMA_ALT"] = "TYPEORM_SCHEMA"; EnvironmentVariableName["SCHEMA_DROP"] = "DB_DROP_SCHEMA"; EnvironmentVariableName["SCHEMA_DROP_ALT"] = "TYPEORM_DROP_SCHEMA"; EnvironmentVariableName["DRIVER_EXTRA"] = "DB_DRIVER_EXTRA"; EnvironmentVariableName["DRIVER_EXTRA_ALT"] = "TYPEORM_DRIVER_EXTRA"; EnvironmentVariableName["SYNCHRONIZE"] = "DB_SYNCHRONIZE"; EnvironmentVariableName["SYNCHRONIZE_ALT"] = "TYPEORM_SYNCHRONIZE"; EnvironmentVariableName["MIGRATIONS"] = "DB_MIGRATIONS"; EnvironmentVariableName["MIGRATIONS_ALT"] = "TYPEORM_MIGRATIONS"; EnvironmentVariableName["MIGRATIONS_RUN"] = "DB_MIGRATIONS_RUN"; EnvironmentVariableName["MIGRATIONS_RUN_ALT"] = "TYPEORM_MIGRATIONS_RUN"; EnvironmentVariableName["MIGRATIONS_TABLE_NAME"] = "DB_MIGRATIONS_TABLE_NAME"; EnvironmentVariableName["MIGRATIONS_TABLE_NAME_ALT"] = "TYPEORM_MIGRATIONS_TABLE_NAME"; EnvironmentVariableName["ENTITIES"] = "DB_ENTITIES"; EnvironmentVariableName["ENTITIES_ALT"] = "TYPEORM_ENTITIES"; EnvironmentVariableName["ENTITY_PREFIX"] = "DB_ENTITY_PREFIX"; EnvironmentVariableName["ENTITY_PREFIX_ALT"] = "TYPEORM_ENTITY_PREFIX"; EnvironmentVariableName["METADATA_TABLE_NAME"] = "DB_METADATA_TABLE_NAME"; EnvironmentVariableName["METADATA_TABLE_NAME_ALT"] = "TYPEORM_METADATA_TABLE_NAME"; EnvironmentVariableName["SUBSCRIBERS"] = "DB_SUBSCRIBERS"; EnvironmentVariableName["SUBSCRIBERS_ALT"] = "TYPEORM_SUBSCRIBERS"; EnvironmentVariableName["LOGGING"] = "DB_LOGGING"; EnvironmentVariableName["LOGGING_ALT"] = "TYPEORM_LOGGING"; EnvironmentVariableName["LOGGER"] = "DB_LOGGER"; EnvironmentVariableName["LOGGER_ALT"] = "TYPEORM_LOGGER"; EnvironmentVariableName["MAX_QUERY_EXECUTION_TIME"] = "DB_MAX_QUERY_EXECUTION_TIME"; EnvironmentVariableName["MAX_QUERY_EXECUTION_TIME_ALT"] = "TYPEORM_MAX_QUERY_EXECUTION_TIME"; EnvironmentVariableName["DEBUG"] = "DB_DEBUG"; EnvironmentVariableName["DEBUG_ALT"] = "TYPEORM_DEBUG"; EnvironmentVariableName["UUID_EXTENSION"] = "DB_UUID_EXTENSION"; EnvironmentVariableName["UUID_EXTENSION_ALT"] = "TYPEORM_UUID_EXTENSION"; EnvironmentVariableName["CACHE"] = "DB_CACHE"; EnvironmentVariableName["CACHE_ALT"] = "TYPEORM_CACHE"; EnvironmentVariableName["CACHE_ALWAYS_ENABLED"] = "DB_CACHE_ALWAYS_ENABLED"; EnvironmentVariableName["CACHE_ALWAYS_ENABLED_ALT"] = "TYPEORM_CACHE_ALWAYS_ENABLED"; EnvironmentVariableName["CACHE_OPTIONS"] = "DB_CACHE_OPTIONS"; EnvironmentVariableName["CACHE_OPTIONS_ALT"] = "TYPEORM_CACHE_OPTIONS"; EnvironmentVariableName["CACHE_DURATION"] = "DB_CACHE_DURATION"; EnvironmentVariableName["CACHE_DURATION_ALT"] = "TYPEORM_CACHE_DURATION"; return EnvironmentVariableName; }({}); function transformLogging(input) { const value = toBool(input); if (typeof value === 'boolean') { return value; } if (input === 'all') { return 'all'; } return toArray(input) ?? []; } function transformCache(input) { const value = toBool(input); if (typeof value === 'boolean') { return value; } if (input === 'redis' || input === 'ioredis' || input === 'database' || input === 'ioredis/cluster') { let options; const envCacheOptions = oneOf([ read(EnvironmentVariableName.CACHE_OPTIONS), read(EnvironmentVariableName.CACHE_OPTIONS_ALT) ]); if (envCacheOptions) { options = JSON.parse(envCacheOptions); } return { type: input, options, alwaysEnabled: oneOf([ readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED), readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED_ALT) ]), duration: oneOf([ readInt(EnvironmentVariableName.CACHE_DURATION), readInt(EnvironmentVariableName.CACHE_DURATION_ALT) ]) }; } return undefined; } let instance$1; function useEnv(key) { if (typeof instance$1 !== 'undefined') { if (typeof key === 'string') { return instance$1[key]; } return instance$1; } const output = { env: read(EnvironmentVariableName.ENV, EnvironmentName.DEVELOPMENT), // Seeder seeds: oneOf([ readArray(EnvironmentVariableName.SEEDS), readArray(EnvironmentVariableName.SEEDS_ALT) ]) ?? [], factories: oneOf([ readArray(EnvironmentVariableName.FACTORIES), readArray(EnvironmentVariableName.FACTORIES_ALT) ]) ?? [], // Database url: oneOf([ read(EnvironmentVariableName.URL), read(EnvironmentVariableName.URL_ALT) ]), host: oneOf([ read(EnvironmentVariableName.HOST), read(EnvironmentVariableName.HOST_ALT) ]), port: oneOf([ readInt(EnvironmentVariableName.PORT), readInt(EnvironmentVariableName.PORT_ALT) ]), username: oneOf([ read(EnvironmentVariableName.USERNAME), read(EnvironmentVariableName.USERNAME_ALT) ]), password: oneOf([ read(EnvironmentVariableName.PASSWORD), read(EnvironmentVariableName.PASSWORD_ALT) ]), database: oneOf([ read(EnvironmentVariableName.DATABASE), read(EnvironmentVariableName.DATABASE_ALT) ]), sid: oneOf([ read(EnvironmentVariableName.SID), read(EnvironmentVariableName.SID_ALT) ]), schema: oneOf([ read(EnvironmentVariableName.SCHEMA), read(EnvironmentVariableName.SCHEMA_ALT) ]), extra: oneOf([ read(EnvironmentVariableName.DRIVER_EXTRA), read(EnvironmentVariableName.DRIVER_EXTRA_ALT) ]), synchronize: oneOf([ readBool(EnvironmentVariableName.SYNCHRONIZE), readBool(EnvironmentVariableName.SYNCHRONIZE_ALT) ]), schemaDrop: oneOf([ readBool(EnvironmentVariableName.SCHEMA_DROP), readBool(EnvironmentVariableName.SCHEMA_DROP_ALT) ]), migrationsRun: oneOf([ readBool(EnvironmentVariableName.MIGRATIONS_RUN), readBool(EnvironmentVariableName.MIGRATIONS_RUN_ALT) ]), entities: oneOf([ readArray(EnvironmentVariableName.ENTITIES), readArray(EnvironmentVariableName.ENTITIES_ALT) ]) ?? [], migrations: oneOf([ readArray(EnvironmentVariableName.MIGRATIONS), readArray(EnvironmentVariableName.MIGRATIONS_ALT) ]) ?? [], migrationsTableName: oneOf([ read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME), read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME_ALT) ]), metadataTableName: oneOf([ read(EnvironmentVariableName.METADATA_TABLE_NAME), read(EnvironmentVariableName.METADATA_TABLE_NAME_ALT) ]), subscribers: oneOf([ readArray(EnvironmentVariableName.SUBSCRIBERS), readArray(EnvironmentVariableName.SUBSCRIBERS_ALT) ]) ?? [], logging: transformLogging(oneOf([ read(EnvironmentVariableName.LOGGING), read(EnvironmentVariableName.LOGGING_ALT) ])), logger: oneOf([ read(EnvironmentVariableName.LOGGER), read(EnvironmentVariableName.LOGGER_ALT) ]), entityPrefix: oneOf([ read(EnvironmentVariableName.ENTITY_PREFIX), read(EnvironmentVariableName.ENTITY_PREFIX_ALT) ]), maxQueryExecutionTime: oneOf([ readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME), readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME_ALT) ]), debug: oneOf([ read(EnvironmentVariableName.DEBUG), read(EnvironmentVariableName.DEBUG_ALT) ]), cache: transformCache(oneOf([ read(EnvironmentVariableName.CACHE), read(EnvironmentVariableName.CACHE_ALT) ])), uuidExtension: oneOf([ read(EnvironmentVariableName.UUID_EXTENSION), read(EnvironmentVariableName.UUID_EXTENSION_ALT) ]) }; if (output.extra) { output.extra = JSON.parse(output.extra); // todo: ensure record<string,any> ?? } let type; const envType = oneOf([ read(EnvironmentVariableName.TYPE), read(EnvironmentVariableName.TYPE_ALT) ]); if (envType) { type = envType; } else { const envURL = oneOf([ read(EnvironmentVariableName.URL), read(EnvironmentVariableName.URL_ALT) ]); if (envURL) { [type] = envURL.split('://'); } } if (type) { output.type = type; // todo: maybe validation here } instance$1 = output; if (typeof key === 'string') { return output[key]; } return instance$1; } function resetEnv() { if (typeof instance$1 !== 'undefined') { instance$1 = undefined; } } const merge = createMerger({ strategy: (target, key, value)=>{ if (typeof target[key] === 'undefined') { target[key] = value; return target; } return undefined; } }); function mergeDataSourceOptions(target, source) { if (target.type !== source.type) { return target; } return merge(target, source); } function hasEnvDataSourceOptions() { return !!useEnv('type'); } /* istanbul ignore next */ function readDataSourceOptionsFromEnv() { if (!hasEnvDataSourceOptions()) { return undefined; } // todo: include seeder options const base = { type: useEnv('type'), entities: useEnv('entities'), subscribers: useEnv('subscribers'), migrations: useEnv('migrations'), migrationsTableName: useEnv('migrationsTableName'), // migrationsTransactionMode: useEnv('migra') metadataTableName: useEnv('metadataTableName'), logging: useEnv('logging'), logger: useEnv('logger'), maxQueryExecutionTime: useEnv('maxQueryExecutionTime'), synchronize: useEnv('synchronize'), migrationsRun: useEnv('migrationsRun'), dropSchema: useEnv('schemaDrop'), entityPrefix: useEnv('entityPrefix'), extra: useEnv('extra'), cache: useEnv('cache') }; const credentialOptions = { url: useEnv('url'), host: useEnv('host'), port: useEnv('port'), username: useEnv('username'), password: useEnv('password'), database: useEnv('database') }; if (base.type === 'mysql' || base.type === 'mariadb') { return { ...base, ...credentialOptions, type: base.type }; } if (base.type === 'postgres') { return { ...base, ...credentialOptions, type: base.type, schema: useEnv('schema'), uuidExtension: useEnv('uuidExtension') }; } if (base.type === 'cockroachdb') { return { ...base, ...credentialOptions, type: base.type, schema: useEnv('schema'), timeTravelQueries: true }; } if (base.type === 'sqlite') { return { ...base, type: base.type, database: useEnv('database') || 'db.sqlite' }; } if (base.type === 'better-sqlite3') { return { ...base, type: base.type, database: useEnv('database') || 'db.sqlite' }; } if (base.type === 'mssql') { return { ...base, ...credentialOptions, type: base.type, schema: useEnv('schema') }; } if (base.type === 'oracle') { return { ...base, ...credentialOptions, type: base.type, sid: useEnv('sid') }; } return { ...base, ...credentialOptions }; } function mergeDataSourceOptionsWithEnv(options) { const env = readDataSourceOptionsFromEnv(); if (!env) { return options; } return mergeDataSourceOptions(env, options); } /** * Build DataSourceOptions from DataSource or from configuration. * * @param context */ async function buildDataSourceOptions(context = {}) { const directory = context.directory || process.cwd(); let tsconfig; if (!context.preserveFilePaths) { if (isObject(context.tsconfig)) { tsconfig = context.tsconfig; } else { tsconfig = await readTSConfig(context.tsconfig); } } const dataSource = await findDataSource({ directory, fileName: context.dataSourceName, tsconfig }); if (dataSource) { if (context.preserveFilePaths) { return mergeDataSourceOptionsWithEnv(dataSource.options); } const options = await adjustFilePaths(dataSource.options, [ 'entities', 'migrations', 'subscribers' ], tsconfig); return mergeDataSourceOptionsWithEnv(options); } const options = readDataSourceOptionsFromEnv(); if (options) { if (context.preserveFilePaths) { return options; } return adjustFilePaths(options, [ 'entities', 'migrations', 'subscribers' ], tsconfig); } throw OptionsError.notFound(); } const instances$1 = {}; const instancePromises = {}; function setDataSourceOptions(options, alias) { instances$1[alias || 'default'] = options; } function hasDataSourceOptions(alias) { return Object.prototype.hasOwnProperty.call(instances$1, alias || 'default'); } async function useDataSourceOptions(alias) { alias = alias || 'default'; if (Object.prototype.hasOwnProperty.call(instances$1, alias)) { return instances$1[alias]; } /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(instancePromises, alias)) { instancePromises[alias] = buildDataSourceOptions().catch((e)=>{ if (alias) { delete instancePromises[alias]; } throw e; }); } instances$1[alias] = await instancePromises[alias]; return instances$1[alias]; } const instances = {}; const initializePromises = {}; const optionsPromises = {}; function setDataSource(dataSource, alias) { alias = alias || 'default'; instances[alias] = dataSource; } function hasDataSource(alias) { alias = alias || 'default'; return Object.prototype.hasOwnProperty.call(instances, alias); } function unsetDataSource(alias) { alias = alias || 'default'; if (Object.prototype.hasOwnProperty.call(instances, alias)) { delete instances[alias]; } /* istanbul ignore next */ if (Object.prototype.hasOwnProperty.call(optionsPromises, alias)) { delete optionsPromises[alias]; } /* istanbul ignore next */ if (Object.prototype.hasOwnProperty.call(initializePromises, alias)) { delete initializePromises[alias]; } } async function useDataSource(alias) { alias = alias || 'default'; if (Object.prototype.hasOwnProperty.call(instances, alias)) { if (!instances[alias].isInitialized) { /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(initializePromises, alias)) { initializePromises[alias] = instances[alias].initialize().catch((e)=>{ if (alias) { delete initializePromises[alias]; } throw e; }); } await initializePromises[alias]; } return instances[alias]; } /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(optionsPromises, alias)) { optionsPromises[alias] = useDataSourceOptions(alias).catch((e)=>{ if (alias) { delete optionsPromises[alias]; } throw e; }); } const options = await optionsPromises[alias]; const dataSource = new DataSource(options); /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(initializePromises, alias)) { initializePromises[alias] = dataSource.initialize().catch((e)=>{ if (alias) { delete initializePromises[alias]; } throw e; }); } await initializePromises[alias]; instances[alias] = dataSource; return dataSource; } /** * Check database setup progress. * * @param context */ async function checkDatabase(context = {}) { const result = { exists: true, schema: false, migrationsPending: [] }; let dataSource; let dataSourceCleanup; if (typeof context.dataSource === 'undefined' && typeof context.options === 'undefined' && hasDataSource(context.alias)) { dataSource = await useDataSource(context.alias); if (dataSource.options.synchronize || dataSource.options.migrationsRun) { dataSource = new DataSource({ ...dataSource.options, synchronize: false, migrationsRun: false }); dataSourceCleanup = true; } else { dataSourceCleanup = false; } } else { let dataSourceOptions; if (context.options) { dataSourceOptions = context.options; } else { dataSourceOptions = await useDataSourceOptions(context.alias); } dataSource = new DataSource({ ...dataSourceOptions, synchronize: false, migrationsRun: false }); dataSourceCleanup = context.dataSourceCleanup ?? true; } try { if (!dataSource.isInitialized) { await dataSource.initialize(); } } catch (e) { result.exists = false; return result; } const queryRunner = dataSource.createQueryRunner(); if (dataSource.migrations && dataSource.migrations.length > 0) { const migrationExecutor = new MigrationExecutor(dataSource, queryRunner); result.migrationsPending = await migrationExecutor.getPendingMigrations(); result.schema = result.migrationsPending.length === 0; } else { let schema; if (hasStringProperty(dataSource.driver.options, 'schema')) { schema = dataSource.driver.options.schema; } const migrationsTableName = dataSource.driver.buildTableName(dataSource.options.migrationsTableName || 'migrations', schema, dataSource.driver.database); const migrationsTableExists = await queryRunner.hasTable(migrationsTableName); if (migrationsTableExists) { result.schema = dataSource.entityMetadatas.length === 0; } else { const tableNames = dataSource.entityMetadatas.map((entityMetadata)=>entityMetadata.tablePath); const tables = await queryRunner.getTables(tableNames); if (tables.length === dataSource.entityMetadatas.length) { const { upQueries } = await dataSource.driver.createSchemaBuilder().log(); result.schema = upQueries.length === 0; } else { result.schema = false; } } } await queryRunner.release(); if (dataSourceCleanup) { await dataSource.destroy(); } return result; } function getCharsetFromDataSourceOptions(options) { if (hasOwnProperty(options, 'charset') && typeof options.charset === 'string') { return options.charset; } if (typeof options?.extra?.charset === 'string') { return options.extra.charset; } return undefined; } function getCharacterSetFromDataSourceOptions(options) { if (hasOwnProperty(options, 'characterSet') && typeof options.characterSet === 'string') { return options.characterSet; } if (typeof options?.extra?.characterSet === 'string') { return options.extra.characterSet; } return undefined; } function buildDriverOptions(options) { let driverOptions; switch(options.type){ case 'mysql': case 'mariadb': case 'postgres': case 'cockroachdb': case 'mssql': case 'oracle': driverOptions = DriverUtils.buildDriverOptions(options.replication ? options.replication.master : options); break; case 'mongodb': driverOptions = DriverUtils.buildMongoDBDriverOptions(options); break; default: driverOptions = DriverUtils.buildDriverOptions(options); } const charset = getCharsetFromDataSourceOptions(options); const characterSet = getCharacterSetFromDataSourceOptions(options); return { host: driverOptions.host, user: driverOptions.user || driverOptions.username, password: driverOptions.password, database: driverOptions.database, port: driverOptions.port, ...charset ? { charset } : {}, ...characterSet ? { characterSet } : {}, ...driverOptions.ssl ? { ssl: driverOptions.ssl } : {}, ...driverOptions.url ? { url: driverOptions.url } : {}, ...driverOptions.connectString ? { connectString: driverOptions.connectString } : {}, ...driverOptions.sid ? { sid: driverOptions.sid } : {}, ...driverOptions.serviceName ? { serviceName: driverOptions.serviceName } : {}, ...driverOptions.template ? { template: driverOptions.template } : {}, ...options.extra ? { extra: options.extra } : {}, ...driverOptions.domain ? { domain: driverOptions.domain } : {}, ...driverOptions.schema ? { schema: driverOptions.schema } : {} }; } const driversRequireDatabaseOption = [ 'sqlite', 'better-sqlite3' ]; function createDriver(connectionOptions) { const fakeConnection = { options: { type: connectionOptions.type, ...driversRequireDatabaseOption.indexOf(connectionOptions.type) !== -1 ? { database: connectionOptions.database