@depup/typeorm-extension 3.9.0-depup.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/LICENSE +21 -0
  2. package/README.MD +674 -0
  3. package/README.md +34 -0
  4. package/bin/cli.cjs +275 -0
  5. package/bin/cli.mjs +273 -0
  6. package/changes.json +22 -0
  7. package/dist/cli/commands/database/create.d.ts +27 -0
  8. package/dist/cli/commands/database/drop.d.ts +23 -0
  9. package/dist/cli/commands/database/index.d.ts +2 -0
  10. package/dist/cli/commands/index.d.ts +2 -0
  11. package/dist/cli/commands/seed/create.d.ts +21 -0
  12. package/dist/cli/commands/seed/index.d.ts +2 -0
  13. package/dist/cli/commands/seed/run.d.ts +24 -0
  14. package/dist/cli/index.d.ts +2 -0
  15. package/dist/data-source/find/index.d.ts +2 -0
  16. package/dist/data-source/find/module.d.ts +3 -0
  17. package/dist/data-source/find/type.d.ts +22 -0
  18. package/dist/data-source/index.d.ts +4 -0
  19. package/dist/data-source/options/index.d.ts +4 -0
  20. package/dist/data-source/options/module.d.ts +8 -0
  21. package/dist/data-source/options/singleton.d.ts +4 -0
  22. package/dist/data-source/options/type.d.ts +26 -0
  23. package/dist/data-source/options/utils/env.d.ts +4 -0
  24. package/dist/data-source/options/utils/index.d.ts +2 -0
  25. package/dist/data-source/options/utils/merge.d.ts +2 -0
  26. package/dist/data-source/singleton.d.ts +5 -0
  27. package/dist/data-source/type.d.ts +2 -0
  28. package/dist/database/driver/cockroachdb.d.ts +4 -0
  29. package/dist/database/driver/index.d.ts +9 -0
  30. package/dist/database/driver/mongodb.d.ts +6 -0
  31. package/dist/database/driver/mssql.d.ts +6 -0
  32. package/dist/database/driver/mysql.d.ts +7 -0
  33. package/dist/database/driver/oracle.d.ts +6 -0
  34. package/dist/database/driver/postgres.d.ts +8 -0
  35. package/dist/database/driver/sqlite.d.ts +3 -0
  36. package/dist/database/driver/types.d.ts +20 -0
  37. package/dist/database/driver/utils/build.d.ts +3 -0
  38. package/dist/database/driver/utils/character-set.d.ts +2 -0
  39. package/dist/database/driver/utils/charset.d.ts +2 -0
  40. package/dist/database/driver/utils/create.d.ts +2 -0
  41. package/dist/database/driver/utils/index.d.ts +4 -0
  42. package/dist/database/index.d.ts +3 -0
  43. package/dist/database/methods/check/index.d.ts +2 -0
  44. package/dist/database/methods/check/module.d.ts +7 -0
  45. package/dist/database/methods/check/types.d.ts +51 -0
  46. package/dist/database/methods/create/index.d.ts +1 -0
  47. package/dist/database/methods/create/module.d.ts +10 -0
  48. package/dist/database/methods/drop/index.d.ts +1 -0
  49. package/dist/database/methods/drop/module.d.ts +10 -0
  50. package/dist/database/methods/index.d.ts +4 -0
  51. package/dist/database/methods/type.d.ts +43 -0
  52. package/dist/database/utils/context.d.ts +3 -0
  53. package/dist/database/utils/index.d.ts +5 -0
  54. package/dist/database/utils/migration.d.ts +2 -0
  55. package/dist/database/utils/query.d.ts +3 -0
  56. package/dist/database/utils/schema.d.ts +3 -0
  57. package/dist/database/utils/type.d.ts +32 -0
  58. package/dist/env/constants.d.ts +68 -0
  59. package/dist/env/index.d.ts +3 -0
  60. package/dist/env/module.d.ts +4 -0
  61. package/dist/env/type.d.ts +36 -0
  62. package/dist/env/utils.d.ts +3 -0
  63. package/dist/errors/base.d.ts +2 -0
  64. package/dist/errors/driver.d.ts +6 -0
  65. package/dist/errors/index.d.ts +3 -0
  66. package/dist/errors/options.d.ts +7 -0
  67. package/dist/helpers/entity/error.d.ts +20 -0
  68. package/dist/helpers/entity/index.d.ts +5 -0
  69. package/dist/helpers/entity/join-columns.d.ts +16 -0
  70. package/dist/helpers/entity/metadata.d.ts +10 -0
  71. package/dist/helpers/entity/property-names.d.ts +11 -0
  72. package/dist/helpers/entity/uniqueness.d.ts +28 -0
  73. package/dist/helpers/index.d.ts +1 -0
  74. package/dist/index.cjs +3258 -0
  75. package/dist/index.cjs.map +1 -0
  76. package/dist/index.d.ts +9 -0
  77. package/dist/index.mjs +3136 -0
  78. package/dist/index.mjs.map +1 -0
  79. package/dist/query/index.d.ts +4 -0
  80. package/dist/query/module.d.ts +5 -0
  81. package/dist/query/parameter/fields/index.d.ts +2 -0
  82. package/dist/query/parameter/fields/module.d.ts +25 -0
  83. package/dist/query/parameter/fields/type.d.ts +6 -0
  84. package/dist/query/parameter/filters/index.d.ts +2 -0
  85. package/dist/query/parameter/filters/module.d.ts +35 -0
  86. package/dist/query/parameter/filters/type.d.ts +12 -0
  87. package/dist/query/parameter/index.d.ts +5 -0
  88. package/dist/query/parameter/pagination/index.d.ts +2 -0
  89. package/dist/query/parameter/pagination/module.d.ts +26 -0
  90. package/dist/query/parameter/pagination/type.d.ts +3 -0
  91. package/dist/query/parameter/relations/index.d.ts +2 -0
  92. package/dist/query/parameter/relations/module.d.ts +27 -0
  93. package/dist/query/parameter/relations/type.d.ts +8 -0
  94. package/dist/query/parameter/sort/index.d.ts +2 -0
  95. package/dist/query/parameter/sort/module.d.ts +26 -0
  96. package/dist/query/parameter/sort/type.d.ts +6 -0
  97. package/dist/query/type.d.ts +16 -0
  98. package/dist/query/utils/alias.d.ts +2 -0
  99. package/dist/query/utils/index.d.ts +3 -0
  100. package/dist/query/utils/key.d.ts +1 -0
  101. package/dist/query/utils/option.d.ts +1 -0
  102. package/dist/seeder/entity.d.ts +42 -0
  103. package/dist/seeder/executor.d.ts +24 -0
  104. package/dist/seeder/factory/index.d.ts +4 -0
  105. package/dist/seeder/factory/manager.d.ts +8 -0
  106. package/dist/seeder/factory/module.d.ts +17 -0
  107. package/dist/seeder/factory/type.d.ts +12 -0
  108. package/dist/seeder/factory/utils.d.ts +7 -0
  109. package/dist/seeder/index.d.ts +6 -0
  110. package/dist/seeder/module.d.ts +5 -0
  111. package/dist/seeder/type.d.ts +44 -0
  112. package/dist/seeder/utils/file-path.d.ts +7 -0
  113. package/dist/seeder/utils/index.d.ts +3 -0
  114. package/dist/seeder/utils/prepare.d.ts +2 -0
  115. package/dist/seeder/utils/template.d.ts +1 -0
  116. package/dist/utils/code-transformation/constants.d.ts +4 -0
  117. package/dist/utils/code-transformation/index.d.ts +2 -0
  118. package/dist/utils/code-transformation/module.d.ts +3 -0
  119. package/dist/utils/entity.d.ts +2 -0
  120. package/dist/utils/file-path.d.ts +9 -0
  121. package/dist/utils/file-system.d.ts +1 -0
  122. package/dist/utils/has-property.d.ts +2 -0
  123. package/dist/utils/index.d.ts +10 -0
  124. package/dist/utils/object.d.ts +2 -0
  125. package/dist/utils/promise.d.ts +1 -0
  126. package/dist/utils/separator.d.ts +3 -0
  127. package/dist/utils/slash.d.ts +2 -0
  128. package/dist/utils/tsconfig/index.d.ts +2 -0
  129. package/dist/utils/tsconfig/module.d.ts +2 -0
  130. package/dist/utils/tsconfig/type.d.ts +13 -0
  131. package/package.json +134 -0
