nestjs-ddd-cli 3.2.2 → 3.3.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 (210) hide show
  1. package/README.md +118 -9
  2. package/ddd.schema.json +11 -1
  3. package/dist/commands/doctor-enhanced.d.ts +1 -0
  4. package/dist/commands/doctor-enhanced.js +94 -79
  5. package/dist/commands/doctor-enhanced.js.map +1 -1
  6. package/dist/commands/doctor.d.ts +9 -0
  7. package/dist/commands/doctor.js +68 -21
  8. package/dist/commands/doctor.js.map +1 -1
  9. package/dist/commands/generate-all.js +54 -28
  10. package/dist/commands/generate-all.js.map +1 -1
  11. package/dist/commands/generate-controller.d.ts +1 -0
  12. package/dist/commands/generate-controller.js +71 -0
  13. package/dist/commands/generate-controller.js.map +1 -0
  14. package/dist/commands/generate-domain-service.d.ts +1 -1
  15. package/dist/commands/generate-domain-service.js +6 -4
  16. package/dist/commands/generate-domain-service.js.map +1 -1
  17. package/dist/commands/generate-dto.d.ts +4 -0
  18. package/dist/commands/generate-dto.js +162 -0
  19. package/dist/commands/generate-dto.js.map +1 -0
  20. package/dist/commands/generate-entity.js +7 -2
  21. package/dist/commands/generate-entity.js.map +1 -1
  22. package/dist/commands/generate-event.js +10 -4
  23. package/dist/commands/generate-event.js.map +1 -1
  24. package/dist/commands/generate-migration-deployment.d.ts +15 -0
  25. package/dist/commands/generate-migration-deployment.js +326 -0
  26. package/dist/commands/generate-migration-deployment.js.map +1 -0
  27. package/dist/commands/generate-migration-deployment.spec.d.ts +1 -0
  28. package/dist/commands/generate-migration-deployment.spec.js +337 -0
  29. package/dist/commands/generate-migration-deployment.spec.js.map +1 -0
  30. package/dist/commands/generate-module.js +4 -2
  31. package/dist/commands/generate-module.js.map +1 -1
  32. package/dist/commands/generate-query.js +23 -4
  33. package/dist/commands/generate-query.js.map +1 -1
  34. package/dist/commands/generate-repository.d.ts +1 -1
  35. package/dist/commands/generate-repository.js +17 -34
  36. package/dist/commands/generate-repository.js.map +1 -1
  37. package/dist/commands/generate-service.js +19 -4
  38. package/dist/commands/generate-service.js.map +1 -1
  39. package/dist/commands/generate-usecase.js +12 -31
  40. package/dist/commands/generate-usecase.js.map +1 -1
  41. package/dist/commands/generate-value-object.js +3 -14
  42. package/dist/commands/generate-value-object.js.map +1 -1
  43. package/dist/commands/init-project.d.ts +3 -0
  44. package/dist/commands/init-project.js +19 -9
  45. package/dist/commands/init-project.js.map +1 -1
  46. package/dist/commands/interactive-scaffold.js +10 -0
  47. package/dist/commands/interactive-scaffold.js.map +1 -1
  48. package/dist/commands/migration.d.ts +3 -0
  49. package/dist/commands/migration.js +83 -48
  50. package/dist/commands/migration.js.map +1 -1
  51. package/dist/commands/recipe.d.ts +2 -0
  52. package/dist/commands/recipe.js +229 -85
  53. package/dist/commands/recipe.js.map +1 -1
  54. package/dist/commands/recipes/banklink-connector-contract.recipe.d.ts +1 -0
  55. package/dist/commands/recipes/banklink-connector-contract.recipe.js +161 -0
  56. package/dist/commands/recipes/banklink-connector-contract.recipe.js.map +1 -0
  57. package/dist/commands/recipes/bounded-json-http.recipe.d.ts +2 -0
  58. package/dist/commands/recipes/bounded-json-http.recipe.js +53 -0
  59. package/dist/commands/recipes/bounded-json-http.recipe.js.map +1 -0
  60. package/dist/commands/recipes/business-reference-identifiers.recipe.d.ts +1 -0
  61. package/dist/commands/recipes/business-reference-identifiers.recipe.js +223 -0
  62. package/dist/commands/recipes/business-reference-identifiers.recipe.js.map +1 -0
  63. package/dist/commands/recipes/event-backbone.recipe.d.ts +1 -0
  64. package/dist/commands/recipes/event-backbone.recipe.js +1118 -0
  65. package/dist/commands/recipes/event-backbone.recipe.js.map +1 -0
  66. package/dist/commands/recipes/external-projection-worker.recipe.d.ts +6 -0
  67. package/dist/commands/recipes/external-projection-worker.recipe.js +211 -0
  68. package/dist/commands/recipes/external-projection-worker.recipe.js.map +1 -0
  69. package/dist/commands/recipes/index.d.ts +11 -0
  70. package/dist/commands/recipes/index.js +11 -0
  71. package/dist/commands/recipes/index.js.map +1 -1
  72. package/dist/commands/recipes/jest-cjs-compat.recipe.d.ts +3 -0
  73. package/dist/commands/recipes/jest-cjs-compat.recipe.js +130 -0
  74. package/dist/commands/recipes/jest-cjs-compat.recipe.js.map +1 -0
  75. package/dist/commands/recipes/joi-env.recipe.js +1 -1
  76. package/dist/commands/recipes/kafka-consumer.recipe.d.ts +1 -0
  77. package/dist/commands/recipes/kafka-consumer.recipe.js +65 -0
  78. package/dist/commands/recipes/kafka-consumer.recipe.js.map +1 -0
  79. package/dist/commands/recipes/oauth2-client-credentials.recipe.d.ts +1 -0
  80. package/dist/commands/recipes/oauth2-client-credentials.recipe.js +60 -0
  81. package/dist/commands/recipes/oauth2-client-credentials.recipe.js.map +1 -0
  82. package/dist/commands/recipes/oauth2-token-introspection.recipe.d.ts +1 -0
  83. package/dist/commands/recipes/oauth2-token-introspection.recipe.js +67 -0
  84. package/dist/commands/recipes/oauth2-token-introspection.recipe.js.map +1 -0
  85. package/dist/commands/recipes/oidc-bff-session.recipe.d.ts +1 -0
  86. package/dist/commands/recipes/oidc-bff-session.recipe.js +57 -0
  87. package/dist/commands/recipes/oidc-bff-session.recipe.js.map +1 -0
  88. package/dist/commands/recipes/oidc-dashboard.recipe.d.ts +1 -0
  89. package/dist/commands/recipes/oidc-dashboard.recipe.js +463 -0
  90. package/dist/commands/recipes/oidc-dashboard.recipe.js.map +1 -0
  91. package/dist/commands/recipes/oidc-resource-server.recipe.d.ts +1 -0
  92. package/dist/commands/recipes/oidc-resource-server.recipe.js +85 -0
  93. package/dist/commands/recipes/oidc-resource-server.recipe.js.map +1 -0
  94. package/dist/commands/recipes/platform-context.recipe.d.ts +1 -0
  95. package/dist/commands/recipes/platform-context.recipe.js +229 -0
  96. package/dist/commands/recipes/platform-context.recipe.js.map +1 -0
  97. package/dist/commands/recipes/platform-parc-authorization.recipe.d.ts +1 -0
  98. package/dist/commands/recipes/platform-parc-authorization.recipe.js +339 -0
  99. package/dist/commands/recipes/platform-parc-authorization.recipe.js.map +1 -0
  100. package/dist/commands/recipes/platform-parc-bound-authorization.recipe.d.ts +1 -0
  101. package/dist/commands/recipes/platform-parc-bound-authorization.recipe.js +56 -0
  102. package/dist/commands/recipes/platform-parc-bound-authorization.recipe.js.map +1 -0
  103. package/dist/commands/recipes/platform-service-access-request-context.recipe.d.ts +1 -0
  104. package/dist/commands/recipes/platform-service-access-request-context.recipe.js +103 -0
  105. package/dist/commands/recipes/platform-service-access-request-context.recipe.js.map +1 -0
  106. package/dist/commands/recipes/platform-service-runtime.recipe.d.ts +1 -0
  107. package/dist/commands/recipes/platform-service-runtime.recipe.js +415 -0
  108. package/dist/commands/recipes/platform-service-runtime.recipe.js.map +1 -0
  109. package/dist/index.js +77 -9
  110. package/dist/index.js.map +1 -1
  111. package/dist/templates/ai-context/CLAUDE.md.hbs +39 -3
  112. package/dist/templates/ai-context/conventions.md.hbs +13 -8
  113. package/dist/templates/command/create-command.hbs +8 -3
  114. package/dist/templates/command/delete-command.hbs +1 -1
  115. package/dist/templates/command/update-command.hbs +1 -1
  116. package/dist/templates/controller/application-controller.hbs +7 -0
  117. package/dist/templates/controller/controller.hbs +6 -0
  118. package/dist/templates/dto/create-dto.hbs +17 -5
  119. package/dist/templates/dto/filter-dto.hbs +1 -1
  120. package/dist/templates/dto/response-dto.hbs +1 -1
  121. package/dist/templates/entity/entity.hbs +4 -0
  122. package/dist/templates/mapper/mapper.hbs +5 -1
  123. package/dist/templates/orm-entity/orm-entity.hbs +9 -1
  124. package/dist/templates/prisma/prisma-mapper.hbs +6 -0
  125. package/dist/templates/prisma/prisma-repository.hbs +57 -5
  126. package/dist/templates/prisma/prisma-schema.hbs +2 -0
  127. package/dist/templates/query/get-all.query.hbs +10 -2
  128. package/dist/templates/recipes/bounded-json-http/README.md.hbs +36 -0
  129. package/dist/templates/recipes/external-projection-worker/README.md.hbs +157 -0
  130. package/dist/templates/recipes/external-projection-worker/canonical-json.ts.hbs +123 -0
  131. package/dist/templates/recipes/external-projection-worker/enqueuer.ts.hbs +167 -0
  132. package/dist/templates/recipes/external-projection-worker/index.ts.hbs +6 -0
  133. package/dist/templates/recipes/external-projection-worker/migration.ts.hbs +88 -0
  134. package/dist/templates/recipes/external-projection-worker/module.ts.hbs +36 -0
  135. package/dist/templates/recipes/external-projection-worker/store.ts.hbs +354 -0
  136. package/dist/templates/recipes/external-projection-worker/types.ts.hbs +134 -0
  137. package/dist/templates/recipes/external-projection-worker/worker.ts.hbs +296 -0
  138. package/dist/templates/recipes/kafka-consumer/README.md.hbs +50 -0
  139. package/dist/templates/recipes/kafka-consumer/kafka-consumer.ts.hbs +135 -0
  140. package/dist/templates/recipes/kafka-consumer/migration.ts.hbs +17 -0
  141. package/dist/templates/recipes/oauth2-client-credentials/README.md.hbs +40 -0
  142. package/dist/templates/recipes/oauth2-client-credentials/client.spec.ts.hbs +234 -0
  143. package/dist/templates/recipes/oauth2-client-credentials/client.ts.hbs +210 -0
  144. package/dist/templates/recipes/oauth2-client-credentials/index.ts.hbs +2 -0
  145. package/dist/templates/recipes/oauth2-client-credentials/module.ts.hbs +58 -0
  146. package/dist/templates/recipes/oauth2-token-introspection/README.md.hbs +158 -0
  147. package/dist/templates/recipes/oauth2-token-introspection/client.spec.ts.hbs +350 -0
  148. package/dist/templates/recipes/oauth2-token-introspection/client.ts.hbs +241 -0
  149. package/dist/templates/recipes/oauth2-token-introspection/http-client.spec.ts.hbs +411 -0
  150. package/dist/templates/recipes/oauth2-token-introspection/http-client.ts.hbs +343 -0
  151. package/dist/templates/recipes/oauth2-token-introspection/index.ts.hbs +3 -0
  152. package/dist/templates/recipes/oauth2-token-introspection/module.ts.hbs +58 -0
  153. package/dist/templates/recipes/oidc-bff-session/README.md.hbs +96 -0
  154. package/dist/templates/recipes/oidc-bff-session/client.test.ts.hbs +663 -0
  155. package/dist/templates/recipes/oidc-bff-session/client.ts.hbs +635 -0
  156. package/dist/templates/recipes/oidc-bff-session/index.ts.hbs +3 -0
  157. package/dist/templates/recipes/oidc-bff-session/store.integration.test.ts.hbs +480 -0
  158. package/dist/templates/recipes/oidc-bff-session/store.test.ts.hbs +87 -0
  159. package/dist/templates/recipes/oidc-bff-session/store.ts.hbs +549 -0
  160. package/dist/templates/recipes/oidc-bff-session/valkey.test.ts.hbs +158 -0
  161. package/dist/templates/recipes/oidc-bff-session/valkey.ts.hbs +148 -0
  162. package/dist/templates/recipes/oidc-resource-server/README.md.hbs +74 -0
  163. package/dist/templates/recipes/oidc-resource-server/index.ts.hbs +2 -0
  164. package/dist/templates/recipes/oidc-resource-server/module.ts.hbs +51 -0
  165. package/dist/templates/recipes/oidc-resource-server/verifier.spec.ts.hbs +186 -0
  166. package/dist/templates/recipes/oidc-resource-server/verifier.ts.hbs +440 -0
  167. package/dist/templates/recipes/platform-parc-bound-authorization/README.md.hbs +92 -0
  168. package/dist/templates/recipes/platform-parc-bound-authorization/client.ts.hbs +142 -0
  169. package/dist/templates/recipes/platform-service-access-request-context/docs.md.hbs +61 -0
  170. package/dist/templates/recipes/platform-service-access-request-context/env.example.hbs +7 -0
  171. package/dist/templates/recipes/platform-service-access-request-context/index.ts.hbs +7 -0
  172. package/dist/templates/recipes/platform-service-access-request-context/request-context-headers.ts.hbs +61 -0
  173. package/dist/templates/recipes/platform-service-access-request-context/request-context-introspection.parser.ts.hbs +132 -0
  174. package/dist/templates/recipes/platform-service-access-request-context/request-context-introspection.types.ts.hbs +40 -0
  175. package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.client.ts.hbs +45 -0
  176. package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.config.ts.hbs +142 -0
  177. package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.guard.ts.hbs +77 -0
  178. package/dist/templates/recipes/platform-service-access-request-context/service-access-request-context.module.ts.hbs +17 -0
  179. package/dist/templates/repository/repository.hbs +42 -4
  180. package/dist/templates/resolver/graphql-input.hbs +23 -6
  181. package/dist/templates/resolver/resolver.hbs +4 -0
  182. package/dist/templates/service/domain-service.hbs +2 -2
  183. package/dist/templates/test/controller.spec.hbs +5 -1
  184. package/dist/templates/test/prisma-repository.spec.hbs +192 -0
  185. package/dist/templates/test/repository.spec.hbs +40 -1
  186. package/dist/templates/test/usecase.spec.hbs +23 -4
  187. package/dist/templates/usecase/application-usecase.hbs +17 -0
  188. package/dist/templates/usecase/create-usecase.hbs +34 -3
  189. package/dist/templates/usecase/update-usecase.hbs +15 -1
  190. package/dist/utils/config.utils.d.ts +2 -0
  191. package/dist/utils/config.utils.js +14 -2
  192. package/dist/utils/config.utils.js.map +1 -1
  193. package/dist/utils/dependency.utils.d.ts +16 -2
  194. package/dist/utils/dependency.utils.js +100 -12
  195. package/dist/utils/dependency.utils.js.map +1 -1
  196. package/dist/utils/field.utils.d.ts +6 -0
  197. package/dist/utils/field.utils.js +50 -8
  198. package/dist/utils/field.utils.js.map +1 -1
  199. package/dist/utils/field.utils.spec.d.ts +1 -0
  200. package/dist/utils/field.utils.spec.js +38 -0
  201. package/dist/utils/field.utils.spec.js.map +1 -0
  202. package/dist/utils/file.utils.d.ts +23 -1
  203. package/dist/utils/file.utils.js +37 -1
  204. package/dist/utils/file.utils.js.map +1 -1
  205. package/dist/utils/naming.utils.js +5 -4
  206. package/dist/utils/naming.utils.js.map +1 -1
  207. package/dist/utils/static-recipe.utils.d.ts +4 -0
  208. package/dist/utils/static-recipe.utils.js +148 -0
  209. package/dist/utils/static-recipe.utils.js.map +1 -0
  210. package/package.json +12 -5
