'use strict'; var rapiq = require('rapiq'); var process$1 = require('node:process'); var typeorm = require('typeorm'); var locter = require('locter'); var path = require('node:path'); var fs = require('node:fs'); var consola = require('consola'); var envix = require('envix'); var smob = require('smob'); var DriverUtils = require('typeorm/driver/DriverUtils'); var DriverFactory = require('typeorm/driver/DriverFactory'); var pascalCase = require('pascal-case'); var MigrationGenerateCommand = require('typeorm/commands/MigrationGenerateCommand'); var faker = require('@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 (typeorm.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 locter.load(filePath); if (locter.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 (!locter.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 (!locter.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 locter.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, rapiq.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 === rapiq.FilterComparisonOperator.NOT_EQUAL) { statement.push('NOT'); } statement.push('NULL'); items.push({ statement: statement.join(' '), binding: {} }); continue; } switch(filter.operator){ case rapiq.FilterComparisonOperator.EQUAL: case rapiq.FilterComparisonOperator.NOT_EQUAL: { if (filter.operator === rapiq.FilterComparisonOperator.EQUAL) { statement.push('='); } else { statement.push('!='); } statement.push(`:${bindingKey}`); break; } case rapiq.FilterComparisonOperator.LIKE: case rapiq.FilterComparisonOperator.NOT_LIKE: { if (filter.operator === rapiq.FilterComparisonOperator.NOT_LIKE) { statement.push('NOT'); } statement.push('LIKE'); statement.push(`:${bindingKey}`); filter.value += '%'; break; } case rapiq.FilterComparisonOperator.IN: case rapiq.FilterComparisonOperator.NOT_IN: { if (filter.operator === rapiq.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 === rapiq.FilterComparisonOperator.NOT_IN) { statement.push('AND'); } else { statement.push('OR'); } statement.push(fullKey); statement.push('IS'); if (filter.operator === rapiq.FilterComparisonOperator.NOT_IN) { statement.push('NOT'); } statement.push('NULL'); statement.push(')'); } } break; } case rapiq.FilterComparisonOperator.LESS_THAN: case rapiq.FilterComparisonOperator.LESS_THAN_EQUAL: case rapiq.FilterComparisonOperator.GREATER_THAN: case rapiq.FilterComparisonOperator.GREATER_THAN_EQUAL: { if (filter.operator === rapiq.FilterComparisonOperator.LESS_THAN) { statement.push('<'); } else if (filter.operator === rapiq.FilterComparisonOperator.LESS_THAN_EQUAL) { statement.push('<='); } else if (filter.operator === rapiq.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 typeorm.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, rapiq.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, rapiq.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, rapiq.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, rapiq.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, rapiq.parseQuery(input, options)); return { ...output, ...options.defaultAlias ? { defaultAlias: options.defaultAlias } : {} }; } async function findDataSource(context = {}) { let tsconfig; if (!context.preserveFilePaths) { if (locter.isObject(context.tsconfig)) { tsconfig = context.tsconfig; } else { tsconfig = await readTSConfig(context.tsconfig); } } const files = [ 'data-source' ]; if (context.fileName) { context.fileName = locter.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 locter.locate(`${files[i]}.{js,cjs,mjs,ts,cts,mts}`, { path: [ process.cwd(), ...directory && !directoryIsPattern ? [ directory ] : [] ], ignore: [ '**/*.d.ts' ] }); if (info) { let moduleRecord = await locter.load(info); if (isPromise(moduleRecord)) { moduleRecord = await moduleRecord; } if (typeorm.InstanceChecker.isDataSource(moduleRecord)) { return moduleRecord; } if (!locter.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 (typeorm.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 = envix.toBool(input); if (typeof value === 'boolean') { return value; } if (input === 'all') { return 'all'; } return envix.toArray(input) ?? []; } function transformCache(input) { const value = envix.toBool(input); if (typeof value === 'boolean') { return value; } if (input === 'redis' || input === 'ioredis' || input === 'database' || input === 'ioredis/cluster') { let options; const envCacheOptions = envix.oneOf([ envix.read(EnvironmentVariableName.CACHE_OPTIONS), envix.read(EnvironmentVariableName.CACHE_OPTIONS_ALT) ]); if (envCacheOptions) { options = JSON.parse(envCacheOptions); } return { type: input, options, alwaysEnabled: envix.oneOf([ envix.readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED), envix.readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED_ALT) ]), duration: envix.oneOf([ envix.readInt(EnvironmentVariableName.CACHE_DURATION), envix.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: envix.read(EnvironmentVariableName.ENV, EnvironmentName.DEVELOPMENT), // Seeder seeds: envix.oneOf([ envix.readArray(EnvironmentVariableName.SEEDS), envix.readArray(EnvironmentVariableName.SEEDS_ALT) ]) ?? [], factories: envix.oneOf([ envix.readArray(EnvironmentVariableName.FACTORIES), envix.readArray(EnvironmentVariableName.FACTORIES_ALT) ]) ?? [], // Database url: envix.oneOf([ envix.read(EnvironmentVariableName.URL), envix.read(EnvironmentVariableName.URL_ALT) ]), host: envix.oneOf([ envix.read(EnvironmentVariableName.HOST), envix.read(EnvironmentVariableName.HOST_ALT) ]), port: envix.oneOf([ envix.readInt(EnvironmentVariableName.PORT), envix.readInt(EnvironmentVariableName.PORT_ALT) ]), username: envix.oneOf([ envix.read(EnvironmentVariableName.USERNAME), envix.read(EnvironmentVariableName.USERNAME_ALT) ]), password: envix.oneOf([ envix.read(EnvironmentVariableName.PASSWORD), envix.read(EnvironmentVariableName.PASSWORD_ALT) ]), database: envix.oneOf([ envix.read(EnvironmentVariableName.DATABASE), envix.read(EnvironmentVariableName.DATABASE_ALT) ]), sid: envix.oneOf([ envix.read(EnvironmentVariableName.SID), envix.read(EnvironmentVariableName.SID_ALT) ]), schema: envix.oneOf([ envix.read(EnvironmentVariableName.SCHEMA), envix.read(EnvironmentVariableName.SCHEMA_ALT) ]), extra: envix.oneOf([ envix.read(EnvironmentVariableName.DRIVER_EXTRA), envix.read(EnvironmentVariableName.DRIVER_EXTRA_ALT) ]), synchronize: envix.oneOf([ envix.readBool(EnvironmentVariableName.SYNCHRONIZE), envix.readBool(EnvironmentVariableName.SYNCHRONIZE_ALT) ]), schemaDrop: envix.oneOf([ envix.readBool(EnvironmentVariableName.SCHEMA_DROP), envix.readBool(EnvironmentVariableName.SCHEMA_DROP_ALT) ]), migrationsRun: envix.oneOf([ envix.readBool(EnvironmentVariableName.MIGRATIONS_RUN), envix.readBool(EnvironmentVariableName.MIGRATIONS_RUN_ALT) ]), entities: envix.oneOf([ envix.readArray(EnvironmentVariableName.ENTITIES), envix.readArray(EnvironmentVariableName.ENTITIES_ALT) ]) ?? [], migrations: envix.oneOf([ envix.readArray(EnvironmentVariableName.MIGRATIONS), envix.readArray(EnvironmentVariableName.MIGRATIONS_ALT) ]) ?? [], migrationsTableName: envix.oneOf([ envix.read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME), envix.read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME_ALT) ]), metadataTableName: envix.oneOf([ envix.read(EnvironmentVariableName.METADATA_TABLE_NAME), envix.read(EnvironmentVariableName.METADATA_TABLE_NAME_ALT) ]), subscribers: envix.oneOf([ envix.readArray(EnvironmentVariableName.SUBSCRIBERS), envix.readArray(EnvironmentVariableName.SUBSCRIBERS_ALT) ]) ?? [], logging: transformLogging(envix.oneOf([ envix.read(EnvironmentVariableName.LOGGING), envix.read(EnvironmentVariableName.LOGGING_ALT) ])), logger: envix.oneOf([ envix.read(EnvironmentVariableName.LOGGER), envix.read(EnvironmentVariableName.LOGGER_ALT) ]), entityPrefix: envix.oneOf([ envix.read(EnvironmentVariableName.ENTITY_PREFIX), envix.read(EnvironmentVariableName.ENTITY_PREFIX_ALT) ]), maxQueryExecutionTime: envix.oneOf([ envix.readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME), envix.readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME_ALT) ]), debug: envix.oneOf([ envix.read(EnvironmentVariableName.DEBUG), envix.read(EnvironmentVariableName.DEBUG_ALT) ]), cache: transformCache(envix.oneOf([ envix.read(EnvironmentVariableName.CACHE), envix.read(EnvironmentVariableName.CACHE_ALT) ])), uuidExtension: envix.oneOf([ envix.read(EnvironmentVariableName.UUID_EXTENSION), envix.read(EnvironmentVariableName.UUID_EXTENSION_ALT) ]) }; if (output.extra) { output.extra = JSON.parse(output.extra); // todo: ensure record ?? } let type; const envType = envix.oneOf([ envix.read(EnvironmentVariableName.TYPE), envix.read(EnvironmentVariableName.TYPE_ALT) ]); if (envType) { type = envType; } else { const envURL = envix.oneOf([ envix.read(EnvironmentVariableName.URL), envix.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 = smob.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 (locter.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 typeorm.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 typeorm.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 typeorm.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 typeorm.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.DriverUtils.buildDriverOptions(options.replication ? options.replication.master : options); break; case 'mongodb': driverOptions = DriverUtils.DriverUtils.buildMongoDBDriverOptions(options); break; default: driverOptions = DriverUtils.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 } : {}, ...options.extra ? { extra: options.extra } : {}, ...driverOptions.domain ? { domain: driverOptions.domain } : {} }; } const driversRequireDatabaseOption = [ 'sqlite', 'better-sqlite3' ]; function createDriver(connectionOptions) { const fakeConnection = { options: { type: connectionOptions.type, ...driversRequireDatabaseOption.indexOf(connectionOptions.type) !== -1 ? { database: connectionOptions.database } : {} } }; const driverFactory = new DriverFactory.DriverFactory(); return driverFactory.create(fakeConnection); } async function setDatabaseContextOptions(context) { if (!context.options) { const dataSource = await findDataSource(context.findOptions); if (dataSource) { context.options = dataSource.options; } if (!context.options) { context.options = await buildDataSourceOptions(); } } Object.assign(context.options, { subscribers: [], synchronize: false, migrationsRun: false, dropSchema: false }); return context; } async function buildDatabaseCreateContext(context) { context = context || {}; context = await setDatabaseContextOptions(context); if (typeof context.synchronize === 'undefined') { context.synchronize = true; } if (typeof context.ifNotExist === 'undefined') { context.ifNotExist = true; } return context; } async function buildDatabaseDropContext(context) { context = context || {}; context = await setDatabaseContextOptions(context); if (typeof context.ifExist === 'undefined') { context.ifExist = true; } return context; } class GenerateCommand extends MigrationGenerateCommand.MigrationGenerateCommand { static prettify(query) { return this.prettifyQuery(query); } } function queryParams(parameters) { if (!parameters || !parameters.length) { return ''; } return `, ${JSON.stringify(parameters)}`; } function buildTemplate(name, timestamp, upStatements, downStatements) { const migrationName = `${pascalCase.pascalCase(name)}${timestamp}`; const up = upStatements.map((statement)=>` ${statement}`); const down = downStatements.map((statement)=>` ${statement}`); return `import { MigrationInterface, QueryRunner } from 'typeorm'; export class ${migrationName} implements MigrationInterface { name = '${migrationName}'; public async up(queryRunner: QueryRunner): Promise { ${up.join(` `)} } public async down(queryRunner: QueryRunner): Promise { ${down.join(` `)} } } `; } async function generateMigration(context) { context.name = context.name || 'Default'; const timestamp = context.timestamp || new Date().getTime(); const fileName = `${timestamp}-${context.name}.ts`; const { dataSource } = context; const up = []; const down = []; if (!dataSource.isInitialized) { await dataSource.initialize(); } const sqlInMemory = await dataSource.driver.createSchemaBuilder().log(); if (context.prettify) { sqlInMemory.upQueries.forEach((upQuery)=>{ upQuery.query = GenerateCommand.prettify(upQuery.query); }); sqlInMemory.downQueries.forEach((downQuery)=>{ downQuery.query = GenerateCommand.prettify(downQuery.query); }); } sqlInMemory.upQueries.forEach((upQuery)=>{ up.push(`await queryRunner.query(\`${upQuery.query.replace(/`/g, '\\`')}\`${queryParams(upQuery.parameters)});`); }); sqlInMemory.downQueries.forEach((downQuery)=>{ down.push(`await queryRunner.query(\`${downQuery.query.replace(/`/g, '\\`')}\`${queryParams(downQuery.parameters)});`); }); await dataSource.destroy(); if (up.length === 0 && down.length === 0) { return { up, down }; } const content = buildTemplate(context.name, timestamp, up, down.reverse()); if (!context.preview) { let directoryPath; if (context.directoryPath) { if (!path.isAbsolute(context.directoryPath)) { directoryPath = path.join(process$1.cwd(), context.directoryPath); } else { directoryPath = context.directoryPath; } } else { directoryPath = path.join(process$1.cwd(), 'migrations'); } try { await fs.promises.access(directoryPath, fs.constants.R_OK | fs.constants.W_OK); } catch (e) { await fs.promises.mkdir(directoryPath, { recursive: true }); } const filePath = path.join(directoryPath, fileName); await fs.promises.writeFile(filePath, content, { encoding: 'utf-8' }); } return { up, down, content }; } /* istanbul ignore next */ function existsQuery(builder, inverse = false) { return `${inverse ? 'not ' : ''}exists (${builder.getQuery()})`; } async function synchronizeDatabaseSchema(input) { let dataSource; let options; if (typeorm.InstanceChecker.isDataSource(input)) { dataSource = input; options = dataSource.options; } else { options = input; dataSource = new typeorm.DataSource(options); } if (!dataSource.isInitialized) { await dataSource.initialize(); } let migrationsCount = 0; if (options.migrations) { migrationsCount = Array.isArray(options.migrations) ? options.migrations.length : Object.keys(options.migrations).length; } let migrations = []; if (migrationsCount > 0) { migrations = await dataSource.runMigrations({ transaction: options.migrationsTransactionMode }); } else { await dataSource.synchronize(false); } if (!typeorm.InstanceChecker.isDataSource(input)) { await dataSource.destroy(); } return migrations; } async function createSimplePostgresConnection(driver, options, operationContext) { /** * pg library */ const { Client } = driver.postgres; const data = { host: options.host, port: options.port, user: options.user, password: options.password, ssl: options.ssl, ...options.extra ? options.extra : {} }; if (typeof operationContext.initialDatabase === 'string') { data.database = operationContext.initialDatabase; } const client = new Client(data); await client.connect(); return client; } async function executeSimplePostgresQuery(connection, query, endConnection = true) { return new Promise((resolve, reject)=>{ connection.query(query, (queryErr, queryResult)=>{ if (endConnection) { connection.end(); } if (queryErr) { reject(queryErr); } resolve(queryResult); }); }); } async function createPostgresDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimplePostgresConnection(driver, options, context); if (context.ifNotExist) { const existQuery = `SELECT * FROM pg_database WHERE lower(datname) = lower('${options.database}');`; const existResult = await executeSimplePostgresQuery(connection, existQuery, false); if (locter.isObject(existResult) && hasOwnProperty(existResult, 'rows') && Array.isArray(existResult.rows) && existResult.rows.length > 0) { await connection.end(); return Promise.resolve(); } } /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/postgres/PostgresQueryRunner.ts#L326 */ let query = `CREATE DATABASE "${options.database}"`; if (typeof options.characterSet === 'string') { query += ` WITH ENCODING '${options.characterSet}'`; } const result = await executeSimplePostgresQuery(connection, query); if (context.synchronize) { await synchronizeDatabaseSchema(context.options); } return result; } async function dropPostgresDatabase(context) { context = await buildDatabaseDropContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimplePostgresConnection(driver, options, context); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/postgres/PostgresQueryRunner.ts#L343 */ const query = context.ifExist ? `DROP DATABASE IF EXISTS "${options.database}"` : `DROP DATABASE "${options.database}"`; return executeSimplePostgresQuery(connection, query); } async function executeSimpleCockroachDBQuery(connection, query, endConnection = true) { return new Promise((resolve, reject)=>{ connection.query(query, (queryErr, queryResult)=>{ if (endConnection) { connection.end(); } if (queryErr) { reject(queryErr); } resolve(queryResult); }); }); } async function createCockroachDBDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimplePostgresConnection(driver, options, context); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/cockroachdb/CockroachQueryRunner.ts#L347 */ const query = `CREATE DATABASE ${context.ifNotExist ? 'IF NOT EXISTS ' : ''} "${options.database}"`; const result = await executeSimpleCockroachDBQuery(connection, query); if (context.synchronize) { await synchronizeDatabaseSchema(context.options); } return result; } async function dropCockroachDBDatabase(context) { context = await buildDatabaseDropContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimplePostgresConnection(driver, options, context); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/cockroachdb/CockroachQueryRunner.ts#L356 */ const query = `DROP DATABASE ${context.ifExist ? 'IF EXISTS ' : ''} "${options.database}"`; return executeSimpleCockroachDBQuery(connection, query); } async function createSimpleMongoDBConnection(driver, options) { /** * mongodb library */ const { MongoClient } = driver.mongodb; let url = 'mongodb://'; if (options.user && options.password) { url += `${options.user}:${options.password}@`; } url += `${options.host || '127.0.0.1'}:${options.port || 27017}/${options.database}`; if (options.ssl) { url += '?tls=true'; } const client = new MongoClient(url); await client.connect(); return client; } async function createMongoDBDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); // connection setup, will create the database on the fly. const client = await createSimpleMongoDBConnection(driver, options); await client.close(); if (context.synchronize) { await synchronizeDatabaseSchema(context.options); } } async function dropMongoDBDatabase(context) { context = await buildDatabaseDropContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const client = await createSimpleMongoDBConnection(driver, options); const result = await client.dropDatabase(); await client.close(); return result; } async function createSimpleMsSQLConnection(driver, options) { const option = { user: options.user, password: options.password, server: options.host, port: options.port || 1433, ...options.extra ? options.extra : {}, ...options.domain ? { domain: options.domain } : {} }; await driver.mssql.connect(option); return driver.mssql; } async function createMsSQLDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimpleMsSQLConnection(driver, options); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/sqlserver/SqlServerQueryRunner.ts#L416 */ let query = context.ifNotExist ? `IF DB_ID('${options.database}') IS NULL CREATE DATABASE "${options.database}"` : `CREATE DATABASE "${options.database}"`; if (typeof options.characterSet === 'string') { query += ` CHARACTER SET ${options.characterSet}`; } const result = await connection.query(query); if (context.synchronize) { await synchronizeDatabaseSchema(context.options); } return result; } async function dropMsSQLDatabase(context) { context = await buildDatabaseDropContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimpleMsSQLConnection(driver, options); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/sqlserver/SqlServerQueryRunner.ts#L425 */ const query = context.ifExist ? `IF DB_ID('${options.database}') IS NOT NULL DROP DATABASE "${options.database}"` : `DROP DATABASE "${options.database}"`; return connection.query(query); } async function createSimpleMySQLConnection(driver, options) { /** * mysql|mysql2 library */ const { createConnection } = driver.mysql; const option = { host: options.host, user: options.user, password: options.password, port: options.port, ssl: options.ssl, ...options.extra ? options.extra : {} }; return createConnection(option); } async function executeSimpleMysqlQuery(connection, query, endConnection = true) { return new Promise((resolve, reject)=>{ connection.query(query, (queryErr, queryResult)=>{ if (endConnection) connection.end(); if (queryErr) { reject(queryErr); } resolve(queryResult); }); }); } async function createMySQLDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimpleMySQLConnection(driver, options); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlQueryRunner.ts#L297 */ let query = context.ifNotExist ? `CREATE DATABASE IF NOT EXISTS \`${options.database}\`` : `CREATE DATABASE \`${options.database}\``; if (typeof options.charset === 'string') { const { charset } = options; let { characterSet } = options; if (typeof characterSet === 'undefined') { if (charset.toLowerCase().startsWith('utf8mb4')) { characterSet = 'utf8mb4'; } else if (charset.toLowerCase().startsWith('utf8')) { characterSet = 'utf8'; } } if (typeof characterSet === 'string') { query += ` CHARACTER SET ${characterSet} COLLATE ${charset}`; } } const result = await executeSimpleMysqlQuery(connection, query); if (context.synchronize) { await synchronizeDatabaseSchema(context.options); } return result; } async function dropMySQLDatabase(context) { context = await buildDatabaseDropContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = await createSimpleMySQLConnection(driver, options); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlQueryRunner.ts#L306 */ const query = context.ifExist ? `DROP DATABASE IF EXISTS \`${options.database}\`` : `DROP DATABASE \`${options.database}\``; await executeSimpleMysqlQuery(connection, 'SET FOREIGN_KEY_CHECKS=0;', false); const result = await executeSimpleMysqlQuery(connection, query, false); await executeSimpleMysqlQuery(connection, 'SET FOREIGN_KEY_CHECKS=1;'); return result; } function createSimpleOracleConnection(driver, options) { const { getConnection } = driver.oracle; if (!options.connectString) { let address = '(PROTOCOL=TCP)'; if (options.host) { address += `(HOST=${options.host})`; } if (options.port) { address += `(PORT=${options.port})`; } let connectData = '(SERVER=DEDICATED)'; if (options.sid) { connectData += `(SID=${options.sid})`; } if (options.serviceName) { connectData += `(SERVICE_NAME=${options.serviceName})`; } options.connectString = `(DESCRIPTION=(ADDRESS=${address})(CONNECT_DATA=${connectData}))`; } return getConnection({ user: options.user, password: options.password, connectString: options.connectString || options.url, ...options.extra ? options.extra : {} }); } async function createOracleDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); const driver = createDriver(context.options); const connection = createSimpleOracleConnection(driver, options); /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/oracle/OracleQueryRunner.ts#L295 */ const query = `CREATE DATABASE IF NOT EXISTS ${options.database}`; const result = await connection.execute(query); if (context.synchronize) { await synchronizeDatabaseSchema(context.options); } return result; } async function dropOracleDatabase(_context) { /** * @link https://github.com/typeorm/typeorm/blob/master/src/driver/oracle/OracleQueryRunner.ts#L295 */ return Promise.resolve(); } async function createSQLiteDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); if (!options.database) { throw OptionsError.databaseNotDefined(); } const filePath = path.isAbsolute(options.database) ? options.database : path.join(process.cwd(), options.database); const directoryPath = path.dirname(filePath); await fs.promises.access(directoryPath, fs.constants.W_OK); if (context.synchronize) { await synchronizeDatabaseSchema(context.options); } } async function dropSQLiteDatabase(context) { context = await buildDatabaseDropContext(context); if (!context.options) { throw OptionsError.undeterminable(); } const options = buildDriverOptions(context.options); if (!options.database) { throw OptionsError.databaseNotDefined(); } const filePath = path.isAbsolute(options.database) ? options.database : path.join(process.cwd(), options.database); try { await fs.promises.access(filePath, fs.constants.F_OK | fs.constants.W_OK); if (context.ifExist) { await fs.promises.unlink(filePath); } } catch (e) { // ... } } /** * Create database for specified driver in ConnectionOptions. * * @throws DriverError * @throws OptionsError * * @param context */ async function createDatabase(context) { context = await buildDatabaseCreateContext(context); if (!context.options) { throw OptionsError.undeterminable(); } if (!context.options.type) { throw DriverError.undeterminable(); } switch(context.options.type){ case 'mongodb': return createMongoDBDatabase(context); case 'mysql': case 'mariadb': return createMySQLDatabase(context); case 'postgres': return createPostgresDatabase(context); case 'cockroachdb': return createCockroachDBDatabase(context); case 'sqlite': case 'better-sqlite3': return createSQLiteDatabase(context); case 'oracle': return createOracleDatabase(context); case 'mssql': return createMsSQLDatabase(context); } throw DriverError.notSupported(context.options.type); } /** * Drop database for specified driver in ConnectionOptions. * * @throws DriverError * @throws OptionsError * * @param context */ async function dropDatabase(context) { context = await buildDatabaseDropContext(context); if (!context.options) { throw OptionsError.undeterminable(); } if (!context.options.type) { throw DriverError.undeterminable(); } switch(context.options.type){ case 'mongodb': return dropMongoDBDatabase(context); case 'mysql': case 'mariadb': return dropMySQLDatabase(context); case 'postgres': return dropPostgresDatabase(context); case 'cockroachdb': return dropCockroachDBDatabase(context); case 'sqlite': case 'better-sqlite3': return dropSQLiteDatabase(context); case 'oracle': return dropOracleDatabase(); case 'mssql': return dropMsSQLDatabase(context); } throw DriverError.notSupported(context.options.type); } class DatabaseCreateCommand { builder(args) { return args.option('preserveFilePaths', { default: false, type: 'boolean', describe: 'This option indicates if file paths should be preserved.' }).option('root', { alias: 'r', default: process.cwd(), describe: 'Root directory of the project.' }).option('tsconfig', { alias: 'tc', default: 'tsconfig.json', describe: 'Name (or relative path incl. name) of the tsconfig file.' }).option('dataSource', { alias: 'd', default: 'data-source', describe: 'Name (or relative path incl. name) of the data-source file.' }).option('synchronize', { alias: 's', default: 'yes', describe: 'Create database schema for all entities.', choices: [ 'yes', 'no' ] }).option('initialDatabase', { describe: 'Specify the initial database to connect to.' }); } async handler(raw) { const args = raw; let tsconfig; let sourcePath = resolveFilePath(args.dataSource, args.root); if (!args.preserveFilePaths) { tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig)); sourcePath = await adjustFilePath(sourcePath, tsconfig); } const source = parseFilePath(sourcePath); consola.consola.info(`DataSource Directory: ${source.directory}`); consola.consola.info(`DataSource Name: ${source.name}`); const dataSourceOptions = await buildDataSourceOptions({ directory: source.directory, dataSourceName: source.name, tsconfig, preserveFilePaths: args.preserveFilePaths }); const context = { ifNotExist: true, options: dataSourceOptions }; if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') { context.initialDatabase = args.initialDatabase; } context.synchronize = args.synchronize === 'yes'; try { await createDatabase(context); consola.consola.success('Created database.'); process.exit(0); } catch (e) { consola.consola.warn('Failed to create database.'); consola.consola.error(e); process.exit(1); } } constructor(){ this.command = 'db:create'; this.describe = 'Create database.'; } } class DatabaseDropCommand { builder(args) { return args.option('preserveFilePaths', { default: false, type: 'boolean', describe: 'This option indicates if file paths should be preserved.' }).option('root', { alias: 'r', default: process.cwd(), describe: 'Root directory of the project.' }).option('tsconfig', { alias: 'tc', default: 'tsconfig.json', describe: 'Name (or relative path incl. name) of the tsconfig file.' }).option('dataSource', { alias: 'd', default: 'data-source', describe: 'Name (or relative path incl. name) of the data-source file.' }).option('initialDatabase', { describe: 'Specify the initial database to connect to.' }); } async handler(raw) { const args = raw; let tsconfig; let sourcePath = resolveFilePath(args.dataSource, args.root); if (!args.preserveFilePaths) { tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig)); sourcePath = await adjustFilePath(sourcePath, tsconfig); } const source = parseFilePath(sourcePath); consola.consola.info(`DataSource Directory: ${source.directory}`); consola.consola.info(`DataSource Name: ${source.name}`); const dataSourceOptions = await buildDataSourceOptions({ directory: source.directory, dataSourceName: source.name, tsconfig, preserveFilePaths: args.preserveFilePaths }); const context = { ifExist: true, options: dataSourceOptions }; if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') { context.initialDatabase = args.initialDatabase; } try { await dropDatabase(context); consola.consola.success('Dropped database.'); process.exit(0); } catch (e) { consola.consola.warn('Failed to drop database.'); consola.consola.error(e); process.exit(1); } } constructor(){ this.command = 'db:drop'; this.describe = 'Drop database.'; } } class SeederEntity { get trackExecution() { if (typeof this.instance === 'undefined') { return undefined; } return this.instance.track; } constructor(ctx){ this.id = ctx.id; this.timestamp = ctx.timestamp; this.name = ctx.name; if (ctx.constructor) { this.instance = new ctx.constructor(); } this.fileName = ctx.fileName; this.filePath = ctx.filePath; } } class SeederFactory { // -------------------------------------------------------------- setMeta(value) { this.meta = value; return this; } setLocale(value) { this.faker = undefined; this.locale = Array.isArray(value) ? value : [ value ]; } // -------------------------------------------------------------- async make(params, save) { const faker = await this.useFaker(); const factoryFn = this.context.factoryFn(faker, this.meta); let entity; if (isPromise(factoryFn)) { entity = await this.resolve(await factoryFn, save); } else { entity = await this.resolve(factoryFn, save); } if (params) { const keys = Object.keys(params); for(let i = 0; i < keys.length; i++){ entity[keys[i]] = params[keys[i]]; } } return entity; } // -------------------------------------------------------------- async save(params, options) { const dataSource = await useDataSource(); const entity = await this.make(params, true); const entityManager = dataSource.getRepository(this.context.entity); return entityManager.save(entity, options); } async saveMany(amount, params, options) { const promises = []; for(let i = 0; i < amount; i++){ const item = this.save(params, options); promises.push(item); } return Promise.all(promises); } // -------------------------------------------------------------- async resolve(entity, save) { const keys = Object.keys(entity); for(let i = 0; i < keys.length; i++){ const key = keys[i]; const value = entity[key]; if (!hasOwnProperty(entity, key)) { continue; } if (typeof value === 'object' && // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore value instanceof SeederFactory) { if (save) { entity[key] = await value.save(); } else { entity[key] = await value.make(); } } if (value && hasOwnProperty(value, 'then') && typeof value.then === 'function') { entity[key] = await value; } } return entity; } async useFaker() { if (typeof this.faker !== 'undefined') { return this.faker; } const options = { locale: [] }; const fakerExports = await locter.load('@faker-js/faker'); let names; if (this.locale) { names = Array.isArray(this.locale) ? this.locale : [ this.locale ]; } else { names = [ 'en' ]; } for(let i = 0; i < names.length; i++){ if (hasOwnProperty(fakerExports, 'default') && locter.isObject(fakerExports.default) && hasOwnProperty(fakerExports.default, names[i])) { options.locale.push(fakerExports.default[names[i]]); continue; } if (hasOwnProperty(fakerExports, names[i])) { options.locale.push(fakerExports[names[i]]); } } this.faker = new faker.Faker(options); return this.faker; } // -------------------------------------------------------------- constructor(context){ this.context = context; } } class SeederFactoryManager { set(entity, factoryFn) { const name = getEntityName(entity); this.items[name] = { factoryFn, entity }; return this.items[name]; } get(entity) { const name = getEntityName(entity); if (!hasOwnProperty(this.items, name)) { throw new Error(`No seeder factory is registered for the entity: ${name}`); } return new SeederFactory({ factoryFn: this.items[name].factoryFn, entity, name }); } constructor(){ this.items = {}; } } async function resolveFilePatterns(filesPattern, root) { return locter.locateMany(filesPattern, { ...root ? { path: root } : {}, ignore: [ '**/*.d.ts' ] }).then(buildFilePathname); } function resolveFilePaths(filePaths, root) { return filePaths.map((filePath)=>path.isAbsolute(filePath) ? filePath : path.resolve(root || process.cwd(), filePath)); } /** * Exported only for testing purposes */ function buildFilePathname(files) { return(// sorting by name so that we can define the order of execution using file names files.sort((a, b)=>a.name > b.name ? 1 : -1).map((el)=>path.join(el.path, el.name + el.extension))); } async function prepareSeederSeeds(input, root) { const items = []; let seedFiles = []; const seedConstructors = []; for(let i = 0; i < input.length; i++){ const value = input[i]; if (typeof value === 'string') { seedFiles.push(value); } else { seedConstructors.push(value); } } if (seedFiles.length > 0) { seedFiles = await resolveFilePatterns(seedFiles, root); seedFiles = resolveFilePaths(seedFiles, root); for(let i = 0; i < seedFiles.length; i++){ const moduleExports = await locter.load(seedFiles[i]); let clazzConstructor; const exportKeys = Object.keys(moduleExports); for(let j = 0; j < exportKeys.length; j++){ const moduleExport = moduleExports[exportKeys[j]]; if (typeof moduleExport === 'function' && moduleExport.prototype) { clazzConstructor = moduleExport; } } if (clazzConstructor) { const fileName = path.basename(seedFiles[i]); const filePath = seedFiles[i]; const match = fileName.match(/^([0-9]{13,})-(.*)$/); let timestamp; if (match) { timestamp = parseInt(match[1], 10); } items.push({ constructor: clazzConstructor, fileName, filePath, ...timestamp ? { timestamp } : {} }); } } } if (seedConstructors.length > 0) { for(let i = 0; i < seedConstructors.length; i++){ items.push({ constructor: seedConstructors[i] }); } } return items; } function buildSeederFileTemplate(name, timestamp) { const className = `${pascalCase.pascalCase(name)}${timestamp}`; return `import { DataSource } from 'typeorm'; import { Seeder, SeederFactoryManager } from 'typeorm-extension'; export class ${className} implements Seeder { track = false; public async run( dataSource: DataSource, factoryManager: SeederFactoryManager ): Promise { } } `; } let instance; function useSeederFactoryManager() { if (typeof instance !== 'undefined') { return instance; } instance = new SeederFactoryManager(); return instance; } function setSeederFactory(entity, factoryFn) { const manager = useSeederFactoryManager(); return manager.set(entity, factoryFn); } function useSeederFactory(entity) { const manager = useSeederFactoryManager(); return manager.get(entity); } async function prepareSeederFactories(items, root) { let factoryFiles = []; const factoryConfigs = []; for(let i = 0; i < items.length; i++){ const value = items[i]; if (typeof value === 'string') { factoryFiles.push(value); } else { factoryConfigs.push(value); } } if (factoryFiles.length > 0) { factoryFiles = await resolveFilePatterns(factoryFiles, root); factoryFiles = resolveFilePaths(factoryFiles, root); for(let i = 0; i < factoryFiles.length; i++){ await locter.load(factoryFiles[i]); } } if (factoryConfigs.length > 0) { const factoryManager = useSeederFactoryManager(); for(let i = 0; i < factoryConfigs.length; i++){ factoryManager.set(factoryConfigs[i].entity, factoryConfigs[i].factoryFn); } } } class SeederExecutor { async execute(input = {}) { const options = await this.buildOptions(input); if (!options.seeds || options.seeds.length === 0) { return []; } if (options.factories) { await prepareSeederFactories(options.factories, this.options.root); } const seederElements = await prepareSeederSeeds(options.seeds, this.options.root); const all = await this.buildEntities(seederElements); let tracking = !!options.seedTracking; if (!tracking) { tracking = all.some((seed)=>!!seed.trackExecution); } let queryRunner; let existing = []; if (tracking) { queryRunner = this.dataSource.createQueryRunner(); await this.createTableIfNotExist(queryRunner); existing = await this.loadExisting(queryRunner); } const isMatch = (seed)=>{ if (!options.seedName) { return true; } if (seed.name === options.seedName || seed.fileName === options.seedName) { return true; } if (!seed.filePath) { return false; } if (seed.filePath === options.seedName) { return true; } return resolveFilePath(options.seedName, this.options.root) === seed.filePath; }; const pending = all.filter((seed)=>{ if (!isMatch(seed)) { return false; } const index = existing.findIndex((el)=>el.name === seed.name); if (index === -1) { return true; } let seedTracking; if (typeof seed.trackExecution !== 'undefined') { seedTracking = seed.trackExecution; } else { seedTracking = options.seedTracking; } return !seedTracking; }); if (pending.length === 0) { if (queryRunner) { await queryRunner.release(); } return []; } this.dataSource.logger.logSchemaBuild(`${existing.length} seeds are already present in the database.`); this.dataSource.logger.logSchemaBuild(`${all.length} seeds were found in the source code.`); const factoryManager = useSeederFactoryManager(); const executed = []; try { for(let i = 0; i < pending.length; i++){ const seeder = pending[i].instance; if (!seeder) { continue; } pending[i].result = await seeder.run(this.dataSource, factoryManager); let seedTracking; if (typeof pending[i].trackExecution !== 'undefined') { seedTracking = pending[i].trackExecution; } else { seedTracking = options.seedTracking; } if (queryRunner && seedTracking) { await this.track(queryRunner, pending[i]); } this.dataSource.logger.logSchemaBuild(`Seed ${pending[i].name} has been executed successfully.`); executed.push(pending[i]); } } finally{ if (queryRunner) { await queryRunner.release(); } } return executed; } async loadExisting(queryRunner) { if (this.dataSource.driver.options.type === 'mongodb') { const mongoRunner = queryRunner; return mongoRunner.cursor(this.tableName, {}).sort({ _id: -1 }).toArray(); } const raw = await this.dataSource.manager.createQueryBuilder(queryRunner).select().orderBy(this.dataSource.driver.escape('id'), 'DESC').from(this.table, this.tableName).getRawMany(); return raw.map((migrationRaw)=>new SeederEntity({ id: parseInt(migrationRaw.id, 10), timestamp: parseInt(migrationRaw.timestamp, 10), name: migrationRaw.name, constructor: undefined })); } /** * Gets all migrations that setup for this connection. */ async buildEntities(seeds) { if (!seeds) { return []; } let timestampCounter = 0; const entities = seeds.map((element)=>{ const { constructor: seed, fileName, filePath } = element; let { timestamp } = element; const className = seed.name || seed.constructor.name; if (!timestamp) { timestamp = this.classNameToTimestamp(className); } const entity = new SeederEntity({ fileName, filePath, timestamp: timestamp || timestampCounter, name: className, constructor: seed }); timestampCounter++; return entity; }); this.checkForDuplicates(entities); // sort them by file name than by timestamp return entities.sort((a, b)=>{ if (typeof a.fileName !== 'undefined' && typeof b.fileName !== 'undefined') { return a.fileName > b.fileName ? 1 : -1; } return a.timestamp - b.timestamp; }); } checkForDuplicates(entities) { const names = entities.map((migration)=>migration.name); const duplicates = Array.from(new Set(names.filter((migrationName, index)=>names.indexOf(migrationName) < index))); if (duplicates.length > 0) { throw Error(`Duplicate seeds: ${duplicates.join(', ')}`); } } async createTableIfNotExist(queryRunner) { // If driver is mongo no need to create if (this.dataSource.driver.options.type === 'mongodb') { return; } const tableExist = await queryRunner.hasTable(this.table); if (!tableExist) { await queryRunner.createTable(new typeorm.Table({ database: this.database, schema: this.schema, name: this.table, columns: [ { name: 'id', type: this.dataSource.driver.normalizeType({ type: this.dataSource.driver.mappedDataTypes.migrationId }), isGenerated: true, generationStrategy: 'increment', isPrimary: true, isNullable: false }, { name: 'timestamp', type: this.dataSource.driver.normalizeType({ type: this.dataSource.driver.mappedDataTypes.migrationTimestamp }), isPrimary: false, isNullable: false }, { name: 'name', type: this.dataSource.driver.normalizeType({ type: this.dataSource.driver.mappedDataTypes.migrationName }), isNullable: false } ] })); } } async track(queryRunner, seederEntity) { const values = {}; if (this.dataSource.driver.options.type === 'mssql') { values.timestamp = new typeorm.MssqlParameter(seederEntity.timestamp, this.dataSource.driver.normalizeType({ type: this.dataSource.driver.mappedDataTypes.migrationTimestamp })); values.name = new typeorm.MssqlParameter(seederEntity.name, this.dataSource.driver.normalizeType({ type: this.dataSource.driver.mappedDataTypes.migrationName })); } else { values.timestamp = seederEntity.timestamp; values.name = seederEntity.name; } if (this.dataSource.driver.options.type === 'mongodb') { const mongoRunner = queryRunner; await mongoRunner.databaseConnection.db(this.dataSource.driver.database).collection(this.tableName).insertOne(values); } else { const qb = queryRunner.manager.createQueryBuilder(); await qb.insert().into(this.table).values(values).execute(); } } get dataSourceOptions() { return this.dataSource.options; } get database() { return this.dataSource.driver.database; } get schema() { return this.dataSource.driver.schema; } get table() { return this.dataSource.driver.buildTableName(this.tableName, this.schema, this.database); } async buildOptions(input = {}) { const options = { ...input, seeds: input.seeds || [], factories: input.factories || [], seedTracking: input.seedTracking ?? false }; if (!options.seeds || options.seeds.length === 0) { options.seeds = this.dataSourceOptions.seeds; } if (!options.seeds || options.seeds.length === 0) { options.seeds = useEnv('seeds'); } if (!options.seeds || options.seeds.length === 0) { options.seeds = [ 'src/database/seeds/**/*{.ts,.js}' ]; } if (!options.factories || options.factories.length === 0) { options.factories = this.dataSourceOptions.factories; } if (!options.factories || options.factories.length === 0) { options.factories = useEnv('factories'); } if (!options.factories || options.factories.length === 0) { options.factories = [ 'src/database/factories/**/*{.ts,.js}' ]; } if (typeof options.seedTracking === 'undefined') { options.seedTracking = this.dataSourceOptions.seedTracking; } if (!this.options.preserveFilePaths) { let tsConfig; if (locter.isObject(this.options.tsconfig)) { tsConfig = this.options.tsconfig; } else { tsConfig = await readTSConfig(resolveFilePath(this.options.tsconfig || 'tsconfig.json', this.options.root)); } await adjustFilePaths(options, [ 'seeds', 'seedName', 'factories' ], tsConfig); } return options; } classNameToTimestamp(className) { const match = className.match(/^(.*)([0-9]{13,})$/); if (match) { return parseInt(match[2], 10); } return undefined; } constructor(dataSource, options){ this.dataSource = dataSource; this.options = options || {}; setDataSource(dataSource); this.tableName = this.dataSourceOptions.seedTableName || 'seeds'; } } async function runSeeder(dataSource, seeder, options = {}) { if (typeof seeder === 'string') { options.seedName = seeder; } else { options.seeds = [ seeder ]; } const executor = new SeederExecutor(dataSource); const output = await executor.execute(options); return output.pop(); } async function runSeeders(dataSource, options) { const executor = new SeederExecutor(dataSource); return executor.execute(options); } class SeedCreateCommand { builder(args) { return args.option('root', { alias: 'r', default: process.cwd(), describe: 'Root directory of the project.' }).option('timestamp', { alias: 't', type: 'number', describe: 'Custom timestamp for the seeder name.' }).option('javascript', { alias: 'j', type: 'boolean', default: false, describe: 'Generate a seeder file for JavaScript instead of TypeScript.' }).option('name', { alias: 'n', describe: 'Name (or relative path incl. name) of the seeder.', demandOption: true }); } async handler(raw) { const args = raw; let timestamp; if (Number.isNaN(args.timestamp) || !args.timestamp) { timestamp = Date.now(); } else { timestamp = args.timestamp; } const sourcePath = parseFilePath(args.name, args.root); const dirNameIsDirectory = await isDirectory(sourcePath.directory); if (!dirNameIsDirectory) { consola.consola.warn(`The output directory ${sourcePath.directory} does not exist.`); process.exit(1); } const extension = args.javascript ? '.js' : '.ts'; const nameExtension = locter.getFileNameExtension(sourcePath.name); const nameWithoutExtension = locter.removeFileNameExtension(sourcePath.name); let fileName; if (nameExtension) { fileName = `${timestamp}-${sourcePath.name}`; } else { fileName = `${timestamp}-${sourcePath.name}${extension}`; } const filePath = sourcePath.directory + path.sep + fileName; const template = buildSeederFileTemplate(nameWithoutExtension, timestamp); consola.consola.info(`Seed Directory: ${sourcePath.directory}`); consola.consola.info(`Seed FileName: ${fileName}`); consola.consola.info(`Seed Name: ${pascalCase.pascalCase(nameWithoutExtension)}`); try { await fs.promises.writeFile(filePath, template, { encoding: 'utf-8' }); } catch (e) { consola.consola.warn(`The seed could not be written to the path ${filePath}.`); process.exit(1); } process.exit(0); } constructor(){ this.command = 'seed:create'; this.describe = 'Create a seeder file.'; } } class SeedRunCommand { builder(args) { return args.option('preserveFilePaths', { default: false, type: 'boolean', describe: 'This option indicates if file paths should be preserved.' }).option('root', { alias: 'r', default: process.cwd(), describe: 'Root directory of the project.' }).option('tsconfig', { alias: 'tc', default: 'tsconfig.json', describe: 'Name (or relative path incl. name) of the tsconfig file.' }).option('dataSource', { alias: 'd', default: 'data-source', describe: 'Name (or relative path incl. name) of the data-source file.' }).option('name', { alias: 'n', describe: 'Name (or relative path incl. name) of the seeder.' }); } async handler(raw) { const args = raw; let tsconfig; let sourcePath = resolveFilePath(args.dataSource, args.root); if (!args.preserveFilePaths) { tsconfig = await readTSConfig(args.root); sourcePath = await adjustFilePath(sourcePath, tsconfig); args.name = await adjustFilePath(args.name, tsconfig); } const source = parseFilePath(sourcePath); consola.consola.info(`DataSource Directory: ${source.directory}`); consola.consola.info(`DataSource Name: ${source.name}`); const dataSourceOptions = await buildDataSourceOptions({ dataSourceName: source.name, directory: source.directory, tsconfig, preserveFilePaths: args.preserveFilePaths }); setDataSourceOptions(dataSourceOptions); if (args.name) { consola.consola.info(`Seed Name: ${args.name}`); } const dataSource = await useDataSource(); const executor = new SeederExecutor(dataSource, { root: args.root, tsconfig, preserveFilePaths: args.preserveFilePaths }); await executor.execute({ seedName: args.name }); process.exit(0); } constructor(){ this.command = 'seed:run'; this.describe = 'Populate the database with an initial data set or generated data by a factory.'; } } class EntityRelationLookupError extends TypeormExtensionError { static notReferenced(relation, columns) { return new EntityRelationLookupError({ message: `${relation} entity is not referenced by ${columns.join(', ')}`, relation, columns }); } static notFound(relation, columns) { return new EntityRelationLookupError({ message: `Can't find ${relation} entity by ${columns.join(', ')}`, relation, columns }); } constructor(options){ super(options.message); this.relation = options.relation; this.columns = options.columns; } } /** * Receive metadata for a given repository or entity-target. * * @experimental * @param input * @param dataSource */ async function getEntityMetadata(input, dataSource) { if (input instanceof typeorm.Repository) { return input.metadata; } dataSource = dataSource || await useDataSource(); const index = dataSource.entityMetadatas.findIndex((entityMetadata)=>entityMetadata.target === input); if (index === -1) { throw new Error(`The entity ${input} is not registered.`); } return dataSource.entityMetadatas[index]; } /** * Get (relation-) property names of a given entity. * * @experimental * @param input * @param dataSource */ async function getEntityPropertyNames(input, dataSource) { let entityMetadata; if (input instanceof typeorm.Repository) { entityMetadata = input.metadata; } else { entityMetadata = await getEntityMetadata(input, dataSource); } const items = []; for(let i = 0; i < entityMetadata.columns.length; i++){ items.push(entityMetadata.columns[i].propertyName); } for(let i = 0; i < entityMetadata.relations.length; i++){ items.push(entityMetadata.relations[i].propertyName); } return items; } /** * Validate join columns of a given entity. * It will look up and append the referenced entities to the input entity. * * @experimental * @param entity * @param options */ async function validateEntityJoinColumns(entity, options) { const dataSource = options.dataSource || await useDataSource(); const entityMetadata = await getEntityMetadata(options.entityTarget, dataSource); const relations = {}; for(let i = 0; i < entityMetadata.relations.length; i++){ const relation = entityMetadata.relations[i]; let skipRelation = false; const where = {}; const columns = []; for(let j = 0; j < relation.joinColumns.length; j++){ const joinColumn = relation.joinColumns[j]; if (typeof entity[joinColumn.propertyName] === 'undefined') { continue; } if (joinColumn.isNullable && entity[joinColumn.propertyName] === null) { skipRelation = true; break; } if (joinColumn.referencedColumn) { where[joinColumn.referencedColumn.propertyName] = entity[joinColumn.propertyName]; columns.push(joinColumn.propertyName); } else { throw EntityRelationLookupError.notReferenced(relation.propertyName, [ joinColumn.propertyName ]); } } if (skipRelation || columns.length === 0) { continue; } const repository = dataSource.getRepository(relation.type); const item = await repository.findOne({ where }); if (!item) { throw EntityRelationLookupError.notFound(relation.propertyName, columns); } relations[relation.propertyName] = item; } const relationKeys = Object.keys(relations); for(let i = 0; i < relationKeys.length; i++){ const relationKey = relationKeys[i]; entity[relationKey] = relations[relationKey]; } return entity; } function transformUndefinedToNull(input) { if (typeof input === 'undefined') { return null; } return input; } function applyWhereExpression(qb, data, type) { const elements = []; const keys = Object.keys(data); for(let i = 0; i < keys.length; i++){ elements.push({ key: keys[i], value: transformUndefinedToNull(data[keys[i]]), operator: type === 'target' ? rapiq.FilterComparisonOperator.EQUAL : rapiq.FilterComparisonOperator.NOT_EQUAL }); } const queryFilters = transformParsedFilters(elements, { bindingKey (key) { if (type === 'source') { return `filter_source_${key}`; } return `filter_target_${key}`; } }); applyFiltersTransformed(qb, queryFilters); return queryFilters; } /** * Check if a given entity does not already exist. * Composite unique keys on a null column can only be present once. * * @experimental * @param options */ async function isEntityUnique(options) { const dataSource = options.dataSource || await useDataSource(); const metadata = await getEntityMetadata(options.entityTarget, dataSource); const repository = dataSource.getRepository(metadata.target); const primaryColumnNames = metadata.primaryColumns.map((c)=>c.propertyName); for(let i = 0; i < metadata.ownUniques.length; i++){ const uniqueColumnNames = metadata.ownUniques[i].columns.map((column)=>column.propertyName); const queryBuilder = repository.createQueryBuilder('entity'); queryBuilder.where(new typeorm.Brackets((qb)=>{ applyWhereExpression(qb, pickRecord(options.entity, uniqueColumnNames), 'target'); })); queryBuilder.andWhere(new typeorm.Brackets((qb)=>{ if (options.entityExisting) { applyWhereExpression(qb, pickRecord(options.entityExisting, primaryColumnNames), 'source'); } })); const entity = await queryBuilder.getOne(); if (entity) { return false; } } return true; } exports.CodeTransformation = CodeTransformation; exports.DatabaseCreateCommand = DatabaseCreateCommand; exports.DatabaseDropCommand = DatabaseDropCommand; exports.DriverError = DriverError; exports.EntityRelationLookupError = EntityRelationLookupError; exports.EnvironmentName = EnvironmentName; exports.EnvironmentVariableName = EnvironmentVariableName; exports.OptionsError = OptionsError; exports.SeedCreateCommand = SeedCreateCommand; exports.SeedRunCommand = SeedRunCommand; exports.SeederEntity = SeederEntity; exports.SeederExecutor = SeederExecutor; exports.SeederFactory = SeederFactory; exports.SeederFactoryManager = SeederFactoryManager; exports.TypeormExtensionError = TypeormExtensionError; exports.adjustFilePath = adjustFilePath; exports.adjustFilePaths = adjustFilePaths; exports.applyFields = applyFields; exports.applyFilters = applyFilters; exports.applyFiltersTransformed = applyFiltersTransformed; exports.applyPagination = applyPagination; exports.applyQuery = applyQuery; exports.applyQueryFields = applyQueryFields; exports.applyQueryFieldsParseOutput = applyQueryFieldsParseOutput; exports.applyQueryFilters = applyQueryFilters; exports.applyQueryFiltersParseOutput = applyQueryFiltersParseOutput; exports.applyQueryPagination = applyQueryPagination; exports.applyQueryPaginationParseOutput = applyQueryPaginationParseOutput; exports.applyQueryParseOutput = applyQueryParseOutput; exports.applyQueryRelations = applyQueryRelations; exports.applyQueryRelationsParseOutput = applyQueryRelationsParseOutput; exports.applyQuerySort = applyQuerySort; exports.applyQuerySortParseOutput = applyQuerySortParseOutput; exports.applyRelations = applyRelations; exports.applySort = applySort; exports.buildDataSourceOptions = buildDataSourceOptions; exports.buildDatabaseCreateContext = buildDatabaseCreateContext; exports.buildDatabaseDropContext = buildDatabaseDropContext; exports.buildDriverOptions = buildDriverOptions; exports.buildFilePathname = buildFilePathname; exports.buildKeyWithPrefix = buildKeyWithPrefix; exports.buildSeederFileTemplate = buildSeederFileTemplate; exports.canReplaceWindowsSeparator = canReplaceWindowsSeparator; exports.checkDatabase = checkDatabase; exports.createCockroachDBDatabase = createCockroachDBDatabase; exports.createDatabase = createDatabase; exports.createDriver = createDriver; exports.createMongoDBDatabase = createMongoDBDatabase; exports.createMsSQLDatabase = createMsSQLDatabase; exports.createMySQLDatabase = createMySQLDatabase; exports.createOracleDatabase = createOracleDatabase; exports.createPostgresDatabase = createPostgresDatabase; exports.createSQLiteDatabase = createSQLiteDatabase; exports.createSimpleMongoDBConnection = createSimpleMongoDBConnection; exports.createSimpleMsSQLConnection = createSimpleMsSQLConnection; exports.createSimpleMySQLConnection = createSimpleMySQLConnection; exports.createSimpleOracleConnection = createSimpleOracleConnection; exports.createSimplePostgresConnection = createSimplePostgresConnection; exports.detectCodeTransformation = detectCodeTransformation; exports.dropCockroachDBDatabase = dropCockroachDBDatabase; exports.dropDatabase = dropDatabase; exports.dropMongoDBDatabase = dropMongoDBDatabase; exports.dropMsSQLDatabase = dropMsSQLDatabase; exports.dropMySQLDatabase = dropMySQLDatabase; exports.dropOracleDatabase = dropOracleDatabase; exports.dropPostgresDatabase = dropPostgresDatabase; exports.dropSQLiteDatabase = dropSQLiteDatabase; exports.executeSimpleCockroachDBQuery = executeSimpleCockroachDBQuery; exports.executeSimpleMysqlQuery = executeSimpleMysqlQuery; exports.executeSimplePostgresQuery = executeSimplePostgresQuery; exports.existsQuery = existsQuery; exports.findDataSource = findDataSource; exports.generateMigration = generateMigration; exports.getAliasForPath = getAliasForPath; exports.getCharacterSetFromDataSourceOptions = getCharacterSetFromDataSourceOptions; exports.getCharsetFromDataSourceOptions = getCharsetFromDataSourceOptions; exports.getEntityMetadata = getEntityMetadata; exports.getEntityName = getEntityName; exports.getEntityPropertyNames = getEntityPropertyNames; exports.hasDataSource = hasDataSource; exports.hasDataSourceOptions = hasDataSourceOptions; exports.hasEnvDataSourceOptions = hasEnvDataSourceOptions; exports.hasOwnProperty = hasOwnProperty; exports.hasStringProperty = hasStringProperty; exports.hasTrailingSlash = hasTrailingSlash; exports.isCodeTransformation = isCodeTransformation; exports.isDirectory = isDirectory; exports.isEntityUnique = isEntityUnique; exports.isPromise = isPromise; exports.isQueryOptionDefined = isQueryOptionDefined; exports.mergeDataSourceOptions = mergeDataSourceOptions; exports.mergeDataSourceOptionsWithEnv = mergeDataSourceOptionsWithEnv; exports.parseFilePath = parseFilePath; exports.pickRecord = pickRecord; exports.prepareSeederFactories = prepareSeederFactories; exports.prepareSeederSeeds = prepareSeederSeeds; exports.readDataSourceOptionsFromEnv = readDataSourceOptionsFromEnv; exports.readTSConfig = readTSConfig; exports.replaceWindowSeparator = replaceWindowSeparator; exports.resetEnv = resetEnv; exports.resolveFilePath = resolveFilePath; exports.resolveFilePaths = resolveFilePaths; exports.resolveFilePatterns = resolveFilePatterns; exports.runSeeder = runSeeder; exports.runSeeders = runSeeders; exports.safeReplaceWindowsSeparator = safeReplaceWindowsSeparator; exports.setDataSource = setDataSource; exports.setDataSourceOptions = setDataSourceOptions; exports.setSeederFactory = setSeederFactory; exports.synchronizeDatabaseSchema = synchronizeDatabaseSchema; exports.transformFilePath = transformFilePath; exports.transformParsedFilters = transformParsedFilters; exports.unsetDataSource = unsetDataSource; exports.useDataSource = useDataSource; exports.useDataSourceOptions = useDataSourceOptions; exports.useEnv = useEnv; exports.useSeederFactory = useSeederFactory; exports.useSeederFactoryManager = useSeederFactoryManager; exports.validateEntityJoinColumns = validateEntityJoinColumns; exports.withoutTrailingSlash = withoutTrailingSlash; //# sourceMappingURL=index.cjs.map