package/dist/index.mjs ADDED
@@ -0,0 +1,3136 @@
1
+ import { load, isObject, removeFileNameExtension, locate, locateMany, getFileNameExtension } from 'locter';
2
+ import { parseQueryFields, FilterComparisonOperator, parseQueryFilters, parseQueryPagination, parseQueryRelations, parseQuerySort, parseQuery } from 'rapiq';
3
+ import process$1 from 'node:process';
4
+ import { InstanceChecker, Brackets, DataSource, MigrationExecutor, Table, MssqlParameter, Repository } from 'typeorm';
5
+ import path from 'node:path';
6
+ import fs from 'node:fs';
7
+ import { consola } from 'consola';
8
+ import { toBool, oneOf, read, readInt, readBool, toArray, readArray } from 'envix';
9
+ import { createMerger } from 'smob';
10
+ import { DriverUtils } from 'typeorm/driver/DriverUtils.js';
11
+ import { DriverFactory } from 'typeorm/driver/DriverFactory.js';
12
+ import { pascalCase } from 'pascal-case';
13
+ import { MigrationGenerateCommand } from 'typeorm/commands/MigrationGenerateCommand.js';
14
+
15
+ class TypeormExtensionError extends Error {
16
+ }
17
+
18
+ class DriverError extends TypeormExtensionError {
19
+ constructor(message){
20
+ super(message || 'A database driver related error has occurred.');
21
+ }
22
+ static undeterminable() {
23
+ return new DriverError('The driver could not be determined.');
24
+ }
25
+ static notSupported(driverName) {
26
+ return new DriverError(`The driver ${driverName} is not supported yet.`);
27
+ }
28
+ }
29
+
30
+ class OptionsError extends TypeormExtensionError {
31
+ constructor(message){
32
+ super(message || 'A database options related error has occurred');
33
+ }
34
+ static undeterminable() {
35
+ return new OptionsError('The database options could not be determined.');
36
+ }
37
+ static notFound() {
38
+ return new OptionsError('The database options could not be located/loaded.');
39
+ }
40
+ static databaseNotDefined() {
41
+ return new OptionsError('The database name to connect to is not defined.');
42
+ }
43
+ }
44
+
45
+ var CodeTransformation = /*#__PURE__*/ function(CodeTransformation) {
46
+ CodeTransformation["JUST_IN_TIME"] = "jit";
47
+ CodeTransformation["NONE"] = "none";
48
+ return CodeTransformation;
49
+ }({});
50
+
51
+ function detectCodeTransformation() {
52
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
53
+ // @ts-ignore
54
+ if (process$1[Symbol.for('ts-node.register.instance')]) {
55
+ return CodeTransformation.JUST_IN_TIME;
56
+ }
57
+ return CodeTransformation.NONE;
58
+ }
59
+ function isCodeTransformation(input) {
60
+ return detectCodeTransformation() === input;
61
+ }
62
+
63
+ function getEntityName(entity) {
64
+ if (typeof entity === 'function') {
65
+ return entity.name;
66
+ }
67
+ if (InstanceChecker.isEntitySchema(entity)) {
68
+ return entity.options.name;
69
+ }
70
+ return new entity().constructor.name;
71
+ }
72
+
73
+ function canReplaceWindowsSeparator(input) {
74
+ // https://superuser.com/questions/176388/why-does-windows-use-backslashes-for-paths-and-unix-forward-slashes/176395#176395
75
+ if (input.startsWith('\\\\?\\')) {
76
+ return false;
77
+ }
78
+ let characterIndex;
79
+ const specialCharacters = [
80
+ '[',
81
+ '{',
82
+ '(',
83
+ '^',
84
+ '$',
85
+ '.',
86
+ '|',
87
+ '?',
88
+ '*',
89
+ '+'
90
+ ];
91
+ for(let i = 0; i < specialCharacters.length; i++){
92
+ characterIndex = input.indexOf(specialCharacters[i]);
93
+ if (characterIndex !== -1) {
94
+ // special character is prefixed with \, no transformation allowed
95
+ if (characterIndex !== 0 && input[characterIndex - 1] === '\\') {
96
+ return false;
97
+ }
98
+ }
99
+ }
100
+ return true;
101
+ }
102
+ function replaceWindowSeparator(input) {
103
+ return input.replace(/\\/g, '/');
104
+ }
105
+ function safeReplaceWindowsSeparator(input) {
106
+ if (input.indexOf('\\') === -1 || !canReplaceWindowsSeparator(input)) {
107
+ return input;
108
+ }
109
+ return replaceWindowSeparator(input);
110
+ }
111
+
112
+ const TRAILING_SLASH_RE = /\/$|\/\?/;
113
+ function hasTrailingSlash(input = '', queryParams = false) {
114
+ if (!queryParams) {
115
+ return input.endsWith('/');
116
+ }
117
+ return TRAILING_SLASH_RE.test(input);
118
+ }
119
+ function withoutTrailingSlash(input = '', queryParams = false) {
120
+ if (!queryParams) {
121
+ return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || '/';
122
+ }
123
+ if (!hasTrailingSlash(input, true)) {
124
+ return input || '/';
125
+ }
126
+ const [s0, ...s] = input.split('?');
127
+ return (s0.slice(0, -1) || '/') + (s.length ? `?${s.join('?')}` : '');
128
+ }
129
+
130
+ async function readTSConfig(input) {
131
+ input = input || process.cwd();
132
+ input = path.isAbsolute(input) ? input : path.resolve(process.cwd(), input);
133
+ const filePath = input.indexOf('.json') === -1 ? path.join(input, 'tsconfig.json') : input;
134
+ try {
135
+ const tsConfig = await load(filePath);
136
+ if (isObject(tsConfig)) {
137
+ return tsConfig;
138
+ }
139
+ } catch (e) {
140
+ // don't do anything ;)
141
+ }
142
+ return {};
143
+ }
144
+
145
+ const stripLeadingModifier = (text)=>{
146
+ if (text.startsWith('./')) {
147
+ text = text.substring(2);
148
+ }
149
+ return text;
150
+ };
151
+ function transformFilePath(input, dist, src) {
152
+ let separator = path.sep;
153
+ const windowsSeparatorReplaceable = canReplaceWindowsSeparator(input);
154
+ if (windowsSeparatorReplaceable) {
155
+ separator = '/';
156
+ input = replaceWindowSeparator(input);
157
+ }
158
+ let base = input;
159
+ let baseIndex = input.lastIndexOf(separator);
160
+ if (baseIndex !== -1) {
161
+ base = base.substring(baseIndex + 1);
162
+ }
163
+ if (src) {
164
+ if (windowsSeparatorReplaceable) {
165
+ src = replaceWindowSeparator(src);
166
+ }
167
+ src = withoutTrailingSlash(stripLeadingModifier(src));
168
+ }
169
+ src = src || 'src';
170
+ if (dist) {
171
+ if (windowsSeparatorReplaceable) {
172
+ dist = replaceWindowSeparator(dist);
173
+ }
174
+ dist = withoutTrailingSlash(stripLeadingModifier(dist));
175
+ }
176
+ dist = dist || 'dist';
177
+ if (input.indexOf(src) !== -1 && input.indexOf(dist) === -1) {
178
+ const lastIndex = input.lastIndexOf(src);
179
+ const prevCharacter = input.substring(lastIndex - 1, lastIndex);
180
+ if (!prevCharacter || prevCharacter === separator) {
181
+ input = input.substring(0, lastIndex) + dist + input.substring(lastIndex + src.length);
182
+ baseIndex = input.lastIndexOf(separator);
183
+ }
184
+ }
185
+ // if the path already contains a js file extension, we are done
186
+ const jsExtensions = [
187
+ 'js',
188
+ 'cjs',
189
+ 'mjs'
190
+ ];
191
+ for(let i = 0; i < jsExtensions.length; i++){
192
+ if (base.indexOf(jsExtensions[i]) !== -1) {
193
+ return input;
194
+ }
195
+ }
196
+ const tsExtensions = [
197
+ 'ts',
198
+ 'cts',
199
+ 'mts'
200
+ ];
201
+ for(let i = 0; i < tsExtensions.length; i++){
202
+ const regex = new RegExp(`(\\.${tsExtensions[i]}|${tsExtensions[i]})`, 'g');
203
+ let matchesSum;
204
+ const matches = base.match(regex);
205
+ if (Array.isArray(matches)) {
206
+ matchesSum = matches.length;
207
+ }
208
+ let matchesCounter = 0;
209
+ const bracketIndex = base.lastIndexOf('{');
210
+ base = base.replace(regex, (...args)=>{
211
+ matchesCounter++;
212
+ // if the file extension name comes after the last bracket index,
213
+ // we can be pretty sure that the extension name is not part of a filename
214
+ if (args[2] >= bracketIndex && bracketIndex !== -1 || bracketIndex === -1 && matchesCounter === matchesSum) {
215
+ return args[0].startsWith('.') ? `.${jsExtensions[i]}` : jsExtensions[i];
216
+ }
217
+ return args[0];
218
+ });
219
+ }
220
+ if (baseIndex !== -1) {
221
+ base = input.substring(0, baseIndex + 1) + base;
222
+ }
223
+ return stripLeadingModifier(base);
224
+ }
225
+ async function adjustFilePath(input, tsconfig) {
226
+ if (isCodeTransformation(CodeTransformation.JUST_IN_TIME)) {
227
+ return input;
228
+ }
229
+ if (!isObject(tsconfig)) {
230
+ tsconfig = await readTSConfig(tsconfig);
231
+ }
232
+ const { compilerOptions } = tsconfig;
233
+ if (typeof input === 'string') {
234
+ return transformFilePath(input, compilerOptions?.outDir);
235
+ }
236
+ if (Array.isArray(input)) {
237
+ for(let i = 0; i < input.length; i++){
238
+ if (typeof input[i] === 'string') {
239
+ input[i] = transformFilePath(input[i], compilerOptions?.outDir);
240
+ }
241
+ }
242
+ }
243
+ return input;
244
+ }
245
+ async function adjustFilePaths(input, keys, tsconfig) {
246
+ if (isCodeTransformation(CodeTransformation.JUST_IN_TIME)) {
247
+ return input;
248
+ }
249
+ if (!isObject(tsconfig)) {
250
+ tsconfig = await readTSConfig(tsconfig);
251
+ }
252
+ keys = keys || Object.keys(input);
253
+ for(let i = 0; i < keys.length; i++){
254
+ input[keys[i]] = await adjustFilePath(input[keys[i]], tsconfig);
255
+ }
256
+ return input;
257
+ }
258
+ function resolveFilePath(filePath, root) {
259
+ if (path.isAbsolute(filePath)) {
260
+ return filePath;
261
+ }
262
+ return filePath.startsWith('/') ? filePath : path.resolve(root || process.cwd(), filePath);
263
+ }
264
+ function parseFilePath(filePath, root) {
265
+ const fullPath = resolveFilePath(filePath, root);
266
+ const directory = path.dirname(fullPath);
267
+ const name = path.basename(fullPath);
268
+ return {
269
+ directory,
270
+ name
271
+ };
272
+ }
273
+
274
+ async function isDirectory(input) {
275
+ try {
276
+ const stat = await fs.promises.stat(input);
277
+ return stat.isDirectory();
278
+ } catch (e) {
279
+ return false;
280
+ }
281
+ }
282
+
283
+ function hasOwnProperty(obj, prop) {
284
+ return Object.prototype.hasOwnProperty.call(obj, prop);
285
+ }
286
+ function hasStringProperty(obj, prop) {
287
+ return hasOwnProperty(obj, prop) && typeof obj[prop] === 'string';
288
+ }
289
+
290
+ function pickRecord(data, keys) {
291
+ const output = {};
292
+ for(let i = 0; i < keys.length; i++){
293
+ output[keys[i]] = data[keys[i]];
294
+ }
295
+ return output;
296
+ }
297
+ function extendObject(target, source) {
298
+ const keys = Object.keys(source);
299
+ for(let i = 0; i < keys.length; i++){
300
+ target[keys[i]] = source[keys[i]];
301
+ }
302
+ return target;
303
+ }
304
+
305
+ function isPromise(p) {
306
+ return isObject(p) && (p instanceof Promise || // eslint-disable-next-line @typescript-eslint/ban-ts-comment
307
+ // @ts-ignore
308
+ typeof p.then === 'function');
309
+ }
310
+
311
+ function getAliasForPath(items, path) {
312
+ if (typeof path === 'undefined' || typeof items === 'undefined') {
313
+ return undefined;
314
+ }
315
+ for(let i = 0; i < items.length; i++){
316
+ if (items[i].key === path) {
317
+ return items[i].value;
318
+ }
319
+ }
320
+ return undefined;
321
+ }
322
+
323
+ function buildKeyWithPrefix(name, prefix) {
324
+ if (prefix) {
325
+ return `${prefix}.${name}`;
326
+ }
327
+ return name;
328
+ }
329
+
330
+ function isQueryOptionDefined(input, option) {
331
+ if (typeof input === 'boolean') {
332
+ return false;
333
+ }
334
+ const options = Array.isArray(option) ? option : [
335
+ option
336
+ ];
337
+ for(let i = 0; i < options.length; i++){
338
+ if (hasOwnProperty(input, options[i])) {
339
+ return true;
340
+ }
341
+ }
342
+ return false;
343
+ }
344
+
345
+ /**
346
+ * Apply parsed fields parameter data on the db query.
347
+ *
348
+ * @param query
349
+ * @param data
350
+ */ /* istanbul ignore next */ function applyQueryFieldsParseOutput(query, data, options = {}) {
351
+ if (data.length === 0) {
352
+ return data;
353
+ }
354
+ query.select(data.map((field)=>{
355
+ const alias = getAliasForPath(options.relations, field.path) || options.defaultAlias || options.defaultPath;
356
+ return buildKeyWithPrefix(field.key, alias);
357
+ }));
358
+ return data;
359
+ }
360
+ /**
361
+ * Apply raw fields parameter data on the db query.
362
+ *
363
+ * @param query
364
+ * @param data
365
+ * @param options
366
+ */ function applyQueryFields(query, data, options) {
367
+ options = options || {};
368
+ if (options.defaultAlias) {
369
+ options.defaultPath = options.defaultAlias;
370
+ }
371
+ return applyQueryFieldsParseOutput(query, parseQueryFields(data, options), options);
372
+ }
373
+ /**
374
+ * Apply raw fields parameter data on the db query.
375
+ *
376
+ * @param query
377
+ * @param data
378
+ * @param options
379
+ */ function applyFields(query, data, options) {
380
+ return applyQueryFields(query, data, options);
381
+ }
382
+
383
+ // --------------------------------------------------
384
+ function transformParsedFilters(data, options = {}) {
385
+ const items = [];
386
+ for(let i = 0; i < data.length; i++){
387
+ const alias = getAliasForPath(options.relations, data[i].path) || options.defaultAlias || options.defaultPath;
388
+ const fullKey = buildKeyWithPrefix(data[i].key, alias);
389
+ const filter = data[i];
390
+ const statement = [
391
+ fullKey
392
+ ];
393
+ let bindingKey;
394
+ if (options.bindingKey) {
395
+ bindingKey = options.bindingKey(fullKey).replace('.', '_');
396
+ } else {
397
+ bindingKey = `filter_${fullKey.replace('.', '_')}`;
398
+ }
399
+ if (filter.value === null || typeof filter.value === 'undefined') {
400
+ statement.push('IS');
401
+ if (filter.operator === FilterComparisonOperator.NOT_EQUAL) {
402
+ statement.push('NOT');
403
+ }
404
+ statement.push('NULL');
405
+ items.push({
406
+ statement: statement.join(' '),
407
+ binding: {}
408
+ });
409
+ continue;
410
+ }
411
+ switch(filter.operator){
412
+ case FilterComparisonOperator.EQUAL:
413
+ case FilterComparisonOperator.NOT_EQUAL:
414
+ {
415
+ if (filter.operator === FilterComparisonOperator.EQUAL) {
416
+ statement.push('=');
417
+ } else {
418
+ statement.push('!=');
419
+ }
420
+ statement.push(`:${bindingKey}`);
421
+ break;
422
+ }
423
+ case FilterComparisonOperator.LIKE:
424
+ case FilterComparisonOperator.NOT_LIKE:
425
+ {
426
+ if (filter.operator === FilterComparisonOperator.NOT_LIKE) {
427
+ statement.push('NOT');
428
+ }
429
+ statement.push('LIKE');
430
+ statement.push(`:${bindingKey}`);
431
+ filter.value += '%';
432
+ break;
433
+ }
434
+ case FilterComparisonOperator.IN:
435
+ case FilterComparisonOperator.NOT_IN:
436
+ {
437
+ if (filter.operator === FilterComparisonOperator.NOT_IN) {
438
+ statement.push('NOT');
439
+ }
440
+ statement.push('IN');
441
+ statement.push(`(:...${bindingKey})`);
442
+ if (Array.isArray(filter.value)) {
443
+ const nullIndex = filter.value.indexOf(null);
444
+ if (nullIndex !== -1) {
445
+ filter.value.splice(nullIndex, 1);
446
+ statement.unshift('(');
447
+ if (filter.operator === FilterComparisonOperator.NOT_IN) {
448
+ statement.push('AND');
449
+ } else {
450
+ statement.push('OR');
451
+ }
452
+ statement.push(fullKey);
453
+ statement.push('IS');
454
+ if (filter.operator === FilterComparisonOperator.NOT_IN) {
455
+ statement.push('NOT');
456
+ }
457
+ statement.push('NULL');
458
+ statement.push(')');
459
+ }
460
+ }
461
+ break;
462
+ }
463
+ case FilterComparisonOperator.LESS_THAN:
464
+ case FilterComparisonOperator.LESS_THAN_EQUAL:
465
+ case FilterComparisonOperator.GREATER_THAN:
466
+ case FilterComparisonOperator.GREATER_THAN_EQUAL:
467
+ {
468
+ if (filter.operator === FilterComparisonOperator.LESS_THAN) {
469
+ statement.push('<');
470
+ } else if (filter.operator === FilterComparisonOperator.LESS_THAN_EQUAL) {
471
+ statement.push('<=');
472
+ } else if (filter.operator === FilterComparisonOperator.GREATER_THAN) {
473
+ statement.push('>');
474
+ } else {
475
+ statement.push('>=');
476
+ }
477
+ statement.push(`:${bindingKey}`);
478
+ break;
479
+ }
480
+ }
481
+ items.push({
482
+ statement: statement.join(' '),
483
+ binding: {
484
+ [bindingKey]: filter.value
485
+ }
486
+ });
487
+ }
488
+ return items;
489
+ }
490
+ /**
491
+ * Apply transformed filter[s] parameter data on the db query.
492
+ *
493
+ * @param query
494
+ * @param data
495
+ */ function applyFiltersTransformed(query, data) {
496
+ if (data.length === 0) {
497
+ return data;
498
+ }
499
+ /* istanbul ignore next */ query.andWhere(new Brackets((qb)=>{
500
+ for(let i = 0; i < data.length; i++){
501
+ if (i === 0) {
502
+ qb.where(data[i].statement, data[i].binding);
503
+ } else {
504
+ qb.andWhere(data[i].statement, data[i].binding);
505
+ }
506
+ }
507
+ }));
508
+ return data;
509
+ }
510
+ /**
511
+ * Apply parsed filter[s] parameter data on the db query.
512
+ *
513
+ * @param query
514
+ * @param data
515
+ * @param options
516
+ */ function applyQueryFiltersParseOutput(query, data, options) {
517
+ applyFiltersTransformed(query, transformParsedFilters(data, options));
518
+ return data;
519
+ }
520
+ // --------------------------------------------------
521
+ /**
522
+ * Apply raw filter[s] parameter data on the db query.
523
+ *
524
+ * @param query
525
+ * @param data
526
+ * @param options
527
+ */ function applyQueryFilters(query, data, options) {
528
+ options = options || {};
529
+ if (options.defaultAlias) {
530
+ options.defaultPath = options.defaultAlias;
531
+ }
532
+ return applyQueryFiltersParseOutput(query, parseQueryFilters(data, options), options);
533
+ }
534
+ /**
535
+ * Apply raw filter[s] parameter data on the db query.
536
+ *
537
+ * @param query
538
+ * @param data
539
+ * @param options
540
+ */ function applyFilters(query, data, options) {
541
+ return applyQueryFilters(query, data, options);
542
+ }
543
+
544
+ /**
545
+ * Apply parsed page/pagination parameter data on the db query.
546
+ *
547
+ * @param query
548
+ * @param data
549
+ */ function applyQueryPaginationParseOutput(query, data) {
550
+ /* istanbul ignore next */ if (typeof data.limit !== 'undefined') {
551
+ query.take(data.limit);
552
+ if (typeof data.offset === 'undefined') {
553
+ query.skip(0);
554
+ }
555
+ }
556
+ /* istanbul ignore next */ if (typeof data.offset !== 'undefined') {
557
+ query.skip(data.offset);
558
+ }
559
+ return data;
560
+ }
561
+ /**
562
+ * Apply raw page/pagination parameter data on the db query.
563
+ *
564
+ * @param query
565
+ * @param data
566
+ * @param options
567
+ */ function applyQueryPagination(query, data, options) {
568
+ return applyQueryPaginationParseOutput(query, parseQueryPagination(data, options));
569
+ }
570
+ /**
571
+ * Apply raw page/pagination parameter data on the db query.
572
+ *
573
+ * @param query
574
+ * @param data
575
+ * @param options
576
+ */ function applyPagination(query, data, options) {
577
+ return applyQueryPagination(query, data, options);
578
+ }
579
+
580
+ /**
581
+ * Apply parsed include/relation parameter data on the db query.
582
+ *
583
+ * @param query
584
+ * @param data
585
+ * @param options
586
+ */ function applyQueryRelationsParseOutput(query, data, options = {}) {
587
+ for(let i = 0; i < data.length; i++){
588
+ const parts = data[i].key.split('.');
589
+ let key;
590
+ if (parts.length > 1) {
591
+ key = parts.slice(-2).join('.');
592
+ } else {
593
+ key = buildKeyWithPrefix(data[i].key, options.defaultAlias);
594
+ }
595
+ data[i].key = key;
596
+ /* istanbul ignore next */ query.leftJoinAndSelect(key, data[i].value);
597
+ if (options.onJoin) {
598
+ options.onJoin(key, data[i].value, query);
599
+ }
600
+ }
601
+ return data;
602
+ }
603
+ /**
604
+ * Apply raw include/relations parameter data on the db query.
605
+ *
606
+ * @param query
607
+ * @param data
608
+ * @param options
609
+ */ function applyQueryRelations(query, data, options = {}) {
610
+ return applyQueryRelationsParseOutput(query, parseQueryRelations(data, options), options);
611
+ }
612
+ /**
613
+ * Apply raw include/relations parameter data on the db query.
614
+ *
615
+ * @param query
616
+ * @param data
617
+ * @param options
618
+ */ function applyRelations(query, data, options = {}) {
619
+ return applyQueryRelations(query, data, options);
620
+ }
621
+
622
+ // --------------------------------------------------
623
+ /**
624
+ * Apply parsed sort parameter data on the db query.
625
+ *
626
+ * @param query
627
+ * @param data
628
+ */ function applyQuerySortParseOutput(query, data) {
629
+ if (data.length === 0) {
630
+ return data;
631
+ }
632
+ const sort = {};
633
+ for(let i = 0; i < data.length; i++){
634
+ const key = buildKeyWithPrefix(data[i].key, data[i].path);
635
+ sort[key] = data[i].value;
636
+ }
637
+ query.orderBy(sort);
638
+ return data;
639
+ }
640
+ /**
641
+ * Apply raw sort parameter data on the db query.
642
+ *
643
+ * @param query
644
+ * @param data
645
+ * @param options
646
+ */ function applyQuerySort(query, data, options) {
647
+ options = options || {};
648
+ if (options.defaultAlias) {
649
+ options.defaultPath = options.defaultAlias;
650
+ }
651
+ return applyQuerySortParseOutput(query, parseQuerySort(data, options));
652
+ }
653
+ /**
654
+ * Apply raw sort parameter data on the db query.
655
+ *
656
+ * @param query
657
+ * @param data
658
+ * @param options
659
+ */ function applySort(query, data, options) {
660
+ return applyQuerySort(query, data, options);
661
+ }
662
+
663
+ function applyQueryParseOutput(query, context, options = {}) {
664
+ if (context.fields) {
665
+ applyQueryFieldsParseOutput(query, context.fields, {
666
+ defaultAlias: context.defaultPath,
667
+ relations: context.relations
668
+ });
669
+ }
670
+ if (context.filters) {
671
+ applyQueryFiltersParseOutput(query, context.filters, {
672
+ defaultAlias: context.defaultPath,
673
+ relations: context.relations
674
+ });
675
+ }
676
+ if (context.pagination) {
677
+ applyQueryPaginationParseOutput(query, context.pagination);
678
+ }
679
+ if (context.relations) {
680
+ const relations = {
681
+ defaultAlias: context.defaultPath
682
+ };
683
+ if (isObject(options.relations)) {
684
+ extendObject(relations, options.relations);
685
+ }
686
+ applyQueryRelationsParseOutput(query, context.relations, relations);
687
+ }
688
+ if (context.sort) {
689
+ applyQuerySortParseOutput(query, context.sort);
690
+ }
691
+ return context;
692
+ }
693
+ function applyQuery(query, input, options = {}) {
694
+ if (options.defaultAlias) {
695
+ options.defaultPath = options.defaultAlias;
696
+ }
697
+ if (typeof options.fields === 'undefined' || !isQueryOptionDefined(options.fields, [
698
+ 'allowed',
699
+ 'default'
700
+ ])) {
701
+ options.fields = false;
702
+ }
703
+ if (typeof options.filters === 'undefined' || !isQueryOptionDefined(options.filters, [
704
+ 'allowed',
705
+ 'default'
706
+ ])) {
707
+ options.filters = false;
708
+ }
709
+ if (options.pagination) {
710
+ if (!input.pagination && !input.page) {
711
+ input.pagination = input.pagination || {};
712
+ }
713
+ } else {
714
+ options.pagination = false;
715
+ }
716
+ if (typeof options.relations === 'undefined' || !isQueryOptionDefined(options.relations, [
717
+ 'allowed'
718
+ ])) {
719
+ options.relations = false;
720
+ }
721
+ if (typeof options.sort === 'undefined' || !isQueryOptionDefined(options.sort, [
722
+ 'allowed',
723
+ 'default'
724
+ ])) {
725
+ options.sort = false;
726
+ }
727
+ const output = applyQueryParseOutput(query, parseQuery(input, options), options);
728
+ return {
729
+ ...output,
730
+ ...options.defaultAlias ? {
731
+ defaultAlias: options.defaultAlias
732
+ } : {}
733
+ };
734
+ }
735
+
736
+ async function findDataSource(context = {}) {
737
+ let tsconfig;
738
+ if (!context.preserveFilePaths) {
739
+ if (isObject(context.tsconfig)) {
740
+ tsconfig = context.tsconfig;
741
+ } else {
742
+ tsconfig = await readTSConfig(context.tsconfig);
743
+ }
744
+ }
745
+ const files = [
746
+ 'data-source'
747
+ ];
748
+ if (context.fileName) {
749
+ context.fileName = removeFileNameExtension(context.fileName, [
750
+ '.ts',
751
+ '.mts',
752
+ '.cts',
753
+ '.js',
754
+ '.mjs',
755
+ '.cjs'
756
+ ]);
757
+ if (context.fileName !== 'data-source') {
758
+ files.unshift(context.fileName);
759
+ }
760
+ }
761
+ let { directory } = context;
762
+ let directoryIsPattern = false;
763
+ if (context.directory) {
764
+ if (path.isAbsolute(context.directory)) {
765
+ directory = context.directory;
766
+ } else {
767
+ directoryIsPattern = true;
768
+ directory = safeReplaceWindowsSeparator(context.directory);
769
+ }
770
+ if (!context.preserveFilePaths) {
771
+ directory = await adjustFilePath(directory, tsconfig);
772
+ }
773
+ }
774
+ const lookupPaths = [];
775
+ for(let j = 0; j < files.length; j++){
776
+ if (directory && directoryIsPattern) {
777
+ lookupPaths.push(path.posix.join(directory, files[j]));
778
+ }
779
+ lookupPaths.push(...[
780
+ path.posix.join('src', files[j]),
781
+ path.posix.join('src/{db,database}', files[j])
782
+ ]);
783
+ }
784
+ files.push(...lookupPaths);
785
+ if (!context.preserveFilePaths) {
786
+ for(let j = 0; j < files.length; j++){
787
+ files[j] = await adjustFilePath(files[j], tsconfig);
788
+ }
789
+ }
790
+ for(let i = 0; i < files.length; i++){
791
+ const info = await locate(`${files[i]}.{js,cjs,mjs,ts,cts,mts}`, {
792
+ path: [
793
+ process.cwd(),
794
+ ...directory && !directoryIsPattern ? [
795
+ directory
796
+ ] : []
797
+ ],
798
+ ignore: [
799
+ '**/*.d.ts'
800
+ ]
801
+ });
802
+ if (info) {
803
+ let moduleRecord = await load(info);
804
+ if (isPromise(moduleRecord)) {
805
+ moduleRecord = await moduleRecord;
806
+ }
807
+ if (InstanceChecker.isDataSource(moduleRecord)) {
808
+ return moduleRecord;
809
+ }
810
+ if (!isObject(moduleRecord)) {
811
+ continue;
812
+ }
813
+ const keys = Object.keys(moduleRecord);
814
+ for(let j = 0; j < keys.length; j++){
815
+ let value = moduleRecord[keys[j]];
816
+ if (isPromise(value)) {
817
+ value = await value;
818
+ }
819
+ if (InstanceChecker.isDataSource(value)) {
820
+ return value;
821
+ }
822
+ }
823
+ }
824
+ }
825
+ return undefined;
826
+ }
827
+
828
+ /*
829
+ * Copyright (c) 2023-2023.
830
+ * Author Peter Placzek (tada5hi)
831
+ * For the full copyright and license information,
832
+ * view the LICENSE file that was distributed with this source code.
833
+ */ var EnvironmentName = /*#__PURE__*/ function(EnvironmentName) {
834
+ EnvironmentName["DEVELOPMENT"] = "development";
835
+ EnvironmentName["PRODUCTION"] = "production";
836
+ EnvironmentName["TEST"] = "test";
837
+ return EnvironmentName;
838
+ }({});
839
+ var EnvironmentVariableName = /*#__PURE__*/ function(EnvironmentVariableName) {
840
+ EnvironmentVariableName["ENV"] = "NODE_ENV";
841
+ // Seeder
842
+ EnvironmentVariableName["SEEDS"] = "DB_SEEDS";
843
+ EnvironmentVariableName["SEEDS_ALT"] = "TYPEORM_SEEDING_SEEDS";
844
+ EnvironmentVariableName["FACTORIES"] = "DB_FACTORIES";
845
+ EnvironmentVariableName["FACTORIES_ALT"] = "TYPEORM_SEEDING_FACTORIES";
846
+ // Database
847
+ EnvironmentVariableName["TYPE"] = "DB_TYPE";
848
+ EnvironmentVariableName["TYPE_ALT"] = "TYPEORM_CONNECTION";
849
+ EnvironmentVariableName["URL"] = "DB_URL";
850
+ EnvironmentVariableName["URL_ALT"] = "TYPEORM_URL";
851
+ EnvironmentVariableName["HOST"] = "DB_HOST";
852
+ EnvironmentVariableName["HOST_ALT"] = "TYPEORM_HOST";
853
+ EnvironmentVariableName["PORT"] = "DB_PORT";
854
+ EnvironmentVariableName["PORT_ALT"] = "TYPEORM_PORT";
855
+ EnvironmentVariableName["USERNAME"] = "DB_USERNAME";
856
+ EnvironmentVariableName["USERNAME_ALT"] = "TYPEORM_USERNAME";
857
+ EnvironmentVariableName["PASSWORD"] = "DB_PASSWORD";
858
+ EnvironmentVariableName["PASSWORD_ALT"] = "TYPEORM_PASSWORD";
859
+ EnvironmentVariableName["DATABASE"] = "DB_DATABASE";
860
+ EnvironmentVariableName["DATABASE_ALT"] = "TYPEORM_DATABASE";
861
+ EnvironmentVariableName["SID"] = "DB_SID";
862
+ EnvironmentVariableName["SID_ALT"] = "TYPEORM_SID";
863
+ EnvironmentVariableName["SCHEMA"] = "DB_SCHEMA";
864
+ EnvironmentVariableName["SCHEMA_ALT"] = "TYPEORM_SCHEMA";
865
+ EnvironmentVariableName["SCHEMA_DROP"] = "DB_DROP_SCHEMA";
866
+ EnvironmentVariableName["SCHEMA_DROP_ALT"] = "TYPEORM_DROP_SCHEMA";
867
+ EnvironmentVariableName["DRIVER_EXTRA"] = "DB_DRIVER_EXTRA";
868
+ EnvironmentVariableName["DRIVER_EXTRA_ALT"] = "TYPEORM_DRIVER_EXTRA";
869
+ EnvironmentVariableName["SYNCHRONIZE"] = "DB_SYNCHRONIZE";
870
+ EnvironmentVariableName["SYNCHRONIZE_ALT"] = "TYPEORM_SYNCHRONIZE";
871
+ EnvironmentVariableName["MIGRATIONS"] = "DB_MIGRATIONS";
872
+ EnvironmentVariableName["MIGRATIONS_ALT"] = "TYPEORM_MIGRATIONS";
873
+ EnvironmentVariableName["MIGRATIONS_RUN"] = "DB_MIGRATIONS_RUN";
874
+ EnvironmentVariableName["MIGRATIONS_RUN_ALT"] = "TYPEORM_MIGRATIONS_RUN";
875
+ EnvironmentVariableName["MIGRATIONS_TABLE_NAME"] = "DB_MIGRATIONS_TABLE_NAME";
876
+ EnvironmentVariableName["MIGRATIONS_TABLE_NAME_ALT"] = "TYPEORM_MIGRATIONS_TABLE_NAME";
877
+ EnvironmentVariableName["ENTITIES"] = "DB_ENTITIES";
878
+ EnvironmentVariableName["ENTITIES_ALT"] = "TYPEORM_ENTITIES";
879
+ EnvironmentVariableName["ENTITY_PREFIX"] = "DB_ENTITY_PREFIX";
880
+ EnvironmentVariableName["ENTITY_PREFIX_ALT"] = "TYPEORM_ENTITY_PREFIX";
881
+ EnvironmentVariableName["METADATA_TABLE_NAME"] = "DB_METADATA_TABLE_NAME";
882
+ EnvironmentVariableName["METADATA_TABLE_NAME_ALT"] = "TYPEORM_METADATA_TABLE_NAME";
883
+ EnvironmentVariableName["SUBSCRIBERS"] = "DB_SUBSCRIBERS";
884
+ EnvironmentVariableName["SUBSCRIBERS_ALT"] = "TYPEORM_SUBSCRIBERS";
885
+ EnvironmentVariableName["LOGGING"] = "DB_LOGGING";
886
+ EnvironmentVariableName["LOGGING_ALT"] = "TYPEORM_LOGGING";
887
+ EnvironmentVariableName["LOGGER"] = "DB_LOGGER";
888
+ EnvironmentVariableName["LOGGER_ALT"] = "TYPEORM_LOGGER";
889
+ EnvironmentVariableName["MAX_QUERY_EXECUTION_TIME"] = "DB_MAX_QUERY_EXECUTION_TIME";
890
+ EnvironmentVariableName["MAX_QUERY_EXECUTION_TIME_ALT"] = "TYPEORM_MAX_QUERY_EXECUTION_TIME";
891
+ EnvironmentVariableName["DEBUG"] = "DB_DEBUG";
892
+ EnvironmentVariableName["DEBUG_ALT"] = "TYPEORM_DEBUG";
893
+ EnvironmentVariableName["UUID_EXTENSION"] = "DB_UUID_EXTENSION";
894
+ EnvironmentVariableName["UUID_EXTENSION_ALT"] = "TYPEORM_UUID_EXTENSION";
895
+ EnvironmentVariableName["CACHE"] = "DB_CACHE";
896
+ EnvironmentVariableName["CACHE_ALT"] = "TYPEORM_CACHE";
897
+ EnvironmentVariableName["CACHE_ALWAYS_ENABLED"] = "DB_CACHE_ALWAYS_ENABLED";
898
+ EnvironmentVariableName["CACHE_ALWAYS_ENABLED_ALT"] = "TYPEORM_CACHE_ALWAYS_ENABLED";
899
+ EnvironmentVariableName["CACHE_OPTIONS"] = "DB_CACHE_OPTIONS";
900
+ EnvironmentVariableName["CACHE_OPTIONS_ALT"] = "TYPEORM_CACHE_OPTIONS";
901
+ EnvironmentVariableName["CACHE_DURATION"] = "DB_CACHE_DURATION";
902
+ EnvironmentVariableName["CACHE_DURATION_ALT"] = "TYPEORM_CACHE_DURATION";
903
+ return EnvironmentVariableName;
904
+ }({});
905
+
906
+ function transformLogging(input) {
907
+ const value = toBool(input);
908
+ if (typeof value === 'boolean') {
909
+ return value;
910
+ }
911
+ if (input === 'all') {
912
+ return 'all';
913
+ }
914
+ return toArray(input) ?? [];
915
+ }
916
+ function transformCache(input) {
917
+ const value = toBool(input);
918
+ if (typeof value === 'boolean') {
919
+ return value;
920
+ }
921
+ if (input === 'redis' || input === 'ioredis' || input === 'database' || input === 'ioredis/cluster') {
922
+ let options;
923
+ const envCacheOptions = oneOf([
924
+ read(EnvironmentVariableName.CACHE_OPTIONS),
925
+ read(EnvironmentVariableName.CACHE_OPTIONS_ALT)
926
+ ]);
927
+ if (envCacheOptions) {
928
+ options = JSON.parse(envCacheOptions);
929
+ }
930
+ return {
931
+ type: input,
932
+ options,
933
+ alwaysEnabled: oneOf([
934
+ readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED),
935
+ readBool(EnvironmentVariableName.CACHE_ALWAYS_ENABLED_ALT)
936
+ ]),
937
+ duration: oneOf([
938
+ readInt(EnvironmentVariableName.CACHE_DURATION),
939
+ readInt(EnvironmentVariableName.CACHE_DURATION_ALT)
940
+ ])
941
+ };
942
+ }
943
+ return undefined;
944
+ }
945
+
946
+ let instance$1;
947
+ function useEnv(key) {
948
+ if (typeof instance$1 !== 'undefined') {
949
+ if (typeof key === 'string') {
950
+ return instance$1[key];
951
+ }
952
+ return instance$1;
953
+ }
954
+ const output = {
955
+ env: read(EnvironmentVariableName.ENV, EnvironmentName.DEVELOPMENT),
956
+ // Seeder
957
+ seeds: oneOf([
958
+ readArray(EnvironmentVariableName.SEEDS),
959
+ readArray(EnvironmentVariableName.SEEDS_ALT)
960
+ ]) ?? [],
961
+ factories: oneOf([
962
+ readArray(EnvironmentVariableName.FACTORIES),
963
+ readArray(EnvironmentVariableName.FACTORIES_ALT)
964
+ ]) ?? [],
965
+ // Database
966
+ url: oneOf([
967
+ read(EnvironmentVariableName.URL),
968
+ read(EnvironmentVariableName.URL_ALT)
969
+ ]),
970
+ host: oneOf([
971
+ read(EnvironmentVariableName.HOST),
972
+ read(EnvironmentVariableName.HOST_ALT)
973
+ ]),
974
+ port: oneOf([
975
+ readInt(EnvironmentVariableName.PORT),
976
+ readInt(EnvironmentVariableName.PORT_ALT)
977
+ ]),
978
+ username: oneOf([
979
+ read(EnvironmentVariableName.USERNAME),
980
+ read(EnvironmentVariableName.USERNAME_ALT)
981
+ ]),
982
+ password: oneOf([
983
+ read(EnvironmentVariableName.PASSWORD),
984
+ read(EnvironmentVariableName.PASSWORD_ALT)
985
+ ]),
986
+ database: oneOf([
987
+ read(EnvironmentVariableName.DATABASE),
988
+ read(EnvironmentVariableName.DATABASE_ALT)
989
+ ]),
990
+ sid: oneOf([
991
+ read(EnvironmentVariableName.SID),
992
+ read(EnvironmentVariableName.SID_ALT)
993
+ ]),
994
+ schema: oneOf([
995
+ read(EnvironmentVariableName.SCHEMA),
996
+ read(EnvironmentVariableName.SCHEMA_ALT)
997
+ ]),
998
+ extra: oneOf([
999
+ read(EnvironmentVariableName.DRIVER_EXTRA),
1000
+ read(EnvironmentVariableName.DRIVER_EXTRA_ALT)
1001
+ ]),
1002
+ synchronize: oneOf([
1003
+ readBool(EnvironmentVariableName.SYNCHRONIZE),
1004
+ readBool(EnvironmentVariableName.SYNCHRONIZE_ALT)
1005
+ ]),
1006
+ schemaDrop: oneOf([
1007
+ readBool(EnvironmentVariableName.SCHEMA_DROP),
1008
+ readBool(EnvironmentVariableName.SCHEMA_DROP_ALT)
1009
+ ]),
1010
+ migrationsRun: oneOf([
1011
+ readBool(EnvironmentVariableName.MIGRATIONS_RUN),
1012
+ readBool(EnvironmentVariableName.MIGRATIONS_RUN_ALT)
1013
+ ]),
1014
+ entities: oneOf([
1015
+ readArray(EnvironmentVariableName.ENTITIES),
1016
+ readArray(EnvironmentVariableName.ENTITIES_ALT)
1017
+ ]) ?? [],
1018
+ migrations: oneOf([
1019
+ readArray(EnvironmentVariableName.MIGRATIONS),
1020
+ readArray(EnvironmentVariableName.MIGRATIONS_ALT)
1021
+ ]) ?? [],
1022
+ migrationsTableName: oneOf([
1023
+ read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME),
1024
+ read(EnvironmentVariableName.MIGRATIONS_TABLE_NAME_ALT)
1025
+ ]),
1026
+ metadataTableName: oneOf([
1027
+ read(EnvironmentVariableName.METADATA_TABLE_NAME),
1028
+ read(EnvironmentVariableName.METADATA_TABLE_NAME_ALT)
1029
+ ]),
1030
+ subscribers: oneOf([
1031
+ readArray(EnvironmentVariableName.SUBSCRIBERS),
1032
+ readArray(EnvironmentVariableName.SUBSCRIBERS_ALT)
1033
+ ]) ?? [],
1034
+ logging: transformLogging(oneOf([
1035
+ read(EnvironmentVariableName.LOGGING),
1036
+ read(EnvironmentVariableName.LOGGING_ALT)
1037
+ ])),
1038
+ logger: oneOf([
1039
+ read(EnvironmentVariableName.LOGGER),
1040
+ read(EnvironmentVariableName.LOGGER_ALT)
1041
+ ]),
1042
+ entityPrefix: oneOf([
1043
+ read(EnvironmentVariableName.ENTITY_PREFIX),
1044
+ read(EnvironmentVariableName.ENTITY_PREFIX_ALT)
1045
+ ]),
1046
+ maxQueryExecutionTime: oneOf([
1047
+ readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME),
1048
+ readInt(EnvironmentVariableName.MAX_QUERY_EXECUTION_TIME_ALT)
1049
+ ]),
1050
+ debug: oneOf([
1051
+ read(EnvironmentVariableName.DEBUG),
1052
+ read(EnvironmentVariableName.DEBUG_ALT)
1053
+ ]),
1054
+ cache: transformCache(oneOf([
1055
+ read(EnvironmentVariableName.CACHE),
1056
+ read(EnvironmentVariableName.CACHE_ALT)
1057
+ ])),
1058
+ uuidExtension: oneOf([
1059
+ read(EnvironmentVariableName.UUID_EXTENSION),
1060
+ read(EnvironmentVariableName.UUID_EXTENSION_ALT)
1061
+ ])
1062
+ };
1063
+ if (output.extra) {
1064
+ output.extra = JSON.parse(output.extra); // todo: ensure record<string,any> ??
1065
+ }
1066
+ let type;
1067
+ const envType = oneOf([
1068
+ read(EnvironmentVariableName.TYPE),
1069
+ read(EnvironmentVariableName.TYPE_ALT)
1070
+ ]);
1071
+ if (envType) {
1072
+ type = envType;
1073
+ } else {
1074
+ const envURL = oneOf([
1075
+ read(EnvironmentVariableName.URL),
1076
+ read(EnvironmentVariableName.URL_ALT)
1077
+ ]);
1078
+ if (envURL) {
1079
+ [type] = envURL.split('://');
1080
+ }
1081
+ }
1082
+ if (type) {
1083
+ output.type = type; // todo: maybe validation here
1084
+ }
1085
+ instance$1 = output;
1086
+ if (typeof key === 'string') {
1087
+ return output[key];
1088
+ }
1089
+ return instance$1;
1090
+ }
1091
+ function resetEnv() {
1092
+ if (typeof instance$1 !== 'undefined') {
1093
+ instance$1 = undefined;
1094
+ }
1095
+ }
1096
+
1097
+ const merge = createMerger({
1098
+ strategy: (target, key, value)=>{
1099
+ if (typeof target[key] === 'undefined') {
1100
+ target[key] = value;
1101
+ return target;
1102
+ }
1103
+ return undefined;
1104
+ }
1105
+ });
1106
+ function mergeDataSourceOptions(target, source) {
1107
+ if (target.type !== source.type) {
1108
+ return target;
1109
+ }
1110
+ return merge(target, source);
1111
+ }
1112
+
1113
+ function hasEnvDataSourceOptions() {
1114
+ return !!useEnv('type');
1115
+ }
1116
+ /* istanbul ignore next */ function readDataSourceOptionsFromEnv() {
1117
+ if (!hasEnvDataSourceOptions()) {
1118
+ return undefined;
1119
+ }
1120
+ // todo: include seeder options
1121
+ const base = {
1122
+ type: useEnv('type'),
1123
+ entities: useEnv('entities'),
1124
+ subscribers: useEnv('subscribers'),
1125
+ migrations: useEnv('migrations'),
1126
+ migrationsTableName: useEnv('migrationsTableName'),
1127
+ // migrationsTransactionMode: useEnv('migra')
1128
+ metadataTableName: useEnv('metadataTableName'),
1129
+ logging: useEnv('logging'),
1130
+ logger: useEnv('logger'),
1131
+ maxQueryExecutionTime: useEnv('maxQueryExecutionTime'),
1132
+ synchronize: useEnv('synchronize'),
1133
+ migrationsRun: useEnv('migrationsRun'),
1134
+ dropSchema: useEnv('schemaDrop'),
1135
+ entityPrefix: useEnv('entityPrefix'),
1136
+ extra: useEnv('extra'),
1137
+ cache: useEnv('cache')
1138
+ };
1139
+ const credentialOptions = {
1140
+ url: useEnv('url'),
1141
+ host: useEnv('host'),
1142
+ port: useEnv('port'),
1143
+ username: useEnv('username'),
1144
+ password: useEnv('password'),
1145
+ database: useEnv('database')
1146
+ };
1147
+ if (base.type === 'mysql' || base.type === 'mariadb') {
1148
+ return {
1149
+ ...base,
1150
+ ...credentialOptions,
1151
+ type: base.type
1152
+ };
1153
+ }
1154
+ if (base.type === 'postgres') {
1155
+ return {
1156
+ ...base,
1157
+ ...credentialOptions,
1158
+ type: base.type,
1159
+ schema: useEnv('schema'),
1160
+ uuidExtension: useEnv('uuidExtension')
1161
+ };
1162
+ }
1163
+ if (base.type === 'cockroachdb') {
1164
+ return {
1165
+ ...base,
1166
+ ...credentialOptions,
1167
+ type: base.type,
1168
+ schema: useEnv('schema'),
1169
+ timeTravelQueries: true
1170
+ };
1171
+ }
1172
+ if (base.type === 'sqlite') {
1173
+ return {
1174
+ ...base,
1175
+ type: base.type,
1176
+ database: useEnv('database') || 'db.sqlite'
1177
+ };
1178
+ }
1179
+ if (base.type === 'better-sqlite3') {
1180
+ return {
1181
+ ...base,
1182
+ type: base.type,
1183
+ database: useEnv('database') || 'db.sqlite'
1184
+ };
1185
+ }
1186
+ if (base.type === 'mssql') {
1187
+ return {
1188
+ ...base,
1189
+ ...credentialOptions,
1190
+ type: base.type,
1191
+ schema: useEnv('schema')
1192
+ };
1193
+ }
1194
+ if (base.type === 'oracle') {
1195
+ return {
1196
+ ...base,
1197
+ ...credentialOptions,
1198
+ type: base.type,
1199
+ sid: useEnv('sid')
1200
+ };
1201
+ }
1202
+ return {
1203
+ ...base,
1204
+ ...credentialOptions
1205
+ };
1206
+ }
1207
+ function mergeDataSourceOptionsWithEnv(options) {
1208
+ const env = readDataSourceOptionsFromEnv();
1209
+ if (!env) {
1210
+ return options;
1211
+ }
1212
+ return mergeDataSourceOptions(env, options);
1213
+ }
1214
+
1215
+ /**
1216
+ * Build DataSourceOptions from DataSource or from configuration.
1217
+ *
1218
+ * @param context
1219
+ */ async function buildDataSourceOptions(context = {}) {
1220
+ const directory = context.directory || process.cwd();
1221
+ let tsconfig;
1222
+ if (!context.preserveFilePaths) {
1223
+ if (isObject(context.tsconfig)) {
1224
+ tsconfig = context.tsconfig;
1225
+ } else {
1226
+ tsconfig = await readTSConfig(context.tsconfig);
1227
+ }
1228
+ }
1229
+ const dataSource = await findDataSource({
1230
+ directory,
1231
+ fileName: context.dataSourceName,
1232
+ tsconfig
1233
+ });
1234
+ if (dataSource) {
1235
+ if (context.preserveFilePaths) {
1236
+ return mergeDataSourceOptionsWithEnv(dataSource.options);
1237
+ }
1238
+ const options = await adjustFilePaths(dataSource.options, [
1239
+ 'entities',
1240
+ 'migrations',
1241
+ 'subscribers'
1242
+ ], tsconfig);
1243
+ return mergeDataSourceOptionsWithEnv(options);
1244
+ }
1245
+ const options = readDataSourceOptionsFromEnv();
1246
+ if (options) {
1247
+ if (context.preserveFilePaths) {
1248
+ return options;
1249
+ }
1250
+ return adjustFilePaths(options, [
1251
+ 'entities',
1252
+ 'migrations',
1253
+ 'subscribers'
1254
+ ], tsconfig);
1255
+ }
1256
+ throw OptionsError.notFound();
1257
+ }
1258
+
1259
+ const instances$1 = {};
1260
+ const instancePromises = {};
1261
+ function setDataSourceOptions(options, alias) {
1262
+ instances$1[alias || 'default'] = options;
1263
+ }
1264
+ function hasDataSourceOptions(alias) {
1265
+ return Object.prototype.hasOwnProperty.call(instances$1, alias || 'default');
1266
+ }
1267
+ async function useDataSourceOptions(alias) {
1268
+ alias = alias || 'default';
1269
+ if (Object.prototype.hasOwnProperty.call(instances$1, alias)) {
1270
+ return instances$1[alias];
1271
+ }
1272
+ /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(instancePromises, alias)) {
1273
+ instancePromises[alias] = buildDataSourceOptions().catch((e)=>{
1274
+ if (alias) {
1275
+ delete instancePromises[alias];
1276
+ }
1277
+ throw e;
1278
+ });
1279
+ }
1280
+ instances$1[alias] = await instancePromises[alias];
1281
+ return instances$1[alias];
1282
+ }
1283
+
1284
+ const instances = {};
1285
+ const initializePromises = {};
1286
+ const optionsPromises = {};
1287
+ function setDataSource(dataSource, alias) {
1288
+ alias = alias || 'default';
1289
+ instances[alias] = dataSource;
1290
+ }
1291
+ function hasDataSource(alias) {
1292
+ alias = alias || 'default';
1293
+ return Object.prototype.hasOwnProperty.call(instances, alias);
1294
+ }
1295
+ function unsetDataSource(alias) {
1296
+ alias = alias || 'default';
1297
+ if (Object.prototype.hasOwnProperty.call(instances, alias)) {
1298
+ delete instances[alias];
1299
+ }
1300
+ /* istanbul ignore next */ if (Object.prototype.hasOwnProperty.call(optionsPromises, alias)) {
1301
+ delete optionsPromises[alias];
1302
+ }
1303
+ /* istanbul ignore next */ if (Object.prototype.hasOwnProperty.call(initializePromises, alias)) {
1304
+ delete initializePromises[alias];
1305
+ }
1306
+ }
1307
+ async function useDataSource(alias) {
1308
+ alias = alias || 'default';
1309
+ if (Object.prototype.hasOwnProperty.call(instances, alias)) {
1310
+ if (!instances[alias].isInitialized) {
1311
+ /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(initializePromises, alias)) {
1312
+ initializePromises[alias] = instances[alias].initialize().catch((e)=>{
1313
+ if (alias) {
1314
+ delete initializePromises[alias];
1315
+ }
1316
+ throw e;
1317
+ });
1318
+ }
1319
+ await initializePromises[alias];
1320
+ }
1321
+ return instances[alias];
1322
+ }
1323
+ /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(optionsPromises, alias)) {
1324
+ optionsPromises[alias] = useDataSourceOptions(alias).catch((e)=>{
1325
+ if (alias) {
1326
+ delete optionsPromises[alias];
1327
+ }
1328
+ throw e;
1329
+ });
1330
+ }
1331
+ const options = await optionsPromises[alias];
1332
+ const dataSource = new DataSource(options);
1333
+ /* istanbul ignore next */ if (!Object.prototype.hasOwnProperty.call(initializePromises, alias)) {
1334
+ initializePromises[alias] = dataSource.initialize().catch((e)=>{
1335
+ if (alias) {
1336
+ delete initializePromises[alias];
1337
+ }
1338
+ throw e;
1339
+ });
1340
+ }
1341
+ await initializePromises[alias];
1342
+ instances[alias] = dataSource;
1343
+ return dataSource;
1344
+ }
1345
+
1346
+ /**
1347
+ * Check database setup progress.
1348
+ *
1349
+ * @param context
1350
+ */ async function checkDatabase(context = {}) {
1351
+ const result = {
1352
+ exists: true,
1353
+ schema: false,
1354
+ migrationsPending: []
1355
+ };
1356
+ let dataSource;
1357
+ let dataSourceCleanup;
1358
+ if (typeof context.dataSource === 'undefined' && typeof context.options === 'undefined' && hasDataSource(context.alias)) {
1359
+ dataSource = await useDataSource(context.alias);
1360
+ if (dataSource.options.synchronize || dataSource.options.migrationsRun) {
1361
+ dataSource = new DataSource({
1362
+ ...dataSource.options,
1363
+ synchronize: false,
1364
+ migrationsRun: false
1365
+ });
1366
+ dataSourceCleanup = true;
1367
+ } else {
1368
+ dataSourceCleanup = false;
1369
+ }
1370
+ } else {
1371
+ let dataSourceOptions;
1372
+ if (context.options) {
1373
+ dataSourceOptions = context.options;
1374
+ } else {
1375
+ dataSourceOptions = await useDataSourceOptions(context.alias);
1376
+ }
1377
+ dataSource = new DataSource({
1378
+ ...dataSourceOptions,
1379
+ synchronize: false,
1380
+ migrationsRun: false
1381
+ });
1382
+ dataSourceCleanup = context.dataSourceCleanup ?? true;
1383
+ }
1384
+ try {
1385
+ if (!dataSource.isInitialized) {
1386
+ await dataSource.initialize();
1387
+ }
1388
+ } catch (e) {
1389
+ result.exists = false;
1390
+ return result;
1391
+ }
1392
+ const queryRunner = dataSource.createQueryRunner();
1393
+ if (dataSource.migrations && dataSource.migrations.length > 0) {
1394
+ const migrationExecutor = new MigrationExecutor(dataSource, queryRunner);
1395
+ result.migrationsPending = await migrationExecutor.getPendingMigrations();
1396
+ result.schema = result.migrationsPending.length === 0;
1397
+ } else {
1398
+ let schema;
1399
+ if (hasStringProperty(dataSource.driver.options, 'schema')) {
1400
+ schema = dataSource.driver.options.schema;
1401
+ }
1402
+ const migrationsTableName = dataSource.driver.buildTableName(dataSource.options.migrationsTableName || 'migrations', schema, dataSource.driver.database);
1403
+ const migrationsTableExists = await queryRunner.hasTable(migrationsTableName);
1404
+ if (migrationsTableExists) {
1405
+ result.schema = dataSource.entityMetadatas.length === 0;
1406
+ } else {
1407
+ const tableNames = dataSource.entityMetadatas.map((entityMetadata)=>entityMetadata.tablePath);
1408
+ const tables = await queryRunner.getTables(tableNames);
1409
+ if (tables.length === dataSource.entityMetadatas.length) {
1410
+ const { upQueries } = await dataSource.driver.createSchemaBuilder().log();
1411
+ result.schema = upQueries.length === 0;
1412
+ } else {
1413
+ result.schema = false;
1414
+ }
1415
+ }
1416
+ }
1417
+ await queryRunner.release();
1418
+ if (dataSourceCleanup) {
1419
+ await dataSource.destroy();
1420
+ }
1421
+ return result;
1422
+ }
1423
+
1424
+ function getCharsetFromDataSourceOptions(options) {
1425
+ if (hasOwnProperty(options, 'charset') && typeof options.charset === 'string') {
1426
+ return options.charset;
1427
+ }
1428
+ if (typeof options?.extra?.charset === 'string') {
1429
+ return options.extra.charset;
1430
+ }
1431
+ return undefined;
1432
+ }
1433
+
1434
+ function getCharacterSetFromDataSourceOptions(options) {
1435
+ if (hasOwnProperty(options, 'characterSet') && typeof options.characterSet === 'string') {
1436
+ return options.characterSet;
1437
+ }
1438
+ if (typeof options?.extra?.characterSet === 'string') {
1439
+ return options.extra.characterSet;
1440
+ }
1441
+ return undefined;
1442
+ }
1443
+
1444
+ function buildDriverOptions(options) {
1445
+ let driverOptions;
1446
+ switch(options.type){
1447
+ case 'mysql':
1448
+ case 'mariadb':
1449
+ case 'postgres':
1450
+ case 'cockroachdb':
1451
+ case 'mssql':
1452
+ case 'oracle':
1453
+ driverOptions = DriverUtils.buildDriverOptions(options.replication ? options.replication.master : options);
1454
+ break;
1455
+ case 'mongodb':
1456
+ driverOptions = DriverUtils.buildMongoDBDriverOptions(options);
1457
+ break;
1458
+ default:
1459
+ driverOptions = DriverUtils.buildDriverOptions(options);
1460
+ }
1461
+ const charset = getCharsetFromDataSourceOptions(options);
1462
+ const characterSet = getCharacterSetFromDataSourceOptions(options);
1463
+ return {
1464
+ host: driverOptions.host,
1465
+ user: driverOptions.user || driverOptions.username,
1466
+ password: driverOptions.password,
1467
+ database: driverOptions.database,
1468
+ port: driverOptions.port,
1469
+ ...charset ? {
1470
+ charset
1471
+ } : {},
1472
+ ...characterSet ? {
1473
+ characterSet
1474
+ } : {},
1475
+ ...driverOptions.ssl ? {
1476
+ ssl: driverOptions.ssl
1477
+ } : {},
1478
+ ...driverOptions.url ? {
1479
+ url: driverOptions.url
1480
+ } : {},
1481
+ ...driverOptions.connectString ? {
1482
+ connectString: driverOptions.connectString
1483
+ } : {},
1484
+ ...driverOptions.sid ? {
1485
+ sid: driverOptions.sid
1486
+ } : {},
1487
+ ...driverOptions.serviceName ? {
1488
+ serviceName: driverOptions.serviceName
1489
+ } : {},
1490
+ ...driverOptions.template ? {
1491
+ template: driverOptions.template
1492
+ } : {},
1493
+ ...options.extra ? {
1494
+ extra: options.extra
1495
+ } : {},
1496
+ ...driverOptions.domain ? {
1497
+ domain: driverOptions.domain
1498
+ } : {},
1499
+ ...driverOptions.schema ? {
1500
+ schema: driverOptions.schema
1501
+ } : {}
1502
+ };
1503
+ }
1504
+
1505
+ const driversRequireDatabaseOption = [
1506
+ 'sqlite',
1507
+ 'better-sqlite3'
1508
+ ];
1509
+ function createDriver(connectionOptions) {
1510
+ const fakeConnection = {
1511
+ options: {
1512
+ type: connectionOptions.type,
1513
+ ...driversRequireDatabaseOption.indexOf(connectionOptions.type) !== -1 ? {
1514
+ database: connectionOptions.database
1515
+ } : {}
1516
+ }
1517
+ };
1518
+ const driverFactory = new DriverFactory();
1519
+ return driverFactory.create(fakeConnection);
1520
+ }
1521
+
1522
+ async function normalizeDataSourceOptions(context) {
1523
+ let options;
1524
+ if (context.options) {
1525
+ options = {
1526
+ ...context.options
1527
+ };
1528
+ } else {
1529
+ const dataSource = await findDataSource(context.findOptions);
1530
+ if (dataSource) {
1531
+ options = {
1532
+ ...dataSource.options
1533
+ };
1534
+ }
1535
+ if (!options) {
1536
+ options = {
1537
+ ...await buildDataSourceOptions()
1538
+ };
1539
+ }
1540
+ }
1541
+ return {
1542
+ ...options,
1543
+ subscribers: [],
1544
+ synchronize: false,
1545
+ migrationsRun: false,
1546
+ dropSchema: false
1547
+ };
1548
+ }
1549
+ async function buildDatabaseCreateContext(input = {}) {
1550
+ return {
1551
+ ...input,
1552
+ options: await normalizeDataSourceOptions(input),
1553
+ synchronize: input.synchronize ?? true,
1554
+ ifNotExist: input.ifNotExist ?? true
1555
+ };
1556
+ }
1557
+ async function buildDatabaseDropContext(input = {}) {
1558
+ return {
1559
+ ...input,
1560
+ options: await normalizeDataSourceOptions(input),
1561
+ ifExist: input.ifExist ?? true
1562
+ };
1563
+ }
1564
+
1565
+ class GenerateCommand extends MigrationGenerateCommand {
1566
+ static prettify(query) {
1567
+ return this.prettifyQuery(query);
1568
+ }
1569
+ }
1570
+ function queryParams(parameters) {
1571
+ if (!parameters || !parameters.length) {
1572
+ return '';
1573
+ }
1574
+ return `, ${JSON.stringify(parameters)}`;
1575
+ }
1576
+ function buildTemplate(name, timestamp, upStatements, downStatements) {
1577
+ const migrationName = `${pascalCase(name)}${timestamp}`;
1578
+ const up = upStatements.map((statement)=>` ${statement}`);
1579
+ const down = downStatements.map((statement)=>` ${statement}`);
1580
+ return `import type { MigrationInterface, QueryRunner } from 'typeorm';
1581
+
1582
+ export class ${migrationName} implements MigrationInterface {
1583
+ name = '${migrationName}';
1584
+
1585
+ public async up(queryRunner: QueryRunner): Promise<void> {
1586
+ ${up.join(`
1587
+ `)}
1588
+ }
1589
+ public async down(queryRunner: QueryRunner): Promise<void> {
1590
+ ${down.join(`
1591
+ `)}
1592
+ }
1593
+ }
1594
+ `;
1595
+ }
1596
+ async function generateMigration(context) {
1597
+ context.name = context.name || 'Default';
1598
+ const timestamp = context.timestamp || new Date().getTime();
1599
+ const fileName = `${timestamp}-${context.name}.ts`;
1600
+ const { dataSource } = context;
1601
+ const up = [];
1602
+ const down = [];
1603
+ const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
1604
+ if (context.prettify) {
1605
+ sqlInMemory.upQueries.forEach((upQuery)=>{
1606
+ upQuery.query = GenerateCommand.prettify(upQuery.query);
1607
+ });
1608
+ sqlInMemory.downQueries.forEach((downQuery)=>{
1609
+ downQuery.query = GenerateCommand.prettify(downQuery.query);
1610
+ });
1611
+ }
1612
+ sqlInMemory.upQueries.forEach((upQuery)=>{
1613
+ up.push(`await queryRunner.query(\`${upQuery.query.replace(/`/g, '\\`')}\`${queryParams(upQuery.parameters)});`);
1614
+ });
1615
+ sqlInMemory.downQueries.forEach((downQuery)=>{
1616
+ down.push(`await queryRunner.query(\`${downQuery.query.replace(/`/g, '\\`')}\`${queryParams(downQuery.parameters)});`);
1617
+ });
1618
+ if (up.length === 0 && down.length === 0) {
1619
+ return {
1620
+ up,
1621
+ down
1622
+ };
1623
+ }
1624
+ const content = buildTemplate(context.name, timestamp, up, down.reverse());
1625
+ if (!context.preview) {
1626
+ let directoryPath;
1627
+ if (context.directoryPath) {
1628
+ if (!path.isAbsolute(context.directoryPath)) {
1629
+ directoryPath = path.join(process$1.cwd(), context.directoryPath);
1630
+ } else {
1631
+ directoryPath = context.directoryPath;
1632
+ }
1633
+ } else {
1634
+ directoryPath = path.join(process$1.cwd(), 'migrations');
1635
+ }
1636
+ try {
1637
+ await fs.promises.access(directoryPath, fs.constants.R_OK | fs.constants.W_OK);
1638
+ } catch (e) {
1639
+ await fs.promises.mkdir(directoryPath, {
1640
+ recursive: true
1641
+ });
1642
+ }
1643
+ await fs.promises.writeFile(path.join(directoryPath, fileName), content, {
1644
+ encoding: 'utf-8'
1645
+ });
1646
+ }
1647
+ return {
1648
+ up,
1649
+ down,
1650
+ content
1651
+ };
1652
+ }
1653
+
1654
+ /* istanbul ignore next */ function existsQuery(builder, inverse = false) {
1655
+ return `${inverse ? 'not ' : ''}exists (${builder.getQuery()})`;
1656
+ }
1657
+
1658
+ async function synchronizeDatabaseSchema(input) {
1659
+ let dataSource;
1660
+ let options;
1661
+ if (InstanceChecker.isDataSource(input)) {
1662
+ dataSource = input;
1663
+ options = dataSource.options;
1664
+ } else {
1665
+ options = input;
1666
+ dataSource = new DataSource(options);
1667
+ }
1668
+ if (!dataSource.isInitialized) {
1669
+ await dataSource.initialize();
1670
+ }
1671
+ let migrationsCount = 0;
1672
+ if (options.migrations) {
1673
+ migrationsCount = Array.isArray(options.migrations) ? options.migrations.length : Object.keys(options.migrations).length;
1674
+ }
1675
+ let migrations = [];
1676
+ if (migrationsCount > 0) {
1677
+ migrations = await dataSource.runMigrations({
1678
+ transaction: options.migrationsTransactionMode
1679
+ });
1680
+ } else {
1681
+ await dataSource.synchronize(false);
1682
+ }
1683
+ if (!InstanceChecker.isDataSource(input)) {
1684
+ await dataSource.destroy();
1685
+ }
1686
+ return migrations;
1687
+ }
1688
+
1689
+ async function createSimplePostgresConnection(driver, options, operationContext) {
1690
+ /**
1691
+ * pg library
1692
+ */ const { Client } = driver.postgres;
1693
+ const data = {
1694
+ host: options.host,
1695
+ port: options.port,
1696
+ user: options.user,
1697
+ password: options.password,
1698
+ ssl: options.ssl,
1699
+ schema: options.schema,
1700
+ ...options.extra ? options.extra : {}
1701
+ };
1702
+ if (typeof operationContext.initialDatabase === 'string') {
1703
+ data.database = operationContext.initialDatabase;
1704
+ }
1705
+ const client = new Client(data);
1706
+ await client.connect();
1707
+ return client;
1708
+ }
1709
+ async function executeSimplePostgresQuery(connection, query, endConnection = true) {
1710
+ return new Promise((resolve, reject)=>{
1711
+ connection.query(query, (queryErr, queryResult)=>{
1712
+ if (endConnection) {
1713
+ connection.end();
1714
+ }
1715
+ if (queryErr) {
1716
+ reject(queryErr);
1717
+ }
1718
+ resolve(queryResult);
1719
+ });
1720
+ });
1721
+ }
1722
+ async function createPostgresDatabase(input = {}) {
1723
+ const context = await buildDatabaseCreateContext(input);
1724
+ const options = buildDriverOptions(context.options);
1725
+ const driver = createDriver(context.options);
1726
+ const connection = await createSimplePostgresConnection(driver, options, context);
1727
+ if (context.ifNotExist) {
1728
+ const existQuery = `SELECT * FROM pg_database WHERE lower(datname) = lower('${options.database}');`;
1729
+ const existResult = await executeSimplePostgresQuery(connection, existQuery, false);
1730
+ if (isObject(existResult) && hasOwnProperty(existResult, 'rows') && Array.isArray(existResult.rows) && existResult.rows.length > 0) {
1731
+ await connection.end();
1732
+ return Promise.resolve();
1733
+ }
1734
+ }
1735
+ /**
1736
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/postgres/PostgresQueryRunner.ts#L326
1737
+ */ let query = `CREATE DATABASE "${options.database}"`;
1738
+ if (typeof options.template === 'string') {
1739
+ query += ` TEMPLATE "${options.template}"`;
1740
+ } else if (typeof options.characterSet === 'string') {
1741
+ query += ` WITH ENCODING '${options.characterSet}'`;
1742
+ }
1743
+ const result = await executeSimplePostgresQuery(connection, query);
1744
+ if (typeof options.schema === 'string' && options.schema !== 'public') {
1745
+ const schemaConnection = await createSimplePostgresConnection(driver, options, {
1746
+ ...context,
1747
+ initialDatabase: options.database
1748
+ });
1749
+ const schemaQuery = `CREATE SCHEMA IF NOT EXISTS "${options.schema}"`;
1750
+ await executeSimplePostgresQuery(schemaConnection, schemaQuery);
1751
+ }
1752
+ if (context.synchronize) {
1753
+ await synchronizeDatabaseSchema(context.options);
1754
+ }
1755
+ return result;
1756
+ }
1757
+ async function dropPostgresDatabase(input = {}) {
1758
+ const context = await buildDatabaseDropContext(input);
1759
+ const options = buildDriverOptions(context.options);
1760
+ const driver = createDriver(context.options);
1761
+ const connection = await createSimplePostgresConnection(driver, options, context);
1762
+ /**
1763
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/postgres/PostgresQueryRunner.ts#L343
1764
+ */ const query = context.ifExist ? `DROP DATABASE IF EXISTS "${options.database}"` : `DROP DATABASE "${options.database}"`;
1765
+ return executeSimplePostgresQuery(connection, query);
1766
+ }
1767
+
1768
+ async function executeSimpleCockroachDBQuery(connection, query, endConnection = true) {
1769
+ return new Promise((resolve, reject)=>{
1770
+ connection.query(query, (queryErr, queryResult)=>{
1771
+ if (endConnection) {
1772
+ connection.end();
1773
+ }
1774
+ if (queryErr) {
1775
+ reject(queryErr);
1776
+ }
1777
+ resolve(queryResult);
1778
+ });
1779
+ });
1780
+ }
1781
+ async function createCockroachDBDatabase(input = {}) {
1782
+ const context = await buildDatabaseCreateContext(input);
1783
+ const options = buildDriverOptions(context.options);
1784
+ const driver = createDriver(context.options);
1785
+ const connection = await createSimplePostgresConnection(driver, options, context);
1786
+ /**
1787
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/cockroachdb/CockroachQueryRunner.ts#L347
1788
+ */ const query = `CREATE DATABASE ${context.ifNotExist ? 'IF NOT EXISTS ' : ''} "${options.database}"`;
1789
+ const result = await executeSimpleCockroachDBQuery(connection, query);
1790
+ if (context.synchronize) {
1791
+ await synchronizeDatabaseSchema(context.options);
1792
+ }
1793
+ return result;
1794
+ }
1795
+ async function dropCockroachDBDatabase(input = {}) {
1796
+ const context = await buildDatabaseDropContext(input);
1797
+ const options = buildDriverOptions(context.options);
1798
+ const driver = createDriver(context.options);
1799
+ const connection = await createSimplePostgresConnection(driver, options, context);
1800
+ /**
1801
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/cockroachdb/CockroachQueryRunner.ts#L356
1802
+ */ const query = `DROP DATABASE ${context.ifExist ? 'IF EXISTS ' : ''} "${options.database}"`;
1803
+ return executeSimpleCockroachDBQuery(connection, query);
1804
+ }
1805
+
1806
+ async function createSimpleMongoDBConnection(driver, options) {
1807
+ /**
1808
+ * mongodb library
1809
+ */ const { MongoClient } = driver.mongodb;
1810
+ let url = 'mongodb://';
1811
+ if (options.user && options.password) {
1812
+ url += `${options.user}:${options.password}@`;
1813
+ }
1814
+ url += `${options.host || '127.0.0.1'}:${options.port || 27017}/${options.database}`;
1815
+ if (options.ssl) {
1816
+ url += '?tls=true';
1817
+ }
1818
+ const client = new MongoClient(url);
1819
+ await client.connect();
1820
+ return client;
1821
+ }
1822
+ async function createMongoDBDatabase(input = {}) {
1823
+ const context = await buildDatabaseCreateContext(input);
1824
+ const options = buildDriverOptions(context.options);
1825
+ const driver = createDriver(context.options);
1826
+ // connection setup, will create the database on the fly.
1827
+ const client = await createSimpleMongoDBConnection(driver, options);
1828
+ await client.close();
1829
+ if (context.synchronize) {
1830
+ await synchronizeDatabaseSchema(context.options);
1831
+ }
1832
+ }
1833
+ async function dropMongoDBDatabase(input = {}) {
1834
+ const context = await buildDatabaseDropContext(input);
1835
+ const options = buildDriverOptions(context.options);
1836
+ const driver = createDriver(context.options);
1837
+ const client = await createSimpleMongoDBConnection(driver, options);
1838
+ const result = await client.dropDatabase();
1839
+ await client.close();
1840
+ return result;
1841
+ }
1842
+
1843
+ async function createSimpleMsSQLConnection(driver, options) {
1844
+ const option = {
1845
+ user: options.user,
1846
+ password: options.password,
1847
+ server: options.host,
1848
+ port: options.port || 1433,
1849
+ ...options.extra ? options.extra : {},
1850
+ ...options.domain ? {
1851
+ domain: options.domain
1852
+ } : {}
1853
+ };
1854
+ await driver.mssql.connect(option);
1855
+ return driver.mssql;
1856
+ }
1857
+ async function createMsSQLDatabase(input = {}) {
1858
+ const context = await buildDatabaseCreateContext(input);
1859
+ const options = buildDriverOptions(context.options);
1860
+ const driver = createDriver(context.options);
1861
+ const connection = await createSimpleMsSQLConnection(driver, options);
1862
+ /**
1863
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/sqlserver/SqlServerQueryRunner.ts#L416
1864
+ */ let query = context.ifNotExist ? `IF DB_ID('${options.database}') IS NULL CREATE DATABASE "${options.database}"` : `CREATE DATABASE "${options.database}"`;
1865
+ if (typeof options.characterSet === 'string') {
1866
+ query += ` CHARACTER SET ${options.characterSet}`;
1867
+ }
1868
+ const result = await connection.query(query);
1869
+ if (context.synchronize) {
1870
+ await synchronizeDatabaseSchema(context.options);
1871
+ }
1872
+ return result;
1873
+ }
1874
+ async function dropMsSQLDatabase(input = {}) {
1875
+ const context = await buildDatabaseDropContext(input);
1876
+ const options = buildDriverOptions(context.options);
1877
+ const driver = createDriver(context.options);
1878
+ const connection = await createSimpleMsSQLConnection(driver, options);
1879
+ /**
1880
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/sqlserver/SqlServerQueryRunner.ts#L425
1881
+ */ const query = context.ifExist ? `IF DB_ID('${options.database}') IS NOT NULL DROP DATABASE "${options.database}"` : `DROP DATABASE "${options.database}"`;
1882
+ return connection.query(query);
1883
+ }
1884
+
1885
+ async function createSimpleMySQLConnection(driver, options) {
1886
+ /**
1887
+ * mysql|mysql2 library
1888
+ */ const { createConnection } = driver.mysql;
1889
+ const option = {
1890
+ host: options.host,
1891
+ user: options.user,
1892
+ password: options.password,
1893
+ port: options.port,
1894
+ ssl: options.ssl,
1895
+ ...options.extra ? options.extra : {}
1896
+ };
1897
+ return createConnection(option);
1898
+ }
1899
+ async function executeSimpleMysqlQuery(connection, query, endConnection = true) {
1900
+ return new Promise((resolve, reject)=>{
1901
+ connection.query(query, (queryErr, queryResult)=>{
1902
+ if (endConnection) connection.end();
1903
+ if (queryErr) {
1904
+ reject(queryErr);
1905
+ }
1906
+ resolve(queryResult);
1907
+ });
1908
+ });
1909
+ }
1910
+ async function createMySQLDatabase(input = {}) {
1911
+ const context = await buildDatabaseCreateContext(input);
1912
+ const options = buildDriverOptions(context.options);
1913
+ const driver = createDriver(context.options);
1914
+ const connection = await createSimpleMySQLConnection(driver, options);
1915
+ /**
1916
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlQueryRunner.ts#L297
1917
+ */ let query = context.ifNotExist ? `CREATE DATABASE IF NOT EXISTS \`${options.database}\`` : `CREATE DATABASE \`${options.database}\``;
1918
+ if (typeof options.charset === 'string') {
1919
+ const { charset } = options;
1920
+ let { characterSet } = options;
1921
+ if (typeof characterSet === 'undefined') {
1922
+ if (charset.toLowerCase().startsWith('utf8mb4')) {
1923
+ characterSet = 'utf8mb4';
1924
+ } else if (charset.toLowerCase().startsWith('utf8')) {
1925
+ characterSet = 'utf8';
1926
+ }
1927
+ }
1928
+ if (typeof characterSet === 'string') {
1929
+ query += ` CHARACTER SET ${characterSet} COLLATE ${charset}`;
1930
+ }
1931
+ }
1932
+ const result = await executeSimpleMysqlQuery(connection, query);
1933
+ if (context.synchronize) {
1934
+ await synchronizeDatabaseSchema(context.options);
1935
+ }
1936
+ return result;
1937
+ }
1938
+ async function dropMySQLDatabase(input = {}) {
1939
+ const context = await buildDatabaseDropContext(input);
1940
+ const options = buildDriverOptions(context.options);
1941
+ const driver = createDriver(context.options);
1942
+ const connection = await createSimpleMySQLConnection(driver, options);
1943
+ /**
1944
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/mysql/MysqlQueryRunner.ts#L306
1945
+ */ const query = context.ifExist ? `DROP DATABASE IF EXISTS \`${options.database}\`` : `DROP DATABASE \`${options.database}\``;
1946
+ await executeSimpleMysqlQuery(connection, 'SET FOREIGN_KEY_CHECKS=0;', false);
1947
+ const result = await executeSimpleMysqlQuery(connection, query, false);
1948
+ await executeSimpleMysqlQuery(connection, 'SET FOREIGN_KEY_CHECKS=1;');
1949
+ return result;
1950
+ }
1951
+
1952
+ function createSimpleOracleConnection(driver, options) {
1953
+ const { getConnection } = driver.oracle;
1954
+ if (!options.connectString) {
1955
+ let address = '(PROTOCOL=TCP)';
1956
+ if (options.host) {
1957
+ address += `(HOST=${options.host})`;
1958
+ }
1959
+ if (options.port) {
1960
+ address += `(PORT=${options.port})`;
1961
+ }
1962
+ let connectData = '(SERVER=DEDICATED)';
1963
+ if (options.sid) {
1964
+ connectData += `(SID=${options.sid})`;
1965
+ }
1966
+ if (options.serviceName) {
1967
+ connectData += `(SERVICE_NAME=${options.serviceName})`;
1968
+ }
1969
+ options.connectString = `(DESCRIPTION=(ADDRESS=${address})(CONNECT_DATA=${connectData}))`;
1970
+ }
1971
+ return getConnection({
1972
+ user: options.user,
1973
+ password: options.password,
1974
+ connectString: options.connectString || options.url,
1975
+ ...options.extra ? options.extra : {}
1976
+ });
1977
+ }
1978
+ async function createOracleDatabase(input = {}) {
1979
+ const context = await buildDatabaseCreateContext(input);
1980
+ const options = buildDriverOptions(context.options);
1981
+ const driver = createDriver(context.options);
1982
+ const connection = createSimpleOracleConnection(driver, options);
1983
+ /**
1984
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/oracle/OracleQueryRunner.ts#L295
1985
+ */ const query = `CREATE DATABASE IF NOT EXISTS ${options.database}`;
1986
+ const result = await connection.execute(query);
1987
+ if (context.synchronize) {
1988
+ await synchronizeDatabaseSchema(context.options);
1989
+ }
1990
+ return result;
1991
+ }
1992
+ async function dropOracleDatabase(_context = {}) {
1993
+ /**
1994
+ * @link https://github.com/typeorm/typeorm/blob/master/src/driver/oracle/OracleQueryRunner.ts#L295
1995
+ */ return Promise.resolve();
1996
+ }
1997
+
1998
+ async function createSQLiteDatabase(input = {}) {
1999
+ const context = await buildDatabaseCreateContext(input);
2000
+ const options = buildDriverOptions(context.options);
2001
+ if (!options.database) {
2002
+ throw OptionsError.databaseNotDefined();
2003
+ }
2004
+ const filePath = path.isAbsolute(options.database) ? options.database : path.join(process.cwd(), options.database);
2005
+ const directoryPath = path.dirname(filePath);
2006
+ await fs.promises.access(directoryPath, fs.constants.W_OK);
2007
+ if (context.synchronize) {
2008
+ await synchronizeDatabaseSchema(context.options);
2009
+ }
2010
+ }
2011
+ async function dropSQLiteDatabase(input = {}) {
2012
+ const context = await buildDatabaseDropContext(input);
2013
+ const options = buildDriverOptions(context.options);
2014
+ if (!options.database) {
2015
+ throw OptionsError.databaseNotDefined();
2016
+ }
2017
+ const filePath = path.isAbsolute(options.database) ? options.database : path.join(process.cwd(), options.database);
2018
+ try {
2019
+ await fs.promises.access(filePath, fs.constants.F_OK | fs.constants.W_OK);
2020
+ if (context.ifExist) {
2021
+ await fs.promises.unlink(filePath);
2022
+ }
2023
+ } catch (e) {
2024
+ // ...
2025
+ }
2026
+ }
2027
+
2028
+ /**
2029
+ * Create database for specified driver in ConnectionOptions.
2030
+ *
2031
+ * @throws DriverError
2032
+ * @throws OptionsError
2033
+ *
2034
+ * @param input
2035
+ */ async function createDatabase(input = {}) {
2036
+ const context = await buildDatabaseCreateContext(input);
2037
+ let output;
2038
+ switch(context.options.type){
2039
+ case 'mongodb':
2040
+ output = await createMongoDBDatabase(context);
2041
+ break;
2042
+ case 'mysql':
2043
+ case 'mariadb':
2044
+ output = await createMySQLDatabase(context);
2045
+ break;
2046
+ case 'postgres':
2047
+ output = await createPostgresDatabase(context);
2048
+ break;
2049
+ case 'cockroachdb':
2050
+ output = await createCockroachDBDatabase(context);
2051
+ break;
2052
+ case 'sqlite':
2053
+ case 'better-sqlite3':
2054
+ output = await createSQLiteDatabase(context);
2055
+ break;
2056
+ case 'oracle':
2057
+ output = await createOracleDatabase(context);
2058
+ break;
2059
+ case 'mssql':
2060
+ output = await createMsSQLDatabase(context);
2061
+ break;
2062
+ default:
2063
+ throw DriverError.notSupported(context.options.type);
2064
+ }
2065
+ return output;
2066
+ }
2067
+
2068
+ /**
2069
+ * Drop database for specified driver in ConnectionOptions.
2070
+ *
2071
+ * @throws DriverError
2072
+ * @throws OptionsError
2073
+ *
2074
+ * @param input
2075
+ */ async function dropDatabase(input = {}) {
2076
+ const context = await buildDatabaseDropContext(input);
2077
+ let output;
2078
+ switch(context.options.type){
2079
+ case 'mongodb':
2080
+ output = await dropMongoDBDatabase(context);
2081
+ break;
2082
+ case 'mysql':
2083
+ case 'mariadb':
2084
+ output = await dropMySQLDatabase(context);
2085
+ break;
2086
+ case 'postgres':
2087
+ output = await dropPostgresDatabase(context);
2088
+ break;
2089
+ case 'cockroachdb':
2090
+ output = await dropCockroachDBDatabase(context);
2091
+ break;
2092
+ case 'sqlite':
2093
+ case 'better-sqlite3':
2094
+ output = await dropSQLiteDatabase(context);
2095
+ break;
2096
+ case 'oracle':
2097
+ output = await dropOracleDatabase(context);
2098
+ break;
2099
+ case 'mssql':
2100
+ output = await dropMsSQLDatabase(context);
2101
+ break;
2102
+ default:
2103
+ throw DriverError.notSupported(context.options.type);
2104
+ }
2105
+ return output;
2106
+ }
2107
+
2108
+ class DatabaseCreateCommand {
2109
+ builder(args) {
2110
+ return args.option('preserveFilePaths', {
2111
+ default: false,
2112
+ type: 'boolean',
2113
+ describe: 'This option indicates if file paths should be preserved.'
2114
+ }).option('root', {
2115
+ alias: 'r',
2116
+ default: process.cwd(),
2117
+ describe: 'Root directory of the project.'
2118
+ }).option('tsconfig', {
2119
+ alias: 'tc',
2120
+ default: 'tsconfig.json',
2121
+ describe: 'Name (or relative path incl. name) of the tsconfig file.'
2122
+ }).option('dataSource', {
2123
+ alias: 'd',
2124
+ default: 'data-source',
2125
+ describe: 'Name (or relative path incl. name) of the data-source file.'
2126
+ }).option('synchronize', {
2127
+ alias: 's',
2128
+ default: 'yes',
2129
+ describe: 'Create database schema for all entities.',
2130
+ choices: [
2131
+ 'yes',
2132
+ 'no'
2133
+ ]
2134
+ }).option('initialDatabase', {
2135
+ describe: 'Specify the initial database to connect to.'
2136
+ });
2137
+ }
2138
+ async handler(raw) {
2139
+ const args = raw;
2140
+ let tsconfig;
2141
+ let sourcePath = resolveFilePath(args.dataSource, args.root);
2142
+ if (!args.preserveFilePaths) {
2143
+ tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig));
2144
+ sourcePath = await adjustFilePath(sourcePath, tsconfig);
2145
+ }
2146
+ const source = parseFilePath(sourcePath);
2147
+ consola.info(`DataSource Directory: ${source.directory}`);
2148
+ consola.info(`DataSource Name: ${source.name}`);
2149
+ const dataSourceOptions = await buildDataSourceOptions({
2150
+ directory: source.directory,
2151
+ dataSourceName: source.name,
2152
+ tsconfig,
2153
+ preserveFilePaths: args.preserveFilePaths
2154
+ });
2155
+ const context = {
2156
+ ifNotExist: true,
2157
+ options: dataSourceOptions,
2158
+ synchronize: args.synchronize === 'yes'
2159
+ };
2160
+ if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') {
2161
+ context.initialDatabase = args.initialDatabase;
2162
+ }
2163
+ try {
2164
+ await createDatabase(context);
2165
+ consola.success('Created database.');
2166
+ process.exit(0);
2167
+ } catch (e) {
2168
+ consola.warn('Failed to create database.');
2169
+ consola.error(e);
2170
+ process.exit(1);
2171
+ }
2172
+ }
2173
+ constructor(){
2174
+ this.command = 'db:create';
2175
+ this.describe = 'Create database.';
2176
+ }
2177
+ }
2178
+
2179
+ class DatabaseDropCommand {
2180
+ builder(args) {
2181
+ return args.option('preserveFilePaths', {
2182
+ default: false,
2183
+ type: 'boolean',
2184
+ describe: 'This option indicates if file paths should be preserved.'
2185
+ }).option('root', {
2186
+ alias: 'r',
2187
+ default: process.cwd(),
2188
+ describe: 'Root directory of the project.'
2189
+ }).option('tsconfig', {
2190
+ alias: 'tc',
2191
+ default: 'tsconfig.json',
2192
+ describe: 'Name (or relative path incl. name) of the tsconfig file.'
2193
+ }).option('dataSource', {
2194
+ alias: 'd',
2195
+ default: 'data-source',
2196
+ describe: 'Name (or relative path incl. name) of the data-source file.'
2197
+ }).option('initialDatabase', {
2198
+ describe: 'Specify the initial database to connect to.'
2199
+ });
2200
+ }
2201
+ async handler(raw) {
2202
+ const args = raw;
2203
+ let tsconfig;
2204
+ let sourcePath = resolveFilePath(args.dataSource, args.root);
2205
+ if (!args.preserveFilePaths) {
2206
+ tsconfig = await readTSConfig(resolveFilePath(args.root, args.tsconfig));
2207
+ sourcePath = await adjustFilePath(sourcePath, tsconfig);
2208
+ }
2209
+ const source = parseFilePath(sourcePath);
2210
+ consola.info(`DataSource Directory: ${source.directory}`);
2211
+ consola.info(`DataSource Name: ${source.name}`);
2212
+ const dataSourceOptions = await buildDataSourceOptions({
2213
+ directory: source.directory,
2214
+ dataSourceName: source.name,
2215
+ tsconfig,
2216
+ preserveFilePaths: args.preserveFilePaths
2217
+ });
2218
+ const context = {
2219
+ ifExist: true,
2220
+ options: dataSourceOptions
2221
+ };
2222
+ if (typeof args.initialDatabase === 'string' && args.initialDatabase !== '') {
2223
+ context.initialDatabase = args.initialDatabase;
2224
+ }
2225
+ try {
2226
+ await dropDatabase(context);
2227
+ consola.success('Dropped database.');
2228
+ process.exit(0);
2229
+ } catch (e) {
2230
+ consola.warn('Failed to drop database.');
2231
+ consola.error(e);
2232
+ process.exit(1);
2233
+ }
2234
+ }
2235
+ constructor(){
2236
+ this.command = 'db:drop';
2237
+ this.describe = 'Drop database.';
2238
+ }
2239
+ }
2240
+
2241
+ class SeederEntity {
2242
+ constructor(ctx){
2243
+ this.id = ctx.id;
2244
+ this.timestamp = ctx.timestamp;
2245
+ this.name = ctx.name;
2246
+ if (ctx.constructor) {
2247
+ this.instance = new ctx.constructor();
2248
+ }
2249
+ this.fileName = ctx.fileName;
2250
+ this.filePath = ctx.filePath;
2251
+ }
2252
+ get trackExecution() {
2253
+ if (typeof this.instance === 'undefined') {
2254
+ return undefined;
2255
+ }
2256
+ return this.instance.track;
2257
+ }
2258
+ }
2259
+
2260
+ class SeederFactory {
2261
+ // --------------------------------------------------------------
2262
+ constructor(context){
2263
+ this.context = context;
2264
+ }
2265
+ // --------------------------------------------------------------
2266
+ setMeta(value) {
2267
+ this.meta = value;
2268
+ return this;
2269
+ }
2270
+ setLocale(value) {
2271
+ this.faker = undefined;
2272
+ this.locale = Array.isArray(value) ? value : [
2273
+ value
2274
+ ];
2275
+ }
2276
+ // --------------------------------------------------------------
2277
+ async make(params, save) {
2278
+ const faker = await this.useFaker();
2279
+ const factoryFn = this.context.factoryFn(faker, this.meta);
2280
+ let entity;
2281
+ if (isPromise(factoryFn)) {
2282
+ entity = await this.resolve(await factoryFn, save);
2283
+ } else {
2284
+ entity = await this.resolve(factoryFn, save);
2285
+ }
2286
+ if (params) {
2287
+ const keys = Object.keys(params);
2288
+ for(let i = 0; i < keys.length; i++){
2289
+ entity[keys[i]] = params[keys[i]];
2290
+ }
2291
+ }
2292
+ return entity;
2293
+ }
2294
+ // --------------------------------------------------------------
2295
+ async save(params, options) {
2296
+ const dataSource = await useDataSource();
2297
+ const entity = await this.make(params, true);
2298
+ const entityManager = dataSource.getRepository(this.context.entity);
2299
+ return entityManager.save(entity, options);
2300
+ }
2301
+ async saveMany(amount, params, options) {
2302
+ const promises = [];
2303
+ for(let i = 0; i < amount; i++){
2304
+ const item = this.save(params, options);
2305
+ promises.push(item);
2306
+ }
2307
+ return Promise.all(promises);
2308
+ }
2309
+ // --------------------------------------------------------------
2310
+ async resolve(entity, save) {
2311
+ const keys = Object.keys(entity);
2312
+ for(let i = 0; i < keys.length; i++){
2313
+ const key = keys[i];
2314
+ const value = entity[key];
2315
+ if (!hasOwnProperty(entity, key)) {
2316
+ continue;
2317
+ }
2318
+ if (typeof value === 'object' && // eslint-disable-next-line @typescript-eslint/ban-ts-comment
2319
+ // @ts-ignore
2320
+ value instanceof SeederFactory) {
2321
+ if (save) {
2322
+ entity[key] = await value.save();
2323
+ } else {
2324
+ entity[key] = await value.make();
2325
+ }
2326
+ }
2327
+ if (value && hasOwnProperty(value, 'then') && typeof value.then === 'function') {
2328
+ entity[key] = await value;
2329
+ }
2330
+ }
2331
+ return entity;
2332
+ }
2333
+ async useFaker() {
2334
+ if (typeof this.faker !== 'undefined') {
2335
+ return this.faker;
2336
+ }
2337
+ const options = {
2338
+ locale: []
2339
+ };
2340
+ const fakerExports = await load('@faker-js/faker');
2341
+ let names;
2342
+ if (this.locale) {
2343
+ names = Array.isArray(this.locale) ? this.locale : [
2344
+ this.locale
2345
+ ];
2346
+ } else {
2347
+ names = [
2348
+ 'en'
2349
+ ];
2350
+ }
2351
+ for(let i = 0; i < names.length; i++){
2352
+ if (hasOwnProperty(fakerExports, 'default') && isObject(fakerExports.default) && hasOwnProperty(fakerExports.default, names[i])) {
2353
+ options.locale.push(fakerExports.default[names[i]]);
2354
+ continue;
2355
+ }
2356
+ if (hasOwnProperty(fakerExports, names[i])) {
2357
+ options.locale.push(fakerExports[names[i]]);
2358
+ }
2359
+ }
2360
+ this.faker = new fakerExports.Faker(options);
2361
+ return this.faker;
2362
+ }
2363
+ }
2364
+
2365
+ class SeederFactoryManager {
2366
+ set(entity, factoryFn) {
2367
+ const name = getEntityName(entity);
2368
+ this.items[name] = {
2369
+ factoryFn,
2370
+ entity
2371
+ };
2372
+ return this.items[name];
2373
+ }
2374
+ get(entity) {
2375
+ const name = getEntityName(entity);
2376
+ if (!hasOwnProperty(this.items, name)) {
2377
+ throw new Error(`No seeder factory is registered for the entity: ${name}`);
2378
+ }
2379
+ return new SeederFactory({
2380
+ factoryFn: this.items[name].factoryFn,
2381
+ entity,
2382
+ name
2383
+ });
2384
+ }
2385
+ constructor(){
2386
+ this.items = {};
2387
+ }
2388
+ }
2389
+
2390
+ async function resolveFilePatterns(filesPattern, root) {
2391
+ return locateMany(filesPattern, {
2392
+ ...root ? {
2393
+ path: root
2394
+ } : {},
2395
+ ignore: [
2396
+ '**/*.d.ts'
2397
+ ]
2398
+ }).then(buildFilePathname);
2399
+ }
2400
+ function resolveFilePaths(filePaths, root) {
2401
+ return filePaths.map((filePath)=>path.isAbsolute(filePath) ? filePath : path.resolve(root || process.cwd(), filePath));
2402
+ }
2403
+ /**
2404
+ * Exported only for testing purposes
2405
+ */ function buildFilePathname(files) {
2406
+ return(// sorting by name so that we can define the order of execution using file names
2407
+ files.sort((a, b)=>a.name > b.name ? 1 : -1).map((el)=>path.join(el.path, el.name + el.extension)));
2408
+ }
2409
+
2410
+ async function prepareSeederSeeds(input, root) {
2411
+ const items = [];
2412
+ let seedFiles = [];
2413
+ const seedConstructors = [];
2414
+ for(let i = 0; i < input.length; i++){
2415
+ const value = input[i];
2416
+ if (typeof value === 'string') {
2417
+ seedFiles.push(value);
2418
+ } else {
2419
+ seedConstructors.push(value);
2420
+ }
2421
+ }
2422
+ if (seedFiles.length > 0) {
2423
+ seedFiles = await resolveFilePatterns(seedFiles, root);
2424
+ seedFiles = resolveFilePaths(seedFiles, root);
2425
+ for(let i = 0; i < seedFiles.length; i++){
2426
+ const moduleExports = await load(seedFiles[i]);
2427
+ let clazzConstructor;
2428
+ const exportKeys = Object.keys(moduleExports);
2429
+ for(let j = 0; j < exportKeys.length; j++){
2430
+ const moduleExport = moduleExports[exportKeys[j]];
2431
+ if (typeof moduleExport === 'function' && moduleExport.prototype) {
2432
+ clazzConstructor = moduleExport;
2433
+ }
2434
+ }
2435
+ if (clazzConstructor) {
2436
+ const fileName = path.basename(seedFiles[i]);
2437
+ const filePath = seedFiles[i];
2438
+ const match = fileName.match(/^([0-9]{13,})-(.*)$/);
2439
+ let timestamp;
2440
+ if (match) {
2441
+ timestamp = parseInt(match[1], 10);
2442
+ }
2443
+ items.push({
2444
+ constructor: clazzConstructor,
2445
+ fileName,
2446
+ filePath,
2447
+ ...timestamp ? {
2448
+ timestamp
2449
+ } : {}
2450
+ });
2451
+ }
2452
+ }
2453
+ }
2454
+ if (seedConstructors.length > 0) {
2455
+ for(let i = 0; i < seedConstructors.length; i++){
2456
+ items.push({
2457
+ constructor: seedConstructors[i]
2458
+ });
2459
+ }
2460
+ }
2461
+ return items;
2462
+ }
2463
+
2464
+ function buildSeederFileTemplate(name, timestamp) {
2465
+ const className = `${pascalCase(name)}${timestamp}`;
2466
+ return `import { DataSource } from 'typeorm';
2467
+ import { Seeder, SeederFactoryManager } from 'typeorm-extension';
2468
+
2469
+ export class ${className} implements Seeder {
2470
+ track = false;
2471
+
2472
+ public async run(
2473
+ dataSource: DataSource,
2474
+ factoryManager: SeederFactoryManager
2475
+ ): Promise<any> {
2476
+
2477
+ }
2478
+ }
2479
+ `;
2480
+ }
2481
+
2482
+ let instance;
2483
+ function useSeederFactoryManager() {
2484
+ if (typeof instance !== 'undefined') {
2485
+ return instance;
2486
+ }
2487
+ instance = new SeederFactoryManager();
2488
+ return instance;
2489
+ }
2490
+ function setSeederFactory(entity, factoryFn) {
2491
+ const manager = useSeederFactoryManager();
2492
+ return manager.set(entity, factoryFn);
2493
+ }
2494
+ function useSeederFactory(entity) {
2495
+ const manager = useSeederFactoryManager();
2496
+ return manager.get(entity);
2497
+ }
2498
+ async function prepareSeederFactories(items, root) {
2499
+ let factoryFiles = [];
2500
+ const factoryConfigs = [];
2501
+ for(let i = 0; i < items.length; i++){
2502
+ const value = items[i];
2503
+ if (typeof value === 'string') {
2504
+ factoryFiles.push(value);
2505
+ } else {
2506
+ factoryConfigs.push(value);
2507
+ }
2508
+ }
2509
+ if (factoryFiles.length > 0) {
2510
+ factoryFiles = await resolveFilePatterns(factoryFiles, root);
2511
+ factoryFiles = resolveFilePaths(factoryFiles, root);
2512
+ for(let i = 0; i < factoryFiles.length; i++){
2513
+ await load(factoryFiles[i]);
2514
+ }
2515
+ }
2516
+ if (factoryConfigs.length > 0) {
2517
+ const factoryManager = useSeederFactoryManager();
2518
+ for(let i = 0; i < factoryConfigs.length; i++){
2519
+ factoryManager.set(factoryConfigs[i].entity, factoryConfigs[i].factoryFn);
2520
+ }
2521
+ }
2522
+ }
2523
+
2524
+ class SeederExecutor {
2525
+ constructor(dataSource, options){
2526
+ this.dataSource = dataSource;
2527
+ this.options = options || {};
2528
+ setDataSource(dataSource);
2529
+ this.tableName = this.dataSourceOptions.seedTableName || 'seeds';
2530
+ }
2531
+ async execute(input = {}) {
2532
+ const options = await this.buildOptions(input);
2533
+ if (!options.seeds || options.seeds.length === 0) {
2534
+ return [];
2535
+ }
2536
+ if (options.factories) {
2537
+ await prepareSeederFactories(options.factories, this.options.root);
2538
+ }
2539
+ const seederElements = await prepareSeederSeeds(options.seeds, this.options.root);
2540
+ const all = await this.buildEntities(seederElements);
2541
+ let tracking = !!options.seedTracking;
2542
+ if (!tracking) {
2543
+ tracking = all.some((seed)=>!!seed.trackExecution);
2544
+ }
2545
+ let queryRunner;
2546
+ let existing = [];
2547
+ if (tracking) {
2548
+ queryRunner = this.dataSource.createQueryRunner();
2549
+ await this.createTableIfNotExist(queryRunner);
2550
+ existing = await this.loadExisting(queryRunner);
2551
+ }
2552
+ const isMatch = (seed)=>{
2553
+ if (!options.seedName) {
2554
+ return true;
2555
+ }
2556
+ if (seed.name === options.seedName || seed.fileName === options.seedName) {
2557
+ return true;
2558
+ }
2559
+ if (!seed.filePath) {
2560
+ return false;
2561
+ }
2562
+ if (seed.filePath === options.seedName) {
2563
+ return true;
2564
+ }
2565
+ return resolveFilePath(options.seedName, this.options.root) === seed.filePath;
2566
+ };
2567
+ const pending = all.filter((seed)=>{
2568
+ if (!isMatch(seed)) {
2569
+ return false;
2570
+ }
2571
+ const index = existing.findIndex((el)=>el.name === seed.name);
2572
+ if (index === -1) {
2573
+ return true;
2574
+ }
2575
+ let seedTracking;
2576
+ if (typeof seed.trackExecution !== 'undefined') {
2577
+ seedTracking = seed.trackExecution;
2578
+ } else {
2579
+ seedTracking = options.seedTracking;
2580
+ }
2581
+ return !seedTracking;
2582
+ });
2583
+ if (pending.length === 0) {
2584
+ if (queryRunner) {
2585
+ await queryRunner.release();
2586
+ }
2587
+ return [];
2588
+ }
2589
+ this.dataSource.logger.logSchemaBuild(`${existing.length} seeds are already present in the database.`);
2590
+ this.dataSource.logger.logSchemaBuild(`${all.length} seeds were found in the source code.`);
2591
+ const factoryManager = useSeederFactoryManager();
2592
+ const executed = [];
2593
+ try {
2594
+ for(let i = 0; i < pending.length; i++){
2595
+ const seeder = pending[i].instance;
2596
+ if (!seeder) {
2597
+ continue;
2598
+ }
2599
+ pending[i].result = await seeder.run(this.dataSource, factoryManager);
2600
+ let seedTracking;
2601
+ if (typeof pending[i].trackExecution !== 'undefined') {
2602
+ seedTracking = pending[i].trackExecution;
2603
+ } else {
2604
+ seedTracking = options.seedTracking;
2605
+ }
2606
+ if (queryRunner && seedTracking) {
2607
+ await this.track(queryRunner, pending[i]);
2608
+ }
2609
+ this.dataSource.logger.logSchemaBuild(`Seed ${pending[i].name} has been executed successfully.`);
2610
+ executed.push(pending[i]);
2611
+ }
2612
+ } finally{
2613
+ if (queryRunner) {
2614
+ await queryRunner.release();
2615
+ }
2616
+ }
2617
+ return executed;
2618
+ }
2619
+ async loadExisting(queryRunner) {
2620
+ if (this.dataSource.driver.options.type === 'mongodb') {
2621
+ const mongoRunner = queryRunner;
2622
+ return mongoRunner.cursor(this.tableName, {}).sort({
2623
+ _id: -1
2624
+ }).toArray();
2625
+ }
2626
+ const raw = await this.dataSource.manager.createQueryBuilder(queryRunner).select().orderBy(this.dataSource.driver.escape('id'), 'DESC').from(this.table, this.tableName).getRawMany();
2627
+ return raw.map((migrationRaw)=>new SeederEntity({
2628
+ id: parseInt(migrationRaw.id, 10),
2629
+ timestamp: parseInt(migrationRaw.timestamp, 10),
2630
+ name: migrationRaw.name,
2631
+ constructor: undefined
2632
+ }));
2633
+ }
2634
+ /**
2635
+ * Gets all migrations that setup for this connection.
2636
+ */ async buildEntities(seeds) {
2637
+ if (!seeds) {
2638
+ return [];
2639
+ }
2640
+ let timestampCounter = 0;
2641
+ const entities = seeds.map((element)=>{
2642
+ const { constructor: seed, fileName, filePath } = element;
2643
+ let { timestamp } = element;
2644
+ const className = seed.name || seed.constructor.name;
2645
+ if (!timestamp) {
2646
+ timestamp = this.classNameToTimestamp(className);
2647
+ }
2648
+ const entity = new SeederEntity({
2649
+ fileName,
2650
+ filePath,
2651
+ timestamp: timestamp || timestampCounter,
2652
+ name: className,
2653
+ constructor: seed
2654
+ });
2655
+ timestampCounter++;
2656
+ return entity;
2657
+ });
2658
+ this.checkForDuplicates(entities);
2659
+ // sort them by file name than by timestamp
2660
+ return entities.sort((a, b)=>{
2661
+ if (typeof a.fileName !== 'undefined' && typeof b.fileName !== 'undefined') {
2662
+ return a.fileName > b.fileName ? 1 : -1;
2663
+ }
2664
+ return a.timestamp - b.timestamp;
2665
+ });
2666
+ }
2667
+ checkForDuplicates(entities) {
2668
+ const names = entities.map((migration)=>migration.name);
2669
+ const duplicates = Array.from(new Set(names.filter((migrationName, index)=>names.indexOf(migrationName) < index)));
2670
+ if (duplicates.length > 0) {
2671
+ throw Error(`Duplicate seeds: ${duplicates.join(', ')}`);
2672
+ }
2673
+ }
2674
+ async createTableIfNotExist(queryRunner) {
2675
+ // If driver is mongo no need to create
2676
+ if (this.dataSource.driver.options.type === 'mongodb') {
2677
+ return;
2678
+ }
2679
+ const tableExist = await queryRunner.hasTable(this.table);
2680
+ if (!tableExist) {
2681
+ await queryRunner.createTable(new Table({
2682
+ database: this.database,
2683
+ schema: this.schema,
2684
+ name: this.table,
2685
+ columns: [
2686
+ {
2687
+ name: 'id',
2688
+ type: this.dataSource.driver.normalizeType({
2689
+ type: this.dataSource.driver.mappedDataTypes.migrationId
2690
+ }),
2691
+ isGenerated: true,
2692
+ generationStrategy: 'increment',
2693
+ isPrimary: true,
2694
+ isNullable: false
2695
+ },
2696
+ {
2697
+ name: 'timestamp',
2698
+ type: this.dataSource.driver.normalizeType({
2699
+ type: this.dataSource.driver.mappedDataTypes.migrationTimestamp
2700
+ }),
2701
+ isPrimary: false,
2702
+ isNullable: false
2703
+ },
2704
+ {
2705
+ name: 'name',
2706
+ type: this.dataSource.driver.normalizeType({
2707
+ type: this.dataSource.driver.mappedDataTypes.migrationName
2708
+ }),
2709
+ isNullable: false
2710
+ }
2711
+ ]
2712
+ }));
2713
+ }
2714
+ }
2715
+ async track(queryRunner, seederEntity) {
2716
+ const values = {};
2717
+ if (this.dataSource.driver.options.type === 'mssql') {
2718
+ values.timestamp = new MssqlParameter(seederEntity.timestamp, this.dataSource.driver.normalizeType({
2719
+ type: this.dataSource.driver.mappedDataTypes.migrationTimestamp
2720
+ }));
2721
+ values.name = new MssqlParameter(seederEntity.name, this.dataSource.driver.normalizeType({
2722
+ type: this.dataSource.driver.mappedDataTypes.migrationName
2723
+ }));
2724
+ } else {
2725
+ values.timestamp = seederEntity.timestamp;
2726
+ values.name = seederEntity.name;
2727
+ }
2728
+ if (this.dataSource.driver.options.type === 'mongodb') {
2729
+ const mongoRunner = queryRunner;
2730
+ await mongoRunner.databaseConnection.db(this.dataSource.driver.database).collection(this.tableName).insertOne(values);
2731
+ } else {
2732
+ const qb = queryRunner.manager.createQueryBuilder();
2733
+ await qb.insert().into(this.table).values(values).execute();
2734
+ }
2735
+ }
2736
+ get dataSourceOptions() {
2737
+ return this.dataSource.options;
2738
+ }
2739
+ get database() {
2740
+ return this.dataSource.driver.database;
2741
+ }
2742
+ get schema() {
2743
+ return this.dataSource.driver.schema;
2744
+ }
2745
+ get table() {
2746
+ return this.dataSource.driver.buildTableName(this.tableName, this.schema, this.database);
2747
+ }
2748
+ async buildOptions(input = {}) {
2749
+ const options = {
2750
+ ...input,
2751
+ seeds: input.seeds || [],
2752
+ factories: input.factories || [],
2753
+ seedTracking: input.seedTracking ?? false
2754
+ };
2755
+ if (!options.seeds || options.seeds.length === 0) {
2756
+ options.seeds = this.dataSourceOptions.seeds;
2757
+ }
2758
+ if (!options.seeds || options.seeds.length === 0) {
2759
+ options.seeds = useEnv('seeds');
2760
+ }
2761
+ if (!options.seeds || options.seeds.length === 0) {
2762
+ options.seeds = [
2763
+ 'src/database/seeds/**/*{.ts,.js}'
2764
+ ];
2765
+ }
2766
+ if (!options.factories || options.factories.length === 0) {
2767
+ options.factories = this.dataSourceOptions.factories;
2768
+ }
2769
+ if (!options.factories || options.factories.length === 0) {
2770
+ options.factories = useEnv('factories');
2771
+ }
2772
+ if (!options.factories || options.factories.length === 0) {
2773
+ options.factories = [
2774
+ 'src/database/factories/**/*{.ts,.js}'
2775
+ ];
2776
+ }
2777
+ if (typeof options.seedTracking === 'undefined') {
2778
+ options.seedTracking = this.dataSourceOptions.seedTracking;
2779
+ }
2780
+ if (!this.options.preserveFilePaths) {
2781
+ let tsConfig;
2782
+ if (isObject(this.options.tsconfig)) {
2783
+ tsConfig = this.options.tsconfig;
2784
+ } else {
2785
+ tsConfig = await readTSConfig(resolveFilePath(this.options.tsconfig || 'tsconfig.json', this.options.root));
2786
+ }
2787
+ await adjustFilePaths(options, [
2788
+ 'seeds',
2789
+ 'seedName',
2790
+ 'factories'
2791
+ ], tsConfig);
2792
+ }
2793
+ return options;
2794
+ }
2795
+ classNameToTimestamp(className) {
2796
+ const match = className.match(/^(.*)([0-9]{13,})$/);
2797
+ if (match) {
2798
+ return parseInt(match[2], 10);
2799
+ }
2800
+ return undefined;
2801
+ }
2802
+ }
2803
+
2804
+ async function runSeeder(dataSource, seeder, options = {}) {
2805
+ if (typeof seeder === 'string') {
2806
+ options.seedName = seeder;
2807
+ } else {
2808
+ options.seeds = [
2809
+ seeder
2810
+ ];
2811
+ }
2812
+ const executor = new SeederExecutor(dataSource);
2813
+ const output = await executor.execute(options);
2814
+ return output.pop();
2815
+ }
2816
+ async function runSeeders(dataSource, options) {
2817
+ const executor = new SeederExecutor(dataSource);
2818
+ return executor.execute(options);
2819
+ }
2820
+
2821
+ class SeedCreateCommand {
2822
+ builder(args) {
2823
+ return args.option('root', {
2824
+ alias: 'r',
2825
+ default: process.cwd(),
2826
+ describe: 'Root directory of the project.'
2827
+ }).option('timestamp', {
2828
+ alias: 't',
2829
+ type: 'number',
2830
+ describe: 'Custom timestamp for the seeder name.'
2831
+ }).option('javascript', {
2832
+ alias: 'j',
2833
+ type: 'boolean',
2834
+ default: false,
2835
+ describe: 'Generate a seeder file for JavaScript instead of TypeScript.'
2836
+ }).option('name', {
2837
+ alias: 'n',
2838
+ describe: 'Name (or relative path incl. name) of the seeder.',
2839
+ demandOption: true
2840
+ });
2841
+ }
2842
+ async handler(raw) {
2843
+ const args = raw;
2844
+ let timestamp;
2845
+ if (Number.isNaN(args.timestamp) || !args.timestamp) {
2846
+ timestamp = Date.now();
2847
+ } else {
2848
+ timestamp = args.timestamp;
2849
+ }
2850
+ const sourcePath = parseFilePath(args.name, args.root);
2851
+ const dirNameIsDirectory = await isDirectory(sourcePath.directory);
2852
+ if (!dirNameIsDirectory) {
2853
+ consola.warn(`The output directory ${sourcePath.directory} does not exist.`);
2854
+ process.exit(1);
2855
+ }
2856
+ const extension = args.javascript ? '.js' : '.ts';
2857
+ const nameExtension = getFileNameExtension(sourcePath.name);
2858
+ const nameWithoutExtension = removeFileNameExtension(sourcePath.name);
2859
+ let fileName;
2860
+ if (nameExtension) {
2861
+ fileName = `${timestamp}-${sourcePath.name}`;
2862
+ } else {
2863
+ fileName = `${timestamp}-${sourcePath.name}${extension}`;
2864
+ }
2865
+ const filePath = sourcePath.directory + path.sep + fileName;
2866
+ const template = buildSeederFileTemplate(nameWithoutExtension, timestamp);
2867
+ consola.info(`Seed Directory: ${sourcePath.directory}`);
2868
+ consola.info(`Seed FileName: ${fileName}`);
2869
+ consola.info(`Seed Name: ${pascalCase(nameWithoutExtension)}`);
2870
+ try {
2871
+ await fs.promises.writeFile(filePath, template, {
2872
+ encoding: 'utf-8'
2873
+ });
2874
+ } catch (e) {
2875
+ consola.warn(`The seed could not be written to the path ${filePath}.`);
2876
+ process.exit(1);
2877
+ }
2878
+ process.exit(0);
2879
+ }
2880
+ constructor(){
2881
+ this.command = 'seed:create';
2882
+ this.describe = 'Create a seeder file.';
2883
+ }
2884
+ }
2885
+
2886
+ class SeedRunCommand {
2887
+ builder(args) {
2888
+ return args.option('preserveFilePaths', {
2889
+ default: false,
2890
+ type: 'boolean',
2891
+ describe: 'This option indicates if file paths should be preserved.'
2892
+ }).option('root', {
2893
+ alias: 'r',
2894
+ default: process.cwd(),
2895
+ describe: 'Root directory of the project.'
2896
+ }).option('tsconfig', {
2897
+ alias: 'tc',
2898
+ default: 'tsconfig.json',
2899
+ describe: 'Name (or relative path incl. name) of the tsconfig file.'
2900
+ }).option('dataSource', {
2901
+ alias: 'd',
2902
+ default: 'data-source',
2903
+ describe: 'Name (or relative path incl. name) of the data-source file.'
2904
+ }).option('name', {
2905
+ alias: 'n',
2906
+ describe: 'Name (or relative path incl. name) of the seeder.'
2907
+ });
2908
+ }
2909
+ async handler(raw) {
2910
+ const args = raw;
2911
+ let tsconfig;
2912
+ let sourcePath = resolveFilePath(args.dataSource, args.root);
2913
+ if (!args.preserveFilePaths) {
2914
+ tsconfig = await readTSConfig(args.root);
2915
+ sourcePath = await adjustFilePath(sourcePath, tsconfig);
2916
+ args.name = await adjustFilePath(args.name, tsconfig);
2917
+ }
2918
+ const source = parseFilePath(sourcePath);
2919
+ consola.info(`DataSource Directory: ${source.directory}`);
2920
+ consola.info(`DataSource Name: ${source.name}`);
2921
+ const dataSourceOptions = await buildDataSourceOptions({
2922
+ dataSourceName: source.name,
2923
+ directory: source.directory,
2924
+ tsconfig,
2925
+ preserveFilePaths: args.preserveFilePaths
2926
+ });
2927
+ setDataSourceOptions(dataSourceOptions);
2928
+ if (args.name) {
2929
+ consola.info(`Seed Name: ${args.name}`);
2930
+ }
2931
+ const dataSource = await useDataSource();
2932
+ const executor = new SeederExecutor(dataSource, {
2933
+ root: args.root,
2934
+ tsconfig,
2935
+ preserveFilePaths: args.preserveFilePaths
2936
+ });
2937
+ await executor.execute({
2938
+ seedName: args.name
2939
+ });
2940
+ process.exit(0);
2941
+ }
2942
+ constructor(){
2943
+ this.command = 'seed:run';
2944
+ this.describe = 'Populate the database with an initial data set or generated data by a factory.';
2945
+ }
2946
+ }
2947
+
2948
+ class EntityRelationLookupError extends TypeormExtensionError {
2949
+ constructor(options){
2950
+ super(options.message);
2951
+ this.relation = options.relation;
2952
+ this.columns = options.columns;
2953
+ }
2954
+ static notReferenced(relation, columns) {
2955
+ return new EntityRelationLookupError({
2956
+ message: `${relation} entity is not referenced by ${columns.join(', ')}`,
2957
+ relation,
2958
+ columns
2959
+ });
2960
+ }
2961
+ static notFound(relation, columns) {
2962
+ return new EntityRelationLookupError({
2963
+ message: `Can't find ${relation} entity by ${columns.join(', ')}`,
2964
+ relation,
2965
+ columns
2966
+ });
2967
+ }
2968
+ }
2969
+
2970
+ /**
2971
+ * Receive metadata for a given repository or entity-target.
2972
+ *
2973
+ * @experimental
2974
+ * @param input
2975
+ * @param dataSource
2976
+ */ async function getEntityMetadata(input, dataSource) {
2977
+ if (input instanceof Repository) {
2978
+ return input.metadata;
2979
+ }
2980
+ dataSource = dataSource || await useDataSource();
2981
+ const index = dataSource.entityMetadatas.findIndex((entityMetadata)=>entityMetadata.target === input);
2982
+ if (index === -1) {
2983
+ throw new Error(`The entity ${input} is not registered.`);
2984
+ }
2985
+ return dataSource.entityMetadatas[index];
2986
+ }
2987
+
2988
+ /**
2989
+ * Get (relation-) property names of a given entity.
2990
+ *
2991
+ * @experimental
2992
+ * @param input
2993
+ * @param dataSource
2994
+ */ async function getEntityPropertyNames(input, dataSource) {
2995
+ let entityMetadata;
2996
+ if (input instanceof Repository) {
2997
+ entityMetadata = input.metadata;
2998
+ } else {
2999
+ entityMetadata = await getEntityMetadata(input, dataSource);
3000
+ }
3001
+ const items = [];
3002
+ for(let i = 0; i < entityMetadata.columns.length; i++){
3003
+ items.push(entityMetadata.columns[i].propertyName);
3004
+ }
3005
+ for(let i = 0; i < entityMetadata.relations.length; i++){
3006
+ items.push(entityMetadata.relations[i].propertyName);
3007
+ }
3008
+ return items;
3009
+ }
3010
+
3011
+ /**
3012
+ * Validate join columns of a given entity.
3013
+ * It will look up and append the referenced entities to the input entity.
3014
+ *
3015
+ * @experimental
3016
+ * @param entity
3017
+ * @param options
3018
+ */ async function validateEntityJoinColumns(entity, options) {
3019
+ const dataSource = options.dataSource || await useDataSource();
3020
+ const entityMetadata = await getEntityMetadata(options.entityTarget, dataSource);
3021
+ const relations = {};
3022
+ for(let i = 0; i < entityMetadata.relations.length; i++){
3023
+ const relation = entityMetadata.relations[i];
3024
+ let skipRelation = false;
3025
+ const where = {};
3026
+ const columns = [];
3027
+ for(let j = 0; j < relation.joinColumns.length; j++){
3028
+ const joinColumn = relation.joinColumns[j];
3029
+ if (typeof entity[joinColumn.propertyName] === 'undefined') {
3030
+ continue;
3031
+ }
3032
+ if (joinColumn.isNullable && entity[joinColumn.propertyName] === null) {
3033
+ skipRelation = true;
3034
+ break;
3035
+ }
3036
+ if (joinColumn.referencedColumn) {
3037
+ where[joinColumn.referencedColumn.propertyName] = entity[joinColumn.propertyName];
3038
+ columns.push(joinColumn.propertyName);
3039
+ } else {
3040
+ throw EntityRelationLookupError.notReferenced(relation.propertyName, [
3041
+ joinColumn.propertyName
3042
+ ]);
3043
+ }
3044
+ }
3045
+ if (skipRelation || columns.length === 0) {
3046
+ continue;
3047
+ }
3048
+ const repository = dataSource.getRepository(relation.type);
3049
+ const item = await repository.findOne({
3050
+ where
3051
+ });
3052
+ if (!item) {
3053
+ throw EntityRelationLookupError.notFound(relation.propertyName, columns);
3054
+ }
3055
+ relations[relation.propertyName] = item;
3056
+ }
3057
+ const relationKeys = Object.keys(relations);
3058
+ for(let i = 0; i < relationKeys.length; i++){
3059
+ const relationKey = relationKeys[i];
3060
+ entity[relationKey] = relations[relationKey];
3061
+ }
3062
+ return entity;
3063
+ }
3064
+
3065
+ function transformUndefinedToNull(input) {
3066
+ if (typeof input === 'undefined') {
3067
+ return null;
3068
+ }
3069
+ return input;
3070
+ }
3071
+ function applyWhereExpression(qb, data, type) {
3072
+ const elements = [];
3073
+ const keys = Object.keys(data);
3074
+ for(let i = 0; i < keys.length; i++){
3075
+ elements.push({
3076
+ key: keys[i],
3077
+ value: transformUndefinedToNull(data[keys[i]]),
3078
+ operator: type === 'target' ? FilterComparisonOperator.EQUAL : FilterComparisonOperator.NOT_EQUAL
3079
+ });
3080
+ }
3081
+ const queryFilters = transformParsedFilters(elements, {
3082
+ bindingKey (key) {
3083
+ if (type === 'source') {
3084
+ return `filter_source_${key}`;
3085
+ }
3086
+ return `filter_target_${key}`;
3087
+ }
3088
+ });
3089
+ applyFiltersTransformed(qb, queryFilters);
3090
+ return queryFilters;
3091
+ }
3092
+ /**
3093
+ * Check if a given entity does not already exist.
3094
+ * Composite unique keys on a null column can only be present once.
3095
+ *
3096
+ * @experimental
3097
+ * @param options
3098
+ */ async function isEntityUnique(options) {
3099
+ const dataSource = options.dataSource || await useDataSource();
3100
+ const metadata = await getEntityMetadata(options.entityTarget, dataSource);
3101
+ const repository = dataSource.getRepository(metadata.target);
3102
+ const primaryColumnNames = metadata.primaryColumns.map((c)=>c.propertyName);
3103
+ const columnGroups = [];
3104
+ if (metadata.ownUniques && metadata.ownUniques.length > 0) {
3105
+ for(let i = 0; i < metadata.ownUniques.length; i++){
3106
+ columnGroups.push(metadata.ownUniques[i].columns.map((column)=>column.propertyName));
3107
+ }
3108
+ } else {
3109
+ for(let i = 0; i < metadata.indices.length; i++){
3110
+ const index = metadata.indices[i];
3111
+ if (!index.isUnique || index.entityMetadata.target !== metadata.target) {
3112
+ continue;
3113
+ }
3114
+ columnGroups.push(index.columns.map((column)=>column.propertyName));
3115
+ }
3116
+ }
3117
+ for(let i = 0; i < columnGroups.length; i++){
3118
+ const queryBuilder = repository.createQueryBuilder('entity');
3119
+ queryBuilder.where(new Brackets((qb)=>{
3120
+ applyWhereExpression(qb, pickRecord(options.entity, columnGroups[i]), 'target');
3121
+ }));
3122
+ if (options.entityExisting) {
3123
+ queryBuilder.andWhere(new Brackets((qb)=>{
3124
+ applyWhereExpression(qb, pickRecord(options.entityExisting, primaryColumnNames), 'source');
3125
+ }));
3126
+ }
3127
+ const entity = await queryBuilder.getOne();
3128
+ if (entity) {
3129
+ return false;
3130
+ }
3131
+ }
3132
+ return true;
3133
+ }
3134
+
3135
+ export { CodeTransformation, DatabaseCreateCommand, DatabaseDropCommand, DriverError, EntityRelationLookupError, EnvironmentName, EnvironmentVariableName, OptionsError, SeedCreateCommand, SeedRunCommand, SeederEntity, SeederExecutor, SeederFactory, SeederFactoryManager, TypeormExtensionError, adjustFilePath, adjustFilePaths, applyFields, applyFilters, applyFiltersTransformed, applyPagination, applyQuery, applyQueryFields, applyQueryFieldsParseOutput, applyQueryFilters, applyQueryFiltersParseOutput, applyQueryPagination, applyQueryPaginationParseOutput, applyQueryParseOutput, applyQueryRelations, applyQueryRelationsParseOutput, applyQuerySort, applyQuerySortParseOutput, applyRelations, applySort, buildDataSourceOptions, buildDatabaseCreateContext, buildDatabaseDropContext, buildDriverOptions, buildFilePathname, buildKeyWithPrefix, buildSeederFileTemplate, canReplaceWindowsSeparator, checkDatabase, createCockroachDBDatabase, createDatabase, createDriver, createMongoDBDatabase, createMsSQLDatabase, createMySQLDatabase, createOracleDatabase, createPostgresDatabase, createSQLiteDatabase, createSimpleMongoDBConnection, createSimpleMsSQLConnection, createSimpleMySQLConnection, createSimpleOracleConnection, createSimplePostgresConnection, detectCodeTransformation, dropCockroachDBDatabase, dropDatabase, dropMongoDBDatabase, dropMsSQLDatabase, dropMySQLDatabase, dropOracleDatabase, dropPostgresDatabase, dropSQLiteDatabase, executeSimpleCockroachDBQuery, executeSimpleMysqlQuery, executeSimplePostgresQuery, existsQuery, extendObject, findDataSource, generateMigration, getAliasForPath, getCharacterSetFromDataSourceOptions, getCharsetFromDataSourceOptions, getEntityMetadata, getEntityName, getEntityPropertyNames, hasDataSource, hasDataSourceOptions, hasEnvDataSourceOptions, hasOwnProperty, hasStringProperty, hasTrailingSlash, isCodeTransformation, isDirectory, isEntityUnique, isPromise, isQueryOptionDefined, mergeDataSourceOptions, mergeDataSourceOptionsWithEnv, parseFilePath, pickRecord, prepareSeederFactories, prepareSeederSeeds, readDataSourceOptionsFromEnv, readTSConfig, replaceWindowSeparator, resetEnv, resolveFilePath, resolveFilePaths, resolveFilePatterns, runSeeder, runSeeders, safeReplaceWindowsSeparator, setDataSource, setDataSourceOptions, setSeederFactory, synchronizeDatabaseSchema, transformFilePath, transformParsedFilters, unsetDataSource, useDataSource, useDataSourceOptions, useEnv, useSeederFactory, useSeederFactoryManager, validateEntityJoinColumns, withoutTrailingSlash };
3136
+ //# sourceMappingURL=index.mjs.map