@@ -0,0 +1,326 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.generateMigrationDeployment = generateMigrationDeployment;
40
+ const fs = __importStar(require("fs-extra"));
41
+ const path = __importStar(require("node:path"));
42
+ const chalk_1 = __importDefault(require("chalk"));
43
+ const file_utils_1 = require("../utils/file.utils");
44
+ const migration_1 = require("./migration");
45
+ const config_utils_1 = require("../utils/config.utils");
46
+ const DNS_LABEL = /^[a-z0-9](?:[-a-z0-9]*[a-z0-9])?$/;
47
+ const SECRET_KEY = /^[A-Za-z_][A-Za-z0-9_.-]*$/;
48
+ const IMMUTABLE_IMAGE = /^[A-Za-z0-9][A-Za-z0-9._/:-]*@sha256:[a-f0-9]{64}$/;
49
+ async function generateMigrationDeployment(options) {
50
+ const resolved = await resolveOptions(options);
51
+ const runnerPath = path.join(resolved.basePath, 'src', 'migration-runner.ts');
52
+ const jobPath = path.join(resolved.basePath, 'k8s', 'migration-job.yaml');
53
+ await assertSafeOutput(runnerPath, resolved.dryRun);
54
+ await assertSafeOutput(jobPath, resolved.dryRun);
55
+ if (!resolved.dryRun) {
56
+ await (0, file_utils_1.ensureDir)(path.dirname(runnerPath));
57
+ await (0, file_utils_1.ensureDir)(path.dirname(jobPath));
58
+ }
59
+ await (0, file_utils_1.writeGeneratedFile)(runnerPath, renderMigrationRunner(resolved.migrationRuntimeDirectory), resolved.dryRun);
60
+ await (0, file_utils_1.writeGeneratedFile)(jobPath, renderMigrationJob(resolved), resolved.dryRun);
61
+ const verb = resolved.dryRun ? 'Would generate' : 'Generated';
62
+ console.log(chalk_1.default.green(` ✓ ${verb} src/migration-runner.ts`));
63
+ console.log(chalk_1.default.green(` ✓ ${verb} k8s/migration-job.yaml`));
64
+ }
65
+ async function resolveOptions(options) {
66
+ const basePath = path.resolve(options.path ?? process.cwd());
67
+ const packageJsonPath = path.join(basePath, 'package.json');
68
+ const packageJson = (await fs.readJson(packageJsonPath));
69
+ const appName = options.appName ?? (typeof packageJson.name === 'string' ? packageJson.name : undefined);
70
+ if (!appName || !isDnsLabel(appName)) {
71
+ throw new Error('Migration deployment requires a DNS-label-safe --app-name');
72
+ }
73
+ if (!options.image || !IMMUTABLE_IMAGE.test(options.image)) {
74
+ throw new Error('Migration deployment --image must be pinned by sha256 digest');
75
+ }
76
+ const databaseSecret = options.databaseSecret ?? `${appName}-secrets`;
77
+ validateDnsLabel(databaseSecret, '--database-secret');
78
+ const databaseUrlKey = options.databaseUrlKey ?? 'DATABASE_URL';
79
+ validateSecretKey(databaseUrlKey, '--database-url-key');
80
+ const databaseSslKey = options.databaseSslKey ?? 'DATABASE_SSL';
81
+ validateSecretKey(databaseSslKey, '--database-ssl-key');
82
+ if (options.configMap) {
83
+ validateDnsLabel(options.configMap, '--config-map');
84
+ }
85
+ if (options.imagePullSecret) {
86
+ validateDnsLabel(options.imagePullSecret, '--image-pull-secret');
87
+ }
88
+ const databaseSslCaSecret = options.databaseSslCaSecret;
89
+ const databaseSslCaKey = options.databaseSslCaKey ?? 'DATABASE_SSL_CA';
90
+ if (databaseSslCaSecret) {
91
+ validateDnsLabel(databaseSslCaSecret, '--database-ssl-ca-secret');
92
+ validateSecretKey(databaseSslCaKey, '--database-ssl-ca-key');
93
+ }
94
+ else if (options.databaseSslCaKey) {
95
+ throw new Error('--database-ssl-ca-key requires --database-ssl-ca-secret');
96
+ }
97
+ const databaseSslInsecureSkipVerify = options.databaseSslInsecureSkipVerify ?? false;
98
+ if (databaseSslCaSecret && databaseSslInsecureSkipVerify) {
99
+ throw new Error('--database-ssl-ca-secret cannot be combined with --database-ssl-insecure-skip-verify');
100
+ }
101
+ if ((databaseSslCaSecret || databaseSslInsecureSkipVerify) && !options.configMap) {
102
+ throw new Error('--config-map is required when configuring a database CA or insecure TLS opt-in');
103
+ }
104
+ const config = await (0, config_utils_1.loadConfig)(basePath);
105
+ const configuredMigrationPath = config.paths?.migrations;
106
+ if (typeof configuredMigrationPath !== 'string' ||
107
+ configuredMigrationPath.trim().length === 0 ||
108
+ /[\0*?{}[\]]/.test(configuredMigrationPath)) {
109
+ throw new Error('.dddrc.json paths.migrations must be a non-empty directory path');
110
+ }
111
+ const migrationSourceDirectory = (0, migration_1.resolveMigrationOutputPath)(basePath, configuredMigrationPath, 'src/migrations');
112
+ const migrationRuntimeDirectory = resolveCompiledMigrationRuntimeDirectory(basePath, migrationSourceDirectory);
113
+ return {
114
+ basePath,
115
+ appName,
116
+ image: options.image,
117
+ databaseSecret,
118
+ databaseUrlKey,
119
+ configMap: options.configMap,
120
+ databaseSslKey,
121
+ databaseSslCaSecret,
122
+ databaseSslCaKey,
123
+ databaseSslInsecureSkipVerify,
124
+ imagePullSecret: options.imagePullSecret,
125
+ migrationRuntimeDirectory,
126
+ dryRun: options.dryRun ?? false,
127
+ };
128
+ }
129
+ function resolveCompiledMigrationRuntimeDirectory(basePath, migrationSourceDirectory) {
130
+ const sourceRoot = findOwningSourceRoot(migrationSourceDirectory);
131
+ if (!sourceRoot) {
132
+ throw new Error('.dddrc.json paths.migrations must be inside a source directory named "src"');
133
+ }
134
+ const compiledMigrationDirectory = path.join(path.dirname(sourceRoot), 'dist', path.relative(sourceRoot, migrationSourceDirectory));
135
+ const compiledRunnerDirectory = path.join(basePath, 'dist');
136
+ const relativeRuntimeDirectory = path.relative(compiledRunnerDirectory, compiledMigrationDirectory);
137
+ return (relativeRuntimeDirectory || '.').split(path.sep).join('/');
138
+ }
139
+ function findOwningSourceRoot(migrationSourceDirectory) {
140
+ let current = path.resolve(migrationSourceDirectory);
141
+ while (true) {
142
+ if (path.basename(current) === 'src') {
143
+ return current;
144
+ }
145
+ const parent = path.dirname(current);
146
+ if (parent === current) {
147
+ return undefined;
148
+ }
149
+ current = parent;
150
+ }
151
+ }
152
+ function isDnsLabel(value) {
153
+ return value.length <= 63 && DNS_LABEL.test(value);
154
+ }
155
+ function validateDnsLabel(value, optionName) {
156
+ if (!isDnsLabel(value)) {
157
+ throw new Error(`${optionName} must be a valid Kubernetes DNS label`);
158
+ }
159
+ }
160
+ function validateSecretKey(value, optionName) {
161
+ if (value.length > 253 || !SECRET_KEY.test(value)) {
162
+ throw new Error(`${optionName} must be a valid Kubernetes Secret or ConfigMap key`);
163
+ }
164
+ }
165
+ async function assertSafeOutput(filePath, dryRun) {
166
+ if (!dryRun && (await (0, file_utils_1.fileExists)(filePath))) {
167
+ throw new Error(`Refusing to overwrite existing migration deployment file: ${filePath}`);
168
+ }
169
+ }
170
+ function renderMigrationRunner(migrationRuntimeDirectory) {
171
+ const serializedRuntimeDirectory = JSON.stringify(migrationRuntimeDirectory);
172
+ return `import 'reflect-metadata';
173
+ import * as path from 'node:path';
174
+ import { DataSource, DataSourceOptions } from 'typeorm';
175
+
176
+ export type MigrationEnvironment = Readonly<Record<string, string | undefined>>;
177
+
178
+ export function createMigrationDataSourceOptions(
179
+ environment: MigrationEnvironment = process.env,
180
+ ): DataSourceOptions {
181
+ const databaseUrl = environment.DATABASE_URL?.trim();
182
+ if (!databaseUrl) {
183
+ throw new Error('DATABASE_URL is required to run migrations');
184
+ }
185
+
186
+ const sslEnabled = parseBoolean(environment.DATABASE_SSL ?? 'false', 'DATABASE_SSL');
187
+ const insecureSkipVerify = parseBoolean(
188
+ environment.DATABASE_SSL_INSECURE_SKIP_VERIFY ?? 'false',
189
+ 'DATABASE_SSL_INSECURE_SKIP_VERIFY',
190
+ );
191
+ const certificateAuthority = environment.DATABASE_SSL_CA?.trim();
192
+
193
+ if (!sslEnabled && (insecureSkipVerify || certificateAuthority)) {
194
+ throw new Error(
195
+ 'DATABASE_SSL must be true when DATABASE_SSL_INSECURE_SKIP_VERIFY or DATABASE_SSL_CA is set',
196
+ );
197
+ }
198
+ if (insecureSkipVerify && certificateAuthority) {
199
+ throw new Error(
200
+ 'DATABASE_SSL_CA cannot be combined with DATABASE_SSL_INSECURE_SKIP_VERIFY=true',
201
+ );
202
+ }
203
+
204
+ const ssl = sslEnabled
205
+ ? {
206
+ rejectUnauthorized: !insecureSkipVerify,
207
+ ...(certificateAuthority ? { ca: certificateAuthority } : {}),
208
+ }
209
+ : false;
210
+
211
+ return {
212
+ type: 'postgres',
213
+ url: databaseUrl,
214
+ ssl,
215
+ synchronize: false,
216
+ migrationsRun: false,
217
+ migrationsTableName: 'migrations',
218
+ migrations: [path.resolve(__dirname, ${serializedRuntimeDirectory}, '*.js')],
219
+ };
220
+ }
221
+
222
+ export async function runMigrations(dataSource: DataSource): Promise<void> {
223
+ await dataSource.initialize();
224
+ try {
225
+ await dataSource.runMigrations({ transaction: 'all' });
226
+ } finally {
227
+ if (dataSource.isInitialized) {
228
+ await dataSource.destroy();
229
+ }
230
+ }
231
+ }
232
+
233
+ function parseBoolean(value: string, name: string): boolean {
234
+ const normalized = value.trim().toLowerCase();
235
+ if (normalized === 'true') return true;
236
+ if (normalized === 'false') return false;
237
+ throw new Error(\`${'${name}'} must be true or false\`);
238
+ }
239
+
240
+ async function main(): Promise<void> {
241
+ const dataSource = new DataSource(createMigrationDataSourceOptions());
242
+ try {
243
+ await runMigrations(dataSource);
244
+ } catch (error: unknown) {
245
+ const message = error instanceof Error ? error.message : 'Unknown migration failure';
246
+ console.error(\`Migration failed: ${'${message}'}\`);
247
+ process.exitCode = 1;
248
+ }
249
+ }
250
+
251
+ if (require.main === module) {
252
+ void main(); // NOSONAR -- CommonJS executable entrypoints cannot use top-level await.
253
+ }
254
+ `;
255
+ }
256
+ function renderMigrationJob(options) {
257
+ const pullSecret = options.imagePullSecret
258
+ ? ` imagePullSecrets:\n - name: ${options.imagePullSecret}\n`
259
+ : '';
260
+ const sslSource = options.configMap
261
+ ? ` - name: DATABASE_SSL\n valueFrom:\n configMapKeyRef:\n name: ${options.configMap}\n key: ${options.databaseSslKey}\n`
262
+ : ` - name: DATABASE_SSL\n value: 'false'\n`;
263
+ const caSource = options.databaseSslCaSecret
264
+ ? ` - name: DATABASE_SSL_CA\n valueFrom:\n secretKeyRef:\n name: ${options.databaseSslCaSecret}\n key: ${options.databaseSslCaKey}\n`
265
+ : '';
266
+ return `apiVersion: batch/v1
267
+ kind: Job
268
+ metadata:
269
+ name: ${options.appName}-migrate
270
+ labels:
271
+ app: ${options.appName}
272
+ app.kubernetes.io/component: database-migration
273
+ annotations:
274
+ argocd.argoproj.io/hook: PreSync
275
+ argocd.argoproj.io/hook-delete-policy: BeforeHookCreation,HookSucceeded
276
+ spec:
277
+ backoffLimit: 2
278
+ activeDeadlineSeconds: 600
279
+ ttlSecondsAfterFinished: 3600
280
+ template:
281
+ metadata:
282
+ labels:
283
+ app: ${options.appName}
284
+ app.kubernetes.io/component: database-migration
285
+ spec:
286
+ automountServiceAccountToken: false
287
+ restartPolicy: Never
288
+ ${pullSecret} securityContext:
289
+ runAsNonRoot: true
290
+ runAsUser: 1001
291
+ runAsGroup: 1001
292
+ seccompProfile:
293
+ type: RuntimeDefault
294
+ containers:
295
+ - name: migrate
296
+ image: ${options.image}
297
+ imagePullPolicy: IfNotPresent
298
+ command:
299
+ - node
300
+ - dist/migration-runner.js
301
+ env:
302
+ - name: DATABASE_URL
303
+ valueFrom:
304
+ secretKeyRef:
305
+ name: ${options.databaseSecret}
306
+ key: ${options.databaseUrlKey}
307
+ ${sslSource} - name: DATABASE_SSL_INSECURE_SKIP_VERIFY
308
+ value: '${options.databaseSslInsecureSkipVerify ? 'true' : 'false'}'
309
+ ${caSource} securityContext:
310
+ allowPrivilegeEscalation: false
311
+ readOnlyRootFilesystem: true
312
+ capabilities:
313
+ drop:
314
+ - ALL
315
+ resources:
316
+ requests:
317
+ cpu: 10m
318
+ memory: 64Mi
319
+ ephemeral-storage: 16Mi
320
+ limits:
321
+ cpu: 250m
322
+ memory: 256Mi
323
+ ephemeral-storage: 128Mi
324
+ `;
325
+ }
326
+ //# sourceMappingURL=generate-migration-deployment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate-migration-deployment.js","sourceRoot":"","sources":["../../src/commands/generate-migration-deployment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,337 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ const fs = __importStar(require("fs-extra"));
40
+ const os = __importStar(require("node:os"));
41
+ const path = __importStar(require("node:path"));
42
+ const typescript_1 = __importDefault(require("typescript"));
43
+ const yaml_1 = require("yaml");
44
+ const config_utils_1 = require("../utils/config.utils");
45
+ const generate_migration_deployment_1 = require("./generate-migration-deployment");
46
+ async function compileAndLoadRunner(projectPath) {
47
+ const runnerPath = path.join(projectPath, 'src/migration-runner.ts');
48
+ const declarationsPath = path.join(projectPath, 'generated-runtime-types.d.ts');
49
+ const outputPath = path.join(projectPath, 'dist');
50
+ await fs.writeFile(declarationsPath, `declare module 'reflect-metadata';
51
+ declare module 'typeorm' {
52
+ export interface DataSourceOptions {
53
+ migrations?: string[];
54
+ ssl?: false | { rejectUnauthorized: boolean; ca?: string };
55
+ [key: string]: unknown;
56
+ }
57
+
58
+ export class DataSource {
59
+ constructor(options: DataSourceOptions);
60
+ isInitialized: boolean;
61
+ initialize(): Promise<void>;
62
+ runMigrations(options: { transaction: 'all' }): Promise<void>;
63
+ destroy(): Promise<void>;
64
+ }
65
+ }
66
+ `, 'utf8');
67
+ const program = typescript_1.default.createProgram({
68
+ rootNames: [declarationsPath, runnerPath],
69
+ options: {
70
+ esModuleInterop: true,
71
+ forceConsistentCasingInFileNames: true,
72
+ module: typescript_1.default.ModuleKind.CommonJS,
73
+ moduleResolution: typescript_1.default.ModuleResolutionKind.Node10,
74
+ noEmitOnError: true,
75
+ outDir: outputPath,
76
+ rootDir: path.join(projectPath, 'src'),
77
+ skipLibCheck: true,
78
+ strict: true,
79
+ target: typescript_1.default.ScriptTarget.ES2020,
80
+ typeRoots: [path.resolve(__dirname, '../../node_modules/@types')],
81
+ types: ['node'],
82
+ },
83
+ });
84
+ const emitResult = program.emit();
85
+ const diagnostics = typescript_1.default
86
+ .getPreEmitDiagnostics(program)
87
+ .concat(emitResult.diagnostics)
88
+ .map((diagnostic) => typescript_1.default.flattenDiagnosticMessageText(diagnostic.messageText, typescript_1.default.sys.newLine));
89
+ expect(diagnostics).toEqual([]);
90
+ await fs.outputFile(path.join(projectPath, 'node_modules/typeorm/index.js'), 'class DataSource {}\nmodule.exports = { DataSource };\n', 'utf8');
91
+ await fs.outputFile(path.join(projectPath, 'node_modules/reflect-metadata/index.js'), '', 'utf8');
92
+ return require(path.join(outputPath, 'migration-runner.js'));
93
+ }
94
+ describe('generateMigrationDeployment', () => {
95
+ let projectPath;
96
+ beforeEach(async () => {
97
+ (0, config_utils_1.resetConfigCache)();
98
+ projectPath = await fs.mkdtemp(path.join(os.tmpdir(), 'ddd-migration-deployment-'));
99
+ await fs.writeJson(path.join(projectPath, 'package.json'), { name: 'identity-api' });
100
+ });
101
+ afterEach(async () => {
102
+ (0, config_utils_1.resetConfigCache)();
103
+ await fs.remove(projectPath);
104
+ });
105
+ it('generates a loadable secure runner and a valid immutable-image PreSync Job', async () => {
106
+ const digest = 'a'.repeat(64);
107
+ await (0, generate_migration_deployment_1.generateMigrationDeployment)({
108
+ path: projectPath,
109
+ image: `registry.example/identity-api@sha256:${digest}`,
110
+ databaseSecret: 'identity-api-secrets',
111
+ configMap: 'identity-api-config',
112
+ imagePullSecret: 'registry-pull',
113
+ });
114
+ const runner = await compileAndLoadRunner(projectPath);
115
+ const migrationOptions = runner.createMigrationDataSourceOptions({
116
+ DATABASE_URL: 'postgres://localhost/identity',
117
+ DATABASE_SSL: 'true',
118
+ });
119
+ const job = (0, yaml_1.parse)(await fs.readFile(path.join(projectPath, 'k8s/migration-job.yaml'), 'utf8'));
120
+ const canonicalProjectPath = await fs.realpath(projectPath);
121
+ expect(migrationOptions.migrations).toEqual([
122
+ path.join(canonicalProjectPath, 'dist/migrations/*.js'),
123
+ ]);
124
+ expect(migrationOptions.ssl).toEqual({ rejectUnauthorized: true });
125
+ const migrationCalls = [];
126
+ const dataSource = {
127
+ isInitialized: false,
128
+ async initialize() {
129
+ this.isInitialized = true;
130
+ migrationCalls.push('initialize');
131
+ },
132
+ async runMigrations(options) {
133
+ migrationCalls.push(`run:${options.transaction}`);
134
+ },
135
+ async destroy() {
136
+ this.isInitialized = false;
137
+ migrationCalls.push('destroy');
138
+ },
139
+ };
140
+ await runner.runMigrations(dataSource);
141
+ expect(migrationCalls).toEqual(['initialize', 'run:all', 'destroy']);
142
+ expect(job).toMatchObject({
143
+ apiVersion: 'batch/v1',
144
+ kind: 'Job',
145
+ metadata: {
146
+ name: 'identity-api-migrate',
147
+ annotations: {
148
+ 'argocd.argoproj.io/hook': 'PreSync',
149
+ 'argocd.argoproj.io/hook-delete-policy': 'BeforeHookCreation,HookSucceeded',
150
+ },
151
+ },
152
+ spec: {
153
+ template: {
154
+ spec: {
155
+ automountServiceAccountToken: false,
156
+ restartPolicy: 'Never',
157
+ securityContext: {
158
+ runAsNonRoot: true,
159
+ runAsUser: 1001,
160
+ runAsGroup: 1001,
161
+ seccompProfile: { type: 'RuntimeDefault' },
162
+ },
163
+ imagePullSecrets: [{ name: 'registry-pull' }],
164
+ containers: [
165
+ {
166
+ name: 'migrate',
167
+ image: `registry.example/identity-api@sha256:${digest}`,
168
+ command: ['node', 'dist/migration-runner.js'],
169
+ env: expect.arrayContaining([
170
+ {
171
+ name: 'DATABASE_URL',
172
+ valueFrom: {
173
+ secretKeyRef: { name: 'identity-api-secrets', key: 'DATABASE_URL' },
174
+ },
175
+ },
176
+ {
177
+ name: 'DATABASE_SSL',
178
+ valueFrom: {
179
+ configMapKeyRef: { name: 'identity-api-config', key: 'DATABASE_SSL' },
180
+ },
181
+ },
182
+ { name: 'DATABASE_SSL_INSECURE_SKIP_VERIFY', value: 'false' },
183
+ ]),
184
+ },
185
+ ],
186
+ },
187
+ },
188
+ },
189
+ });
190
+ });
191
+ it('uses the configured compiled migration directory inside an application', async () => {
192
+ await fs.writeJson(path.join(projectPath, '.dddrc.json'), {
193
+ paths: { migrations: 'src/database/migrations' },
194
+ });
195
+ await (0, generate_migration_deployment_1.generateMigrationDeployment)({
196
+ path: projectPath,
197
+ image: `registry.example/identity-api@sha256:${'b'.repeat(64)}`,
198
+ });
199
+ const runner = await compileAndLoadRunner(projectPath);
200
+ const options = runner.createMigrationDataSourceOptions({
201
+ DATABASE_URL: 'postgres://localhost/identity',
202
+ });
203
+ const canonicalProjectPath = await fs.realpath(projectPath);
204
+ expect(options.migrations).toEqual([
205
+ path.join(canonicalProjectPath, 'dist/database/migrations/*.js'),
206
+ ]);
207
+ });
208
+ it('uses the owning package dist directory for monorepo-shared migrations', async () => {
209
+ const workspacePath = projectPath;
210
+ const appPath = path.join(workspacePath, 'apps/identity-api');
211
+ await fs.ensureDir(path.join(workspacePath, '.git'));
212
+ await fs.ensureDir(path.join(workspacePath, 'packages/identity-storage/src/migrations'));
213
+ await fs.ensureDir(appPath);
214
+ await fs.writeJson(path.join(appPath, 'package.json'), { name: 'identity-api' });
215
+ await fs.writeJson(path.join(appPath, '.dddrc.json'), {
216
+ paths: { migrations: '../../packages/identity-storage/src/migrations' },
217
+ });
218
+ await (0, generate_migration_deployment_1.generateMigrationDeployment)({
219
+ path: appPath,
220
+ image: `registry.example/identity-api@sha256:${'c'.repeat(64)}`,
221
+ });
222
+ const runner = await compileAndLoadRunner(appPath);
223
+ const options = runner.createMigrationDataSourceOptions({
224
+ DATABASE_URL: 'postgres://localhost/identity',
225
+ });
226
+ const canonicalWorkspacePath = await fs.realpath(workspacePath);
227
+ expect(options.migrations).toEqual([
228
+ path.join(canonicalWorkspacePath, 'packages/identity-storage/dist/migrations/*.js'),
229
+ ]);
230
+ });
231
+ it('keeps TLS verification on, supports a trusted CA, and gates insecure TLS explicitly', async () => {
232
+ await (0, generate_migration_deployment_1.generateMigrationDeployment)({
233
+ path: projectPath,
234
+ image: `registry.example/identity-api@sha256:${'d'.repeat(64)}`,
235
+ configMap: 'identity-api-config',
236
+ databaseSslCaSecret: 'identity-database-ca',
237
+ });
238
+ const runner = await compileAndLoadRunner(projectPath);
239
+ const databaseUrl = 'postgres://localhost/identity';
240
+ expect(runner.createMigrationDataSourceOptions({
241
+ DATABASE_URL: databaseUrl,
242
+ DATABASE_SSL: 'true',
243
+ DATABASE_SSL_CA: 'trusted-ca-pem',
244
+ }).ssl).toEqual({ rejectUnauthorized: true, ca: 'trusted-ca-pem' });
245
+ expect(runner.createMigrationDataSourceOptions({
246
+ DATABASE_URL: databaseUrl,
247
+ DATABASE_SSL: 'true',
248
+ DATABASE_SSL_INSECURE_SKIP_VERIFY: 'true',
249
+ }).ssl).toEqual({ rejectUnauthorized: false });
250
+ expect(() => runner.createMigrationDataSourceOptions({
251
+ DATABASE_URL: databaseUrl,
252
+ DATABASE_SSL: 'false',
253
+ DATABASE_SSL_INSECURE_SKIP_VERIFY: 'true',
254
+ })).toThrow('DATABASE_SSL must be true');
255
+ expect(() => runner.createMigrationDataSourceOptions({
256
+ DATABASE_URL: databaseUrl,
257
+ DATABASE_SSL: 'true',
258
+ DATABASE_SSL_CA: 'trusted-ca-pem',
259
+ DATABASE_SSL_INSECURE_SKIP_VERIFY: 'true',
260
+ })).toThrow('cannot be combined');
261
+ const job = (0, yaml_1.parse)(await fs.readFile(path.join(projectPath, 'k8s/migration-job.yaml'), 'utf8'));
262
+ expect(job.spec.template.spec.containers[0]?.env).toEqual(expect.arrayContaining([
263
+ {
264
+ name: 'DATABASE_SSL_CA',
265
+ valueFrom: {
266
+ secretKeyRef: { name: 'identity-database-ca', key: 'DATABASE_SSL_CA' },
267
+ },
268
+ },
269
+ ]));
270
+ });
271
+ it('requires an explicit coherent opt-in before generating insecure database TLS', async () => {
272
+ const image = `registry.example/identity-api@sha256:${'3'.repeat(64)}`;
273
+ await expect((0, generate_migration_deployment_1.generateMigrationDeployment)({
274
+ path: projectPath,
275
+ image,
276
+ databaseSslInsecureSkipVerify: true,
277
+ })).rejects.toThrow('--config-map is required');
278
+ await expect((0, generate_migration_deployment_1.generateMigrationDeployment)({
279
+ path: projectPath,
280
+ image,
281
+ configMap: 'identity-api-config',
282
+ databaseSslCaSecret: 'identity-database-ca',
283
+ databaseSslInsecureSkipVerify: true,
284
+ })).rejects.toThrow('cannot be combined');
285
+ await (0, generate_migration_deployment_1.generateMigrationDeployment)({
286
+ path: projectPath,
287
+ image,
288
+ configMap: 'identity-api-config',
289
+ databaseSslInsecureSkipVerify: true,
290
+ });
291
+ const job = (0, yaml_1.parse)(await fs.readFile(path.join(projectPath, 'k8s/migration-job.yaml'), 'utf8'));
292
+ expect(job.spec.template.spec.containers[0]?.env).toEqual(expect.arrayContaining([{ name: 'DATABASE_SSL_INSECURE_SKIP_VERIFY', value: 'true' }]));
293
+ });
294
+ it('rejects invalid or unbuildable configured migration paths', async () => {
295
+ await fs.writeJson(path.join(projectPath, '.dddrc.json'), {
296
+ paths: { migrations: 'migrations' },
297
+ });
298
+ await expect((0, generate_migration_deployment_1.generateMigrationDeployment)({
299
+ path: projectPath,
300
+ image: `registry.example/identity-api@sha256:${'e'.repeat(64)}`,
301
+ })).rejects.toThrow('must be inside a source directory named "src"');
302
+ (0, config_utils_1.resetConfigCache)();
303
+ await fs.writeJson(path.join(projectPath, '.dddrc.json'), {
304
+ paths: { migrations: '' },
305
+ });
306
+ await expect((0, generate_migration_deployment_1.generateMigrationDeployment)({
307
+ path: projectPath,
308
+ image: `registry.example/identity-api@sha256:${'f'.repeat(64)}`,
309
+ })).rejects.toThrow('must be a non-empty directory path');
310
+ });
311
+ it('rejects a mutable image tag', async () => {
312
+ await expect((0, generate_migration_deployment_1.generateMigrationDeployment)({
313
+ path: projectPath,
314
+ image: 'registry.example/identity-api:staging',
315
+ })).rejects.toThrow('must be pinned by sha256 digest');
316
+ });
317
+ it('does not overwrite an existing migration gate', async () => {
318
+ const options = {
319
+ path: projectPath,
320
+ image: `registry.example/identity-api@sha256:${'1'.repeat(64)}`,
321
+ };
322
+ await (0, generate_migration_deployment_1.generateMigrationDeployment)(options);
323
+ await expect((0, generate_migration_deployment_1.generateMigrationDeployment)(options)).rejects.toThrow('Refusing to overwrite');
324
+ });
325
+ it('keeps dry-run non-mutating', async () => {
326
+ await (0, generate_migration_deployment_1.generateMigrationDeployment)({
327
+ path: projectPath,
328
+ image: `registry.example/identity-api@sha256:${'2'.repeat(64)}`,
329
+ dryRun: true,
330
+ });
331
+ await expect(fs.pathExists(path.join(projectPath, 'src/migration-runner.ts'))).resolves.toBe(false);
332
+ await expect(fs.pathExists(path.join(projectPath, 'k8s/migration-job.yaml'))).resolves.toBe(false);
333
+ await expect(fs.pathExists(path.join(projectPath, 'src'))).resolves.toBe(false);
334
+ await expect(fs.pathExists(path.join(projectPath, 'k8s'))).resolves.toBe(false);
335
+ });
336
+ });
337
+ //# sourceMappingURL=generate-migration-deployment.spec.js.map