@solid-stack/mason 1.0.26 → 1.0.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +926 -321
- package/dist/bin.cjs.map +1 -1
- package/dist/bin.js +923 -318
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/commands/env/audit.d.ts +18 -0
- package/dist/commands/env/audit.d.ts.map +1 -0
- package/dist/commands/env/index.d.ts +8 -0
- package/dist/commands/env/index.d.ts.map +1 -0
- package/dist/commands/env/init.d.ts +4 -0
- package/dist/commands/env/init.d.ts.map +1 -0
- package/dist/commands/env/initExamples.d.ts +4 -0
- package/dist/commands/env/initExamples.d.ts.map +1 -0
- package/dist/commands/env/mutate.d.ts +13 -0
- package/dist/commands/env/mutate.d.ts.map +1 -0
- package/dist/commands/env.d.ts +1 -2
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/envExamples.d.ts +1 -2
- package/dist/commands/envExamples.d.ts.map +1 -1
- package/dist/commands/menu.d.ts.map +1 -1
- package/dist/index.cjs +862 -244
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +850 -243
- package/dist/index.js.map +1 -1
- package/dist/templates/domain/class.hbs +5 -5
- package/dist/templates/domain/interface.hbs +1 -1
- package/dist/templates/domain/valueObject.hbs +11 -11
- package/dist/templates/httpHandler/handler.hbs +34 -34
- package/dist/templates/infra/concrete.hbs +8 -8
- package/dist/templates/infra/stub.hbs +76 -76
- package/dist/templates/injectable/injectable.hbs +11 -11
- package/dist/templates/injectable/interface.hbs +1 -1
- package/dist/templates/interface/interface.hbs +7 -7
- package/dist/templates/provider/provider.hbs +1 -1
- package/dist/templates/useCase/useCase.hbs +13 -13
- package/dist/templates/useCase/useCaseTest.hbs +10 -10
- package/dist/utils/envManager.d.ts +23 -0
- package/dist/utils/envManager.d.ts.map +1 -0
- package/package.json +6 -2
- package/templates/domain/class.hbs +5 -5
- package/templates/domain/interface.hbs +1 -1
- package/templates/domain/valueObject.hbs +11 -11
- package/templates/httpHandler/handler.hbs +34 -34
- package/templates/infra/concrete.hbs +8 -8
- package/templates/infra/stub.hbs +76 -76
- package/templates/injectable/injectable.hbs +11 -11
- package/templates/injectable/interface.hbs +1 -1
- package/templates/interface/interface.hbs +7 -7
- package/templates/provider/provider.hbs +1 -1
- package/templates/useCase/useCase.hbs +13 -13
- package/templates/useCase/useCaseTest.hbs +10 -10
package/dist/index.cjs
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var fs4 = require('fs');
|
|
4
4
|
var path2 = require('path');
|
|
5
|
-
var
|
|
6
|
-
var
|
|
5
|
+
var p19 = require('@clack/prompts');
|
|
6
|
+
var pc14 = require('picocolors');
|
|
7
7
|
var url = require('url');
|
|
8
8
|
var child_process = require('child_process');
|
|
9
9
|
var crypto = require('crypto');
|
|
@@ -33,8 +33,8 @@ function _interopNamespace(e) {
|
|
|
33
33
|
|
|
34
34
|
var fs4__default = /*#__PURE__*/_interopDefault(fs4);
|
|
35
35
|
var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
36
|
-
var
|
|
37
|
-
var
|
|
36
|
+
var p19__namespace = /*#__PURE__*/_interopNamespace(p19);
|
|
37
|
+
var pc14__default = /*#__PURE__*/_interopDefault(pc14);
|
|
38
38
|
var Handlebars__default = /*#__PURE__*/_interopDefault(Handlebars);
|
|
39
39
|
|
|
40
40
|
// src/utils/formatting.ts
|
|
@@ -141,17 +141,17 @@ function writeFileSafely(absolutePath, content, options = {}) {
|
|
|
141
141
|
function printGeneratedFiles(files, isDryRun = false) {
|
|
142
142
|
console.log("");
|
|
143
143
|
if (isDryRun) {
|
|
144
|
-
|
|
144
|
+
p19__namespace.log.warn(pc14__default.default.yellow(pc14__default.default.bold("[DRY RUN PREVIEW] No files were written to disk.")));
|
|
145
145
|
}
|
|
146
146
|
for (const f of files) {
|
|
147
147
|
if (f.action === "created") {
|
|
148
|
-
|
|
148
|
+
p19__namespace.log.success(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(created)")}`);
|
|
149
149
|
} else if (f.action === "overwritten") {
|
|
150
|
-
|
|
150
|
+
p19__namespace.log.warn(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(overwritten)")}`);
|
|
151
151
|
} else if (f.action === "skipped") {
|
|
152
|
-
|
|
152
|
+
p19__namespace.log.info(`${pc14__default.default.dim(f.relativePath)} ${pc14__default.default.dim("(skipped - already exists, pass -o to overwrite)")}`);
|
|
153
153
|
} else {
|
|
154
|
-
|
|
154
|
+
p19__namespace.log.info(`${pc14__default.default.bold(f.relativePath)} ${pc14__default.default.dim("(preview)")}`);
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
}
|
|
@@ -353,23 +353,23 @@ var PromptCancelledError = class extends Error {
|
|
|
353
353
|
}
|
|
354
354
|
};
|
|
355
355
|
function handleCancel(result) {
|
|
356
|
-
if (
|
|
357
|
-
|
|
356
|
+
if (p19__namespace.isCancel(result)) {
|
|
357
|
+
p19__namespace.cancel("Operation cancelled.");
|
|
358
358
|
throw new PromptCancelledError();
|
|
359
359
|
}
|
|
360
360
|
return result;
|
|
361
361
|
}
|
|
362
362
|
function printBanner() {
|
|
363
363
|
console.log("");
|
|
364
|
-
|
|
364
|
+
p19__namespace.intro(pc14__default.default.bgCyan(pc14__default.default.black(" @solid-stack/mason ")));
|
|
365
365
|
}
|
|
366
366
|
async function promptProperties(options = {}) {
|
|
367
367
|
const properties = options.defaults ? [...options.defaults] : [];
|
|
368
368
|
if (properties.length > 0) {
|
|
369
|
-
const summary = properties.map((
|
|
370
|
-
|
|
369
|
+
const summary = properties.map((p22) => `${p22.name}: ${p22.type}${p22.optional ? "?" : ""}`).join(", ");
|
|
370
|
+
p19__namespace.log.info(pc14__default.default.dim(`Pre-configured fields: ${summary}`));
|
|
371
371
|
const addMore = handleCancel(
|
|
372
|
-
await
|
|
372
|
+
await p19__namespace.confirm({
|
|
373
373
|
message: "Would you like to add custom fields/properties?",
|
|
374
374
|
initialValue: false
|
|
375
375
|
})
|
|
@@ -378,10 +378,10 @@ async function promptProperties(options = {}) {
|
|
|
378
378
|
return properties;
|
|
379
379
|
}
|
|
380
380
|
}
|
|
381
|
-
|
|
381
|
+
p19__namespace.log.info(pc14__default.default.cyan("Enter properties (leave name blank when finished):"));
|
|
382
382
|
while (true) {
|
|
383
383
|
const name = handleCancel(
|
|
384
|
-
await
|
|
384
|
+
await p19__namespace.text({
|
|
385
385
|
message: "Property name (or press Enter to finish):",
|
|
386
386
|
placeholder: "e.g. title, email, status"
|
|
387
387
|
})
|
|
@@ -390,7 +390,7 @@ async function promptProperties(options = {}) {
|
|
|
390
390
|
break;
|
|
391
391
|
}
|
|
392
392
|
const type = handleCancel(
|
|
393
|
-
await
|
|
393
|
+
await p19__namespace.select({
|
|
394
394
|
message: `Type for property "${name.trim()}":`,
|
|
395
395
|
options: [
|
|
396
396
|
{ value: "string", label: "string" },
|
|
@@ -407,7 +407,7 @@ async function promptProperties(options = {}) {
|
|
|
407
407
|
let finalType = type;
|
|
408
408
|
if (type === "custom") {
|
|
409
409
|
finalType = handleCancel(
|
|
410
|
-
await
|
|
410
|
+
await p19__namespace.text({
|
|
411
411
|
message: `Enter custom type for "${name.trim()}":`,
|
|
412
412
|
placeholder: "e.g. UserRole, string | null",
|
|
413
413
|
validate: (val) => val && val.trim() ? void 0 : "Type cannot be empty"
|
|
@@ -415,7 +415,7 @@ async function promptProperties(options = {}) {
|
|
|
415
415
|
);
|
|
416
416
|
}
|
|
417
417
|
const optional = handleCancel(
|
|
418
|
-
await
|
|
418
|
+
await p19__namespace.confirm({
|
|
419
419
|
message: `Is "${name.trim()}" optional?`,
|
|
420
420
|
initialValue: false
|
|
421
421
|
})
|
|
@@ -425,8 +425,8 @@ async function promptProperties(options = {}) {
|
|
|
425
425
|
type: finalType.trim(),
|
|
426
426
|
optional
|
|
427
427
|
});
|
|
428
|
-
|
|
429
|
-
|
|
428
|
+
p19__namespace.log.success(
|
|
429
|
+
pc14__default.default.green(`Added: ${name.trim()}${optional ? "?" : ""}: ${finalType.trim()}`)
|
|
430
430
|
);
|
|
431
431
|
}
|
|
432
432
|
return properties;
|
|
@@ -634,8 +634,8 @@ function getMissingNpmPackages(projectRoot, requiredPackages) {
|
|
|
634
634
|
...Object.keys(pkg.devDependencies || {}),
|
|
635
635
|
...Object.keys(pkg.peerDependencies || {})
|
|
636
636
|
]);
|
|
637
|
-
return requiredPackages.filter((
|
|
638
|
-
const nameOnly =
|
|
637
|
+
return requiredPackages.filter((p22) => {
|
|
638
|
+
const nameOnly = p22.startsWith("@") ? `@${p22.slice(1).split("@")[0] || ""}` : p22.split("@")[0] || p22;
|
|
639
639
|
return !installed.has(nameOnly);
|
|
640
640
|
});
|
|
641
641
|
} catch {
|
|
@@ -660,7 +660,7 @@ function installNpmPackages(projectRoot, packages, pm) {
|
|
|
660
660
|
installCmd = `npm install ${packages.join(" ")}`;
|
|
661
661
|
break;
|
|
662
662
|
}
|
|
663
|
-
|
|
663
|
+
p19__namespace.log.info(`Installing missing dependencies with ${manager}: ${pc14__default.default.cyan(packages.join(", "))}`);
|
|
664
664
|
child_process.execSync(installCmd, {
|
|
665
665
|
cwd: projectRoot,
|
|
666
666
|
stdio: "inherit"
|
|
@@ -862,7 +862,7 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
|
|
|
862
862
|
const idKey = `${entityKey}Id`;
|
|
863
863
|
const listKey = `${entityKey}s`;
|
|
864
864
|
const formattedCustomMethods = customMethods.map((m) => {
|
|
865
|
-
const params = m.params && m.params.length > 0 ? m.params.map((
|
|
865
|
+
const params = m.params && m.params.length > 0 ? m.params.map((p22) => `${p22.name}: ${p22.type}`).join("; ") : "";
|
|
866
866
|
const returnType = m.returnType ? `Promise<${m.returnType}>` : "Promise<void>";
|
|
867
867
|
return {
|
|
868
868
|
name: m.name,
|
|
@@ -1443,22 +1443,22 @@ function generateUseCaseCode(useCaseName, dependencies = [], inputProps = [], ou
|
|
|
1443
1443
|
from: /^I[A-Z]/.test(dep) ? `../domain/${dep}.js` : `./${dep}.js`
|
|
1444
1444
|
}));
|
|
1445
1445
|
const domainTypesToImport = new Set(domainImports);
|
|
1446
|
-
for (const
|
|
1447
|
-
const rawType =
|
|
1446
|
+
for (const p22 of [...inputProps, ...outputProps]) {
|
|
1447
|
+
const rawType = p22.type.trim();
|
|
1448
1448
|
const baseType = rawType.replace(/\[\]$/, "").trim();
|
|
1449
1449
|
if (!primitiveTypes.has(baseType) && /^[A-Z][A-Za-z0-9_]*$/.test(baseType) && !dependencies.includes(baseType)) {
|
|
1450
1450
|
domainTypesToImport.add(baseType);
|
|
1451
1451
|
}
|
|
1452
1452
|
}
|
|
1453
|
-
const formattedInputProps = inputProps.map((
|
|
1454
|
-
name:
|
|
1455
|
-
optional:
|
|
1456
|
-
type:
|
|
1453
|
+
const formattedInputProps = inputProps.map((p22) => ({
|
|
1454
|
+
name: p22.name,
|
|
1455
|
+
optional: p22.optional,
|
|
1456
|
+
type: p22.optional && !p22.type.includes("undefined") ? `${p22.type} | undefined` : p22.type
|
|
1457
1457
|
}));
|
|
1458
|
-
const formattedOutputProps = outputProps.map((
|
|
1459
|
-
name:
|
|
1460
|
-
optional:
|
|
1461
|
-
type:
|
|
1458
|
+
const formattedOutputProps = outputProps.map((p22) => ({
|
|
1459
|
+
name: p22.name,
|
|
1460
|
+
optional: p22.optional,
|
|
1461
|
+
type: p22.optional && !p22.type.includes("undefined") ? `${p22.type} | undefined` : p22.type
|
|
1462
1462
|
}));
|
|
1463
1463
|
const formattedDeps = dependencies.map((dep) => ({
|
|
1464
1464
|
key: toCamelCase(dep),
|
|
@@ -2133,7 +2133,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2133
2133
|
}
|
|
2134
2134
|
if (!name) {
|
|
2135
2135
|
name = handleCancel(
|
|
2136
|
-
await
|
|
2136
|
+
await p19__namespace.text({
|
|
2137
2137
|
message: "What is the feature name?",
|
|
2138
2138
|
placeholder: "e.g. users, auth, orders, billing",
|
|
2139
2139
|
validate: (val) => {
|
|
@@ -2149,7 +2149,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2149
2149
|
let targetDirRel = defaultDir;
|
|
2150
2150
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2151
2151
|
targetDirRel = handleCancel(
|
|
2152
|
-
await
|
|
2152
|
+
await p19__namespace.text({
|
|
2153
2153
|
message: "Target directory for the feature:",
|
|
2154
2154
|
initialValue: defaultDir,
|
|
2155
2155
|
defaultValue: defaultDir
|
|
@@ -2171,14 +2171,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2171
2171
|
const customUseCases = [];
|
|
2172
2172
|
if (!flags.yes) {
|
|
2173
2173
|
includeExpress = handleCancel(
|
|
2174
|
-
await
|
|
2174
|
+
await p19__namespace.confirm({
|
|
2175
2175
|
message: "Include Express Presentation layer (pxExpress routes & handlers)?",
|
|
2176
2176
|
initialValue: true
|
|
2177
2177
|
})
|
|
2178
2178
|
);
|
|
2179
2179
|
if (includeExpress) {
|
|
2180
2180
|
routePath = handleCancel(
|
|
2181
|
-
await
|
|
2181
|
+
await p19__namespace.text({
|
|
2182
2182
|
message: "Express route prefix:",
|
|
2183
2183
|
initialValue: `/${normalizedName}`,
|
|
2184
2184
|
defaultValue: `/${normalizedName}`
|
|
@@ -2186,14 +2186,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2186
2186
|
).trim();
|
|
2187
2187
|
}
|
|
2188
2188
|
includeDomain = handleCancel(
|
|
2189
|
-
await
|
|
2189
|
+
await p19__namespace.confirm({
|
|
2190
2190
|
message: "Include Domain Entity/Entities?",
|
|
2191
2191
|
initialValue: true
|
|
2192
2192
|
})
|
|
2193
2193
|
);
|
|
2194
2194
|
if (includeDomain) {
|
|
2195
2195
|
const domainInput = handleCancel(
|
|
2196
|
-
await
|
|
2196
|
+
await p19__namespace.text({
|
|
2197
2197
|
message: "Domain entity name(s) (comma-separated):",
|
|
2198
2198
|
initialValue: defaultEntity,
|
|
2199
2199
|
defaultValue: defaultEntity,
|
|
@@ -2212,21 +2212,21 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2212
2212
|
entityNames = [defaultEntity];
|
|
2213
2213
|
}
|
|
2214
2214
|
includeInterface = handleCancel(
|
|
2215
|
-
await
|
|
2215
|
+
await p19__namespace.confirm({
|
|
2216
2216
|
message: `Include Domain Repository interface(s) for ${entityNames.join(", ")}?`,
|
|
2217
2217
|
initialValue: true
|
|
2218
2218
|
})
|
|
2219
2219
|
);
|
|
2220
2220
|
if (includeInterface) {
|
|
2221
2221
|
includeInfra = handleCancel(
|
|
2222
|
-
await
|
|
2222
|
+
await p19__namespace.confirm({
|
|
2223
2223
|
message: `Include Infrastructure Stub implementation(s) for ${entityNames.join(", ")}?`,
|
|
2224
2224
|
initialValue: true
|
|
2225
2225
|
})
|
|
2226
2226
|
);
|
|
2227
2227
|
}
|
|
2228
2228
|
includeUseCase = handleCancel(
|
|
2229
|
-
await
|
|
2229
|
+
await p19__namespace.confirm({
|
|
2230
2230
|
message: "Include Domain Use Cases?",
|
|
2231
2231
|
initialValue: true
|
|
2232
2232
|
})
|
|
@@ -2240,7 +2240,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2240
2240
|
{ value: "delete", label: "Delete (Delete<Entity>)", hint: "DELETE /:id" }
|
|
2241
2241
|
];
|
|
2242
2242
|
crudOperations = handleCancel(
|
|
2243
|
-
await
|
|
2243
|
+
await p19__namespace.multiselect({
|
|
2244
2244
|
message: "Select standard CRUD use cases to generate:",
|
|
2245
2245
|
options: crudChoices,
|
|
2246
2246
|
initialValues: ["create", "read", "list", "update", "delete"],
|
|
@@ -2248,13 +2248,13 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2248
2248
|
})
|
|
2249
2249
|
);
|
|
2250
2250
|
includeTests = handleCancel(
|
|
2251
|
-
await
|
|
2251
|
+
await p19__namespace.confirm({
|
|
2252
2252
|
message: "Generate Vitest unit tests for use cases?",
|
|
2253
2253
|
initialValue: true
|
|
2254
2254
|
})
|
|
2255
2255
|
);
|
|
2256
2256
|
const wantCustom = handleCancel(
|
|
2257
|
-
await
|
|
2257
|
+
await p19__namespace.confirm({
|
|
2258
2258
|
message: "Do you want to add any custom use cases to this feature?",
|
|
2259
2259
|
initialValue: false
|
|
2260
2260
|
})
|
|
@@ -2264,7 +2264,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2264
2264
|
const availableIfaces = entityNames.map((e) => `I${e}Repository`);
|
|
2265
2265
|
while (addMore) {
|
|
2266
2266
|
const customName = handleCancel(
|
|
2267
|
-
await
|
|
2267
|
+
await p19__namespace.text({
|
|
2268
2268
|
message: "Custom Use Case name:",
|
|
2269
2269
|
placeholder: "e.g. ResetPassword, VerifyEmail, AssignRole",
|
|
2270
2270
|
validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
|
|
@@ -2274,7 +2274,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2274
2274
|
let selectedDeps = [];
|
|
2275
2275
|
if (availableIfaces.length > 0) {
|
|
2276
2276
|
selectedDeps = handleCancel(
|
|
2277
|
-
await
|
|
2277
|
+
await p19__namespace.multiselect({
|
|
2278
2278
|
message: `Select domain interfaces to inject into ${customUcName}:`,
|
|
2279
2279
|
options: availableIfaces.map((i) => ({ value: i, label: i })),
|
|
2280
2280
|
required: false
|
|
@@ -2283,7 +2283,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2283
2283
|
}
|
|
2284
2284
|
let customInputProps = [];
|
|
2285
2285
|
const hasInput = handleCancel(
|
|
2286
|
-
await
|
|
2286
|
+
await p19__namespace.confirm({
|
|
2287
2287
|
message: `Does ${customUcName} accept input arguments?`,
|
|
2288
2288
|
initialValue: true
|
|
2289
2289
|
})
|
|
@@ -2294,7 +2294,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2294
2294
|
}
|
|
2295
2295
|
let customOutputProps = [];
|
|
2296
2296
|
const hasCustomOutput = handleCancel(
|
|
2297
|
-
await
|
|
2297
|
+
await p19__namespace.confirm({
|
|
2298
2298
|
message: `Does ${customUcName} return custom output data?`,
|
|
2299
2299
|
initialValue: false
|
|
2300
2300
|
})
|
|
@@ -2304,7 +2304,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2304
2304
|
});
|
|
2305
2305
|
}
|
|
2306
2306
|
const customIncludeTest = handleCancel(
|
|
2307
|
-
await
|
|
2307
|
+
await p19__namespace.confirm({
|
|
2308
2308
|
message: `Generate Vitest unit test for ${customUcName}?`,
|
|
2309
2309
|
initialValue: true
|
|
2310
2310
|
})
|
|
@@ -2314,14 +2314,14 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2314
2314
|
let customHttpPath = `/${toKebabCase(customUcName)}`;
|
|
2315
2315
|
if (includeExpress) {
|
|
2316
2316
|
customCreateHttp = handleCancel(
|
|
2317
|
-
await
|
|
2317
|
+
await p19__namespace.confirm({
|
|
2318
2318
|
message: `Generate a matching ExpressRoute HTTP handler for ${customUcName}?`,
|
|
2319
2319
|
initialValue: true
|
|
2320
2320
|
})
|
|
2321
2321
|
);
|
|
2322
2322
|
if (customCreateHttp) {
|
|
2323
2323
|
customHttpMethod = handleCancel(
|
|
2324
|
-
await
|
|
2324
|
+
await p19__namespace.select({
|
|
2325
2325
|
message: "HTTP method for route:",
|
|
2326
2326
|
options: [
|
|
2327
2327
|
{ value: "post", label: "POST" },
|
|
@@ -2334,7 +2334,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2334
2334
|
})
|
|
2335
2335
|
);
|
|
2336
2336
|
customHttpPath = handleCancel(
|
|
2337
|
-
await
|
|
2337
|
+
await p19__namespace.text({
|
|
2338
2338
|
message: "Route subpath (relative to feature prefix):",
|
|
2339
2339
|
initialValue: `/${toKebabCase(customUcName)}`,
|
|
2340
2340
|
defaultValue: `/${toKebabCase(customUcName)}`
|
|
@@ -2355,7 +2355,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2355
2355
|
validationFields: customInputProps
|
|
2356
2356
|
});
|
|
2357
2357
|
addMore = handleCancel(
|
|
2358
|
-
await
|
|
2358
|
+
await p19__namespace.confirm({
|
|
2359
2359
|
message: "Add another custom use case?",
|
|
2360
2360
|
initialValue: false
|
|
2361
2361
|
})
|
|
@@ -2364,7 +2364,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2364
2364
|
}
|
|
2365
2365
|
}
|
|
2366
2366
|
includeDiProvider = handleCancel(
|
|
2367
|
-
await
|
|
2367
|
+
await p19__namespace.confirm({
|
|
2368
2368
|
message: "Generate / update feature DI provider (diProvider.ts)?",
|
|
2369
2369
|
initialValue: true
|
|
2370
2370
|
})
|
|
@@ -2376,8 +2376,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2376
2376
|
entityNames = [defaultEntity];
|
|
2377
2377
|
}
|
|
2378
2378
|
}
|
|
2379
|
-
const s =
|
|
2380
|
-
s.start(`Generating feature '${
|
|
2379
|
+
const s = p19__namespace.spinner();
|
|
2380
|
+
s.start(`Generating feature '${pc14__default.default.cyan(normalizedName)}'...`);
|
|
2381
2381
|
const options = {
|
|
2382
2382
|
name: normalizedName,
|
|
2383
2383
|
projectRoot,
|
|
@@ -2423,8 +2423,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
|
|
|
2423
2423
|
}
|
|
2424
2424
|
s.stop("Feature scaffolded successfully!");
|
|
2425
2425
|
printGeneratedFiles(files, options.dryRun);
|
|
2426
|
-
|
|
2427
|
-
|
|
2426
|
+
p19__namespace.outro(
|
|
2427
|
+
pc14__default.default.green(
|
|
2428
2428
|
`\u2713 Feature '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2429
2429
|
)
|
|
2430
2430
|
);
|
|
@@ -2538,7 +2538,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2538
2538
|
}
|
|
2539
2539
|
if (!name) {
|
|
2540
2540
|
name = handleCancel(
|
|
2541
|
-
await
|
|
2541
|
+
await p19__namespace.text({
|
|
2542
2542
|
message: "What is the shared module name?",
|
|
2543
2543
|
placeholder: "e.g. hasher, time, uuid, cache, logger",
|
|
2544
2544
|
validate: (val) => {
|
|
@@ -2554,7 +2554,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2554
2554
|
let targetDirRel = defaultDir;
|
|
2555
2555
|
if (!flags.yes && !flags.target && !flags.dest) {
|
|
2556
2556
|
targetDirRel = handleCancel(
|
|
2557
|
-
await
|
|
2557
|
+
await p19__namespace.text({
|
|
2558
2558
|
message: "Target directory for the shared module:",
|
|
2559
2559
|
initialValue: defaultDir,
|
|
2560
2560
|
defaultValue: defaultDir
|
|
@@ -2566,21 +2566,21 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2566
2566
|
let includeStub = true;
|
|
2567
2567
|
if (!flags.yes) {
|
|
2568
2568
|
description = handleCancel(
|
|
2569
|
-
await
|
|
2569
|
+
await p19__namespace.text({
|
|
2570
2570
|
message: "Module description (optional):",
|
|
2571
2571
|
placeholder: "e.g. In-memory caching and Redis port abstraction",
|
|
2572
2572
|
initialValue: ""
|
|
2573
2573
|
})
|
|
2574
2574
|
).trim();
|
|
2575
2575
|
includeStub = handleCancel(
|
|
2576
|
-
await
|
|
2576
|
+
await p19__namespace.confirm({
|
|
2577
2577
|
message: "Include stub implementation in infrastructure/?",
|
|
2578
2578
|
initialValue: true
|
|
2579
2579
|
})
|
|
2580
2580
|
);
|
|
2581
2581
|
}
|
|
2582
|
-
const s =
|
|
2583
|
-
s.start(`Generating shared module '${
|
|
2582
|
+
const s = p19__namespace.spinner();
|
|
2583
|
+
s.start(`Generating shared module '${pc14__default.default.cyan(normalizedName)}'...`);
|
|
2584
2584
|
const files = generateShared({
|
|
2585
2585
|
name: normalizedName,
|
|
2586
2586
|
description,
|
|
@@ -2593,8 +2593,8 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
|
|
|
2593
2593
|
});
|
|
2594
2594
|
s.stop("Shared module generated successfully!");
|
|
2595
2595
|
printGeneratedFiles(files, Boolean(flags.dryRun));
|
|
2596
|
-
|
|
2597
|
-
|
|
2596
|
+
p19__namespace.outro(
|
|
2597
|
+
pc14__default.default.green(
|
|
2598
2598
|
`\u2713 Shared module '${normalizedName}' initialized in ${path2__default.default.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
|
|
2599
2599
|
)
|
|
2600
2600
|
);
|
|
@@ -2608,14 +2608,14 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2608
2608
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
2609
2609
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
2610
2610
|
const selected = handleCancel(
|
|
2611
|
-
await
|
|
2611
|
+
await p19__namespace.select({
|
|
2612
2612
|
message: "Select target feature:",
|
|
2613
2613
|
options: featureOptions
|
|
2614
2614
|
})
|
|
2615
2615
|
);
|
|
2616
2616
|
if (selected === "__custom__") {
|
|
2617
2617
|
feature = handleCancel(
|
|
2618
|
-
await
|
|
2618
|
+
await p19__namespace.text({
|
|
2619
2619
|
message: "Enter feature name:",
|
|
2620
2620
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2621
2621
|
})
|
|
@@ -2625,7 +2625,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2625
2625
|
}
|
|
2626
2626
|
} else {
|
|
2627
2627
|
feature = handleCancel(
|
|
2628
|
-
await
|
|
2628
|
+
await p19__namespace.text({
|
|
2629
2629
|
message: "Enter target feature name:",
|
|
2630
2630
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2631
2631
|
})
|
|
@@ -2636,7 +2636,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2636
2636
|
let name = useCaseNameArg?.trim();
|
|
2637
2637
|
if (!name) {
|
|
2638
2638
|
name = handleCancel(
|
|
2639
|
-
await
|
|
2639
|
+
await p19__namespace.text({
|
|
2640
2640
|
message: "Use Case name:",
|
|
2641
2641
|
placeholder: "e.g. CreateUser, GetUser, CancelOrder",
|
|
2642
2642
|
validate: (v) => v && v.trim() ? void 0 : "Use Case name cannot be empty"
|
|
@@ -2652,7 +2652,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2652
2652
|
label: i.name
|
|
2653
2653
|
}));
|
|
2654
2654
|
selectedDeps = handleCancel(
|
|
2655
|
-
await
|
|
2655
|
+
await p19__namespace.multiselect({
|
|
2656
2656
|
message: "Select domain interfaces to inject into use case:",
|
|
2657
2657
|
options: multiOptions,
|
|
2658
2658
|
required: false
|
|
@@ -2667,7 +2667,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2667
2667
|
let httpPath = "/";
|
|
2668
2668
|
if (!flags.yes) {
|
|
2669
2669
|
const hasInput = handleCancel(
|
|
2670
|
-
await
|
|
2670
|
+
await p19__namespace.confirm({
|
|
2671
2671
|
message: "Does this use case accept input arguments?",
|
|
2672
2672
|
initialValue: true
|
|
2673
2673
|
})
|
|
@@ -2677,7 +2677,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2677
2677
|
});
|
|
2678
2678
|
}
|
|
2679
2679
|
const hasCustomOutput = handleCancel(
|
|
2680
|
-
await
|
|
2680
|
+
await p19__namespace.confirm({
|
|
2681
2681
|
message: "Does this use case return custom output data?",
|
|
2682
2682
|
initialValue: false
|
|
2683
2683
|
})
|
|
@@ -2687,20 +2687,20 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2687
2687
|
});
|
|
2688
2688
|
}
|
|
2689
2689
|
includeTest = handleCancel(
|
|
2690
|
-
await
|
|
2690
|
+
await p19__namespace.confirm({
|
|
2691
2691
|
message: "Generate Vitest unit test skeleton (.test.ts)?",
|
|
2692
2692
|
initialValue: true
|
|
2693
2693
|
})
|
|
2694
2694
|
);
|
|
2695
2695
|
createHttp = handleCancel(
|
|
2696
|
-
await
|
|
2696
|
+
await p19__namespace.confirm({
|
|
2697
2697
|
message: "Generate a matching ExpressRoute HTTP handler?",
|
|
2698
2698
|
initialValue: false
|
|
2699
2699
|
})
|
|
2700
2700
|
);
|
|
2701
2701
|
if (createHttp) {
|
|
2702
2702
|
httpMethod = handleCancel(
|
|
2703
|
-
await
|
|
2703
|
+
await p19__namespace.select({
|
|
2704
2704
|
message: "HTTP method for route:",
|
|
2705
2705
|
options: [
|
|
2706
2706
|
{ value: "post", label: "POST" },
|
|
@@ -2713,7 +2713,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2713
2713
|
})
|
|
2714
2714
|
);
|
|
2715
2715
|
httpPath = handleCancel(
|
|
2716
|
-
await
|
|
2716
|
+
await p19__namespace.text({
|
|
2717
2717
|
message: "Route subpath (relative to feature prefix):",
|
|
2718
2718
|
initialValue: "/",
|
|
2719
2719
|
defaultValue: "/"
|
|
@@ -2721,8 +2721,8 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2721
2721
|
).trim();
|
|
2722
2722
|
}
|
|
2723
2723
|
}
|
|
2724
|
-
const s =
|
|
2725
|
-
s.start(`Generating use case '${
|
|
2724
|
+
const s = p19__namespace.spinner();
|
|
2725
|
+
s.start(`Generating use case '${pc14__default.default.cyan(ucName)}'...`);
|
|
2726
2726
|
const options = {
|
|
2727
2727
|
name: ucName,
|
|
2728
2728
|
feature,
|
|
@@ -2754,7 +2754,7 @@ async function runUseCaseCommand(useCaseNameArg, flags = {}) {
|
|
|
2754
2754
|
}
|
|
2755
2755
|
s.stop("Use case generated successfully!");
|
|
2756
2756
|
printGeneratedFiles(files, options.dryRun);
|
|
2757
|
-
|
|
2757
|
+
p19__namespace.outro(pc14__default.default.green(`Use case '${ucName}' created in feature '${feature}'!`));
|
|
2758
2758
|
}
|
|
2759
2759
|
async function runDomainCommand(domainNameArg, flags = {}) {
|
|
2760
2760
|
const projectRoot = findProjectRoot();
|
|
@@ -2765,14 +2765,14 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2765
2765
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
2766
2766
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
2767
2767
|
const selected = handleCancel(
|
|
2768
|
-
await
|
|
2768
|
+
await p19__namespace.select({
|
|
2769
2769
|
message: "Select target feature:",
|
|
2770
2770
|
options: featureOptions
|
|
2771
2771
|
})
|
|
2772
2772
|
);
|
|
2773
2773
|
if (selected === "__custom__") {
|
|
2774
2774
|
feature = handleCancel(
|
|
2775
|
-
await
|
|
2775
|
+
await p19__namespace.text({
|
|
2776
2776
|
message: "Enter feature name:",
|
|
2777
2777
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2778
2778
|
})
|
|
@@ -2782,7 +2782,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2782
2782
|
}
|
|
2783
2783
|
} else {
|
|
2784
2784
|
feature = handleCancel(
|
|
2785
|
-
await
|
|
2785
|
+
await p19__namespace.text({
|
|
2786
2786
|
message: "Enter target feature name:",
|
|
2787
2787
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2788
2788
|
})
|
|
@@ -2793,7 +2793,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2793
2793
|
let name = domainNameArg?.trim();
|
|
2794
2794
|
if (!name) {
|
|
2795
2795
|
name = handleCancel(
|
|
2796
|
-
await
|
|
2796
|
+
await p19__namespace.text({
|
|
2797
2797
|
message: "Domain model / entity name:",
|
|
2798
2798
|
placeholder: "e.g. User, Order, Product, Address",
|
|
2799
2799
|
validate: (v) => v && v.trim() ? void 0 : "Domain name cannot be empty"
|
|
@@ -2805,7 +2805,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2805
2805
|
let properties = [];
|
|
2806
2806
|
if (!flags.yes) {
|
|
2807
2807
|
kind = handleCancel(
|
|
2808
|
-
await
|
|
2808
|
+
await p19__namespace.select({
|
|
2809
2809
|
message: "Select domain model type:",
|
|
2810
2810
|
options: [
|
|
2811
2811
|
{ value: "interface", label: "TypeScript Interface (pure data structure)" },
|
|
@@ -2824,8 +2824,8 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2824
2824
|
defaults: kind === "valueObject" ? [{ name: "value", type: "string" }] : defaultProps
|
|
2825
2825
|
});
|
|
2826
2826
|
}
|
|
2827
|
-
const s =
|
|
2828
|
-
s.start(`Generating domain model '${
|
|
2827
|
+
const s = p19__namespace.spinner();
|
|
2828
|
+
s.start(`Generating domain model '${pc14__default.default.cyan(entityName)}'...`);
|
|
2829
2829
|
const options = {
|
|
2830
2830
|
name: entityName,
|
|
2831
2831
|
feature,
|
|
@@ -2838,7 +2838,7 @@ async function runDomainCommand(domainNameArg, flags = {}) {
|
|
|
2838
2838
|
const file = generateDomain(options);
|
|
2839
2839
|
s.stop("Domain model generated successfully!");
|
|
2840
2840
|
printGeneratedFiles([file], options.dryRun);
|
|
2841
|
-
|
|
2841
|
+
p19__namespace.outro(pc14__default.default.green(`Domain model '${entityName}' created in feature '${feature}'!`));
|
|
2842
2842
|
}
|
|
2843
2843
|
async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
2844
2844
|
const projectRoot = findProjectRoot();
|
|
@@ -2847,7 +2847,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2847
2847
|
let feature = flags.feature?.trim();
|
|
2848
2848
|
if (!flags.yes && !flags.global && !flags.feature) {
|
|
2849
2849
|
scope = handleCancel(
|
|
2850
|
-
await
|
|
2850
|
+
await p19__namespace.select({
|
|
2851
2851
|
message: "Where should this infrastructure implementation be placed?",
|
|
2852
2852
|
options: [
|
|
2853
2853
|
{ value: "feature", label: "Feature-level (features/<feature>/infrastructure)" },
|
|
@@ -2862,14 +2862,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2862
2862
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
2863
2863
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
2864
2864
|
const selected = handleCancel(
|
|
2865
|
-
await
|
|
2865
|
+
await p19__namespace.select({
|
|
2866
2866
|
message: "Select target feature:",
|
|
2867
2867
|
options: featureOptions
|
|
2868
2868
|
})
|
|
2869
2869
|
);
|
|
2870
2870
|
if (selected === "__custom__") {
|
|
2871
2871
|
feature = handleCancel(
|
|
2872
|
-
await
|
|
2872
|
+
await p19__namespace.text({
|
|
2873
2873
|
message: "Enter feature name:",
|
|
2874
2874
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2875
2875
|
})
|
|
@@ -2879,7 +2879,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2879
2879
|
}
|
|
2880
2880
|
} else {
|
|
2881
2881
|
feature = handleCancel(
|
|
2882
|
-
await
|
|
2882
|
+
await p19__namespace.text({
|
|
2883
2883
|
message: "Enter target feature name:",
|
|
2884
2884
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
2885
2885
|
})
|
|
@@ -2892,7 +2892,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2892
2892
|
let name = classNameArg?.trim();
|
|
2893
2893
|
if (!name) {
|
|
2894
2894
|
name = handleCancel(
|
|
2895
|
-
await
|
|
2895
|
+
await p19__namespace.text({
|
|
2896
2896
|
message: "Infrastructure class name:",
|
|
2897
2897
|
placeholder: "e.g. StubUserRepository, PostgresUserRepository, S3FileStorage",
|
|
2898
2898
|
validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
|
|
@@ -2911,7 +2911,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2911
2911
|
(i) => className.toLowerCase().includes(removeInterfacePrefix(i.name).toLowerCase())
|
|
2912
2912
|
);
|
|
2913
2913
|
const ifaceChoice = handleCancel(
|
|
2914
|
-
await
|
|
2914
|
+
await p19__namespace.select({
|
|
2915
2915
|
message: "Does this class implement a domain interface or abstract class?",
|
|
2916
2916
|
options: [
|
|
2917
2917
|
...interfaces.map((i) => ({ value: i.name, label: i.name })),
|
|
@@ -2923,7 +2923,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2923
2923
|
);
|
|
2924
2924
|
if (ifaceChoice === "__custom__") {
|
|
2925
2925
|
implementsInterface = handleCancel(
|
|
2926
|
-
await
|
|
2926
|
+
await p19__namespace.text({
|
|
2927
2927
|
message: "Enter interface or abstract class name:",
|
|
2928
2928
|
placeholder: "e.g. IUserRepository, BaseStorageAdapter",
|
|
2929
2929
|
validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
|
|
@@ -2934,14 +2934,14 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2934
2934
|
}
|
|
2935
2935
|
} else {
|
|
2936
2936
|
const wantImpl = handleCancel(
|
|
2937
|
-
await
|
|
2937
|
+
await p19__namespace.confirm({
|
|
2938
2938
|
message: "Does this class implement a domain interface or abstract class?",
|
|
2939
2939
|
initialValue: false
|
|
2940
2940
|
})
|
|
2941
2941
|
);
|
|
2942
2942
|
if (wantImpl) {
|
|
2943
2943
|
implementsInterface = handleCancel(
|
|
2944
|
-
await
|
|
2944
|
+
await p19__namespace.text({
|
|
2945
2945
|
message: "Enter interface or abstract class name:",
|
|
2946
2946
|
placeholder: "e.g. IUserRepository, BaseStorageAdapter",
|
|
2947
2947
|
validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
|
|
@@ -2961,7 +2961,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2961
2961
|
const allInterfaces = listDomainInterfaces(projectRoot);
|
|
2962
2962
|
if (allInterfaces.length > 0) {
|
|
2963
2963
|
const ifaceChoice = handleCancel(
|
|
2964
|
-
await
|
|
2964
|
+
await p19__namespace.select({
|
|
2965
2965
|
message: "Does this class implement an interface or abstract class?",
|
|
2966
2966
|
options: [
|
|
2967
2967
|
...allInterfaces.map((i) => ({ value: i.name, label: `${i.name} (${i.feature})` })),
|
|
@@ -2973,7 +2973,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2973
2973
|
);
|
|
2974
2974
|
if (ifaceChoice === "__custom__") {
|
|
2975
2975
|
implementsInterface = handleCancel(
|
|
2976
|
-
await
|
|
2976
|
+
await p19__namespace.text({
|
|
2977
2977
|
message: "Enter interface or abstract class name:",
|
|
2978
2978
|
placeholder: "e.g. IAuthRepository",
|
|
2979
2979
|
validate: (v) => v && v.trim() ? void 0 : "Name cannot be empty"
|
|
@@ -2986,7 +2986,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2986
2986
|
}
|
|
2987
2987
|
if (!flags.yes && !flags.kind) {
|
|
2988
2988
|
kind = handleCancel(
|
|
2989
|
-
await
|
|
2989
|
+
await p19__namespace.select({
|
|
2990
2990
|
message: "Select implementation kind:",
|
|
2991
2991
|
options: [
|
|
2992
2992
|
{ value: "stub", label: "In-Memory Stub (with in-memory store and test helper methods)" },
|
|
@@ -2997,7 +2997,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
2997
2997
|
);
|
|
2998
2998
|
if (scope === "feature" && feature && implementsInterface) {
|
|
2999
2999
|
registerInProvider = handleCancel(
|
|
3000
|
-
await
|
|
3000
|
+
await p19__namespace.confirm({
|
|
3001
3001
|
message: "Register this implementation in feature diProvider.ts?",
|
|
3002
3002
|
initialValue: true
|
|
3003
3003
|
})
|
|
@@ -3006,8 +3006,8 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
3006
3006
|
} else if (flags.yes && scope === "feature" && feature && implementsInterface) {
|
|
3007
3007
|
registerInProvider = !flags.noProvider;
|
|
3008
3008
|
}
|
|
3009
|
-
const s =
|
|
3010
|
-
s.start(`Generating infrastructure class '${
|
|
3009
|
+
const s = p19__namespace.spinner();
|
|
3010
|
+
s.start(`Generating infrastructure class '${pc14__default.default.cyan(className)}'...`);
|
|
3011
3011
|
const files = [];
|
|
3012
3012
|
const options = {
|
|
3013
3013
|
name: className,
|
|
@@ -3042,7 +3042,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
|
|
|
3042
3042
|
}
|
|
3043
3043
|
s.stop("Infrastructure class generated successfully!");
|
|
3044
3044
|
printGeneratedFiles(files, options.dryRun);
|
|
3045
|
-
|
|
3045
|
+
p19__namespace.outro(pc14__default.default.green(`Infrastructure class '${className}' created!`));
|
|
3046
3046
|
}
|
|
3047
3047
|
async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
3048
3048
|
const projectRoot = findProjectRoot();
|
|
@@ -3053,14 +3053,14 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3053
3053
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
3054
3054
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
3055
3055
|
const selected = handleCancel(
|
|
3056
|
-
await
|
|
3056
|
+
await p19__namespace.select({
|
|
3057
3057
|
message: "Select target feature:",
|
|
3058
3058
|
options: featureOptions
|
|
3059
3059
|
})
|
|
3060
3060
|
);
|
|
3061
3061
|
if (selected === "__custom__") {
|
|
3062
3062
|
feature = handleCancel(
|
|
3063
|
-
await
|
|
3063
|
+
await p19__namespace.text({
|
|
3064
3064
|
message: "Enter feature name:",
|
|
3065
3065
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3066
3066
|
})
|
|
@@ -3070,7 +3070,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3070
3070
|
}
|
|
3071
3071
|
} else {
|
|
3072
3072
|
feature = handleCancel(
|
|
3073
|
-
await
|
|
3073
|
+
await p19__namespace.text({
|
|
3074
3074
|
message: "Enter target feature name:",
|
|
3075
3075
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3076
3076
|
})
|
|
@@ -3081,7 +3081,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3081
3081
|
let name = interfaceNameArg?.trim();
|
|
3082
3082
|
if (!name) {
|
|
3083
3083
|
name = handleCancel(
|
|
3084
|
-
await
|
|
3084
|
+
await p19__namespace.text({
|
|
3085
3085
|
message: "Domain interface name:",
|
|
3086
3086
|
placeholder: "e.g. IUserRepository, IPaymentService",
|
|
3087
3087
|
validate: (v) => v && v.trim() ? void 0 : "Interface name cannot be empty"
|
|
@@ -3099,7 +3099,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3099
3099
|
if (!flags.yes) {
|
|
3100
3100
|
if (existingEntities.length > 0) {
|
|
3101
3101
|
const selectedEntity = handleCancel(
|
|
3102
|
-
await
|
|
3102
|
+
await p19__namespace.select({
|
|
3103
3103
|
message: "Select domain entity associated with this interface:",
|
|
3104
3104
|
options: [
|
|
3105
3105
|
...existingEntities.map((e) => ({ value: e.name, label: e.name })),
|
|
@@ -3110,7 +3110,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3110
3110
|
);
|
|
3111
3111
|
if (selectedEntity === "__custom__") {
|
|
3112
3112
|
entityName = handleCancel(
|
|
3113
|
-
await
|
|
3113
|
+
await p19__namespace.text({
|
|
3114
3114
|
message: "Enter entity name:",
|
|
3115
3115
|
defaultValue: removeInterfacePrefix(ifaceName).replace(/Repository$|Service$/, "")
|
|
3116
3116
|
})
|
|
@@ -3120,7 +3120,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3120
3120
|
}
|
|
3121
3121
|
}
|
|
3122
3122
|
kind = handleCancel(
|
|
3123
|
-
await
|
|
3123
|
+
await p19__namespace.select({
|
|
3124
3124
|
message: "Interface definition style:",
|
|
3125
3125
|
options: [
|
|
3126
3126
|
{ value: "abstractClass", label: "Abstract Class (Recommended - acts as DI token & type)" },
|
|
@@ -3130,7 +3130,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3130
3130
|
})
|
|
3131
3131
|
);
|
|
3132
3132
|
methodsPreset = handleCancel(
|
|
3133
|
-
await
|
|
3133
|
+
await p19__namespace.select({
|
|
3134
3134
|
message: "Method signature preset:",
|
|
3135
3135
|
options: [
|
|
3136
3136
|
{ value: "crud", label: "Standard CRUD (GetById, List, Create, Update, Delete)" },
|
|
@@ -3140,22 +3140,22 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3140
3140
|
})
|
|
3141
3141
|
);
|
|
3142
3142
|
generateStub = handleCancel(
|
|
3143
|
-
await
|
|
3143
|
+
await p19__namespace.confirm({
|
|
3144
3144
|
message: "Auto-generate Infrastructure Stub implementation?",
|
|
3145
3145
|
initialValue: true
|
|
3146
3146
|
})
|
|
3147
3147
|
);
|
|
3148
3148
|
if (generateStub) {
|
|
3149
3149
|
registerInProvider = handleCancel(
|
|
3150
|
-
await
|
|
3150
|
+
await p19__namespace.confirm({
|
|
3151
3151
|
message: "Auto-register interface & stub in feature diProvider.ts?",
|
|
3152
3152
|
initialValue: true
|
|
3153
3153
|
})
|
|
3154
3154
|
);
|
|
3155
3155
|
}
|
|
3156
3156
|
}
|
|
3157
|
-
const s =
|
|
3158
|
-
s.start(`Generating interface '${
|
|
3157
|
+
const s = p19__namespace.spinner();
|
|
3158
|
+
s.start(`Generating interface '${pc14__default.default.cyan(ifaceName)}'...`);
|
|
3159
3159
|
const files = [];
|
|
3160
3160
|
const options = {
|
|
3161
3161
|
name: ifaceName,
|
|
@@ -3204,7 +3204,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
|
|
|
3204
3204
|
}
|
|
3205
3205
|
s.stop("Interface generated successfully!");
|
|
3206
3206
|
printGeneratedFiles(files, options.dryRun);
|
|
3207
|
-
|
|
3207
|
+
p19__namespace.outro(pc14__default.default.green(`Interface '${ifaceName}' created in feature '${feature}'!`));
|
|
3208
3208
|
}
|
|
3209
3209
|
async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
3210
3210
|
const projectRoot = findProjectRoot();
|
|
@@ -3215,14 +3215,14 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3215
3215
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
3216
3216
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
3217
3217
|
const selected = handleCancel(
|
|
3218
|
-
await
|
|
3218
|
+
await p19__namespace.select({
|
|
3219
3219
|
message: "Select target feature:",
|
|
3220
3220
|
options: featureOptions
|
|
3221
3221
|
})
|
|
3222
3222
|
);
|
|
3223
3223
|
if (selected === "__custom__") {
|
|
3224
3224
|
feature = handleCancel(
|
|
3225
|
-
await
|
|
3225
|
+
await p19__namespace.text({
|
|
3226
3226
|
message: "Enter feature name:",
|
|
3227
3227
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3228
3228
|
})
|
|
@@ -3232,7 +3232,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3232
3232
|
}
|
|
3233
3233
|
} else {
|
|
3234
3234
|
feature = handleCancel(
|
|
3235
|
-
await
|
|
3235
|
+
await p19__namespace.text({
|
|
3236
3236
|
message: "Enter target feature name:",
|
|
3237
3237
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3238
3238
|
})
|
|
@@ -3243,7 +3243,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3243
3243
|
let name = handlerNameArg?.trim();
|
|
3244
3244
|
if (!name) {
|
|
3245
3245
|
name = handleCancel(
|
|
3246
|
-
await
|
|
3246
|
+
await p19__namespace.text({
|
|
3247
3247
|
message: "HTTP Route Handler class name:",
|
|
3248
3248
|
placeholder: "e.g. CreateUserHttp, GetUserHttp, HealthHttp",
|
|
3249
3249
|
validate: (v) => v && v.trim() ? void 0 : "Handler name cannot be empty"
|
|
@@ -3262,7 +3262,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3262
3262
|
let validationFields = [];
|
|
3263
3263
|
if (!flags.yes) {
|
|
3264
3264
|
method = handleCancel(
|
|
3265
|
-
await
|
|
3265
|
+
await p19__namespace.select({
|
|
3266
3266
|
message: "HTTP Method:",
|
|
3267
3267
|
options: [
|
|
3268
3268
|
{ value: "get", label: "GET" },
|
|
@@ -3276,7 +3276,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3276
3276
|
);
|
|
3277
3277
|
const defaultPath = method === "get" ? "/" : "/";
|
|
3278
3278
|
subPath = handleCancel(
|
|
3279
|
-
await
|
|
3279
|
+
await p19__namespace.text({
|
|
3280
3280
|
message: "Route subpath (relative to feature prefix):",
|
|
3281
3281
|
initialValue: defaultPath,
|
|
3282
3282
|
defaultValue: defaultPath
|
|
@@ -3287,7 +3287,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3287
3287
|
const ucOptions = useCases.map((u) => ({ value: u.name, label: u.name }));
|
|
3288
3288
|
ucOptions.push({ value: "__none__", label: "None / Standalone Handler" });
|
|
3289
3289
|
const chosenUc = handleCancel(
|
|
3290
|
-
await
|
|
3290
|
+
await p19__namespace.select({
|
|
3291
3291
|
message: "Connect this handler to a domain Use Case?",
|
|
3292
3292
|
options: ucOptions,
|
|
3293
3293
|
initialValue: ucOptions[0]?.value
|
|
@@ -3298,7 +3298,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3298
3298
|
}
|
|
3299
3299
|
}
|
|
3300
3300
|
includeValidation = handleCancel(
|
|
3301
|
-
await
|
|
3301
|
+
await p19__namespace.confirm({
|
|
3302
3302
|
message: "Include Zod inbound request validation?",
|
|
3303
3303
|
initialValue: method !== "delete"
|
|
3304
3304
|
})
|
|
@@ -3306,7 +3306,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3306
3306
|
if (includeValidation) {
|
|
3307
3307
|
const defaultSource = method === "get" ? "query" : "body";
|
|
3308
3308
|
validationSource = handleCancel(
|
|
3309
|
-
await
|
|
3309
|
+
await p19__namespace.select({
|
|
3310
3310
|
message: "Validation data source:",
|
|
3311
3311
|
options: [
|
|
3312
3312
|
{ value: "body", label: "req.body (for POST / PUT / PATCH payloads)" },
|
|
@@ -3320,8 +3320,8 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3320
3320
|
});
|
|
3321
3321
|
}
|
|
3322
3322
|
}
|
|
3323
|
-
const s =
|
|
3324
|
-
s.start(`Generating HTTP handler '${
|
|
3323
|
+
const s = p19__namespace.spinner();
|
|
3324
|
+
s.start(`Generating HTTP handler '${pc14__default.default.cyan(className)}'...`);
|
|
3325
3325
|
const options = {
|
|
3326
3326
|
name: className,
|
|
3327
3327
|
feature,
|
|
@@ -3339,7 +3339,7 @@ async function runHttpHandlerCommand(handlerNameArg, flags = {}) {
|
|
|
3339
3339
|
const files = generateHttpHandler(options);
|
|
3340
3340
|
s.stop("HTTP handler generated successfully!");
|
|
3341
3341
|
printGeneratedFiles(files, options.dryRun);
|
|
3342
|
-
|
|
3342
|
+
p19__namespace.outro(pc14__default.default.green(`HTTP handler '${className}' created in feature '${feature}'!`));
|
|
3343
3343
|
}
|
|
3344
3344
|
function generateInjectableCode(className, dependencies = [], methods = [], implementsInterface) {
|
|
3345
3345
|
const formattedDeps = dependencies.map((dep) => ({
|
|
@@ -3349,7 +3349,7 @@ function generateInjectableCode(className, dependencies = [], methods = [], impl
|
|
|
3349
3349
|
const formattedMethods = methods.map((m) => {
|
|
3350
3350
|
const isAsync = m.async !== false;
|
|
3351
3351
|
const retType = m.returnType ? isAsync ? `Promise<${m.returnType}>` : m.returnType : isAsync ? "Promise<void>" : "void";
|
|
3352
|
-
const params = m.params && m.params.length > 0 ? m.params.map((
|
|
3352
|
+
const params = m.params && m.params.length > 0 ? m.params.map((p22) => `${p22.name}: ${p22.type}`).join(", ") : "";
|
|
3353
3353
|
return {
|
|
3354
3354
|
name: m.name,
|
|
3355
3355
|
isAsync,
|
|
@@ -3419,7 +3419,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3419
3419
|
let name = classNameArg?.trim();
|
|
3420
3420
|
if (!name) {
|
|
3421
3421
|
name = handleCancel(
|
|
3422
|
-
await
|
|
3422
|
+
await p19__namespace.text({
|
|
3423
3423
|
message: "Injectable class name:",
|
|
3424
3424
|
placeholder: "e.g. PasswordHasher, TokenService, UserTransformer",
|
|
3425
3425
|
validate: (v) => v && v.trim() ? void 0 : "Class name cannot be empty"
|
|
@@ -3432,7 +3432,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3432
3432
|
let customPath;
|
|
3433
3433
|
if (!flags.yes) {
|
|
3434
3434
|
location = handleCancel(
|
|
3435
|
-
await
|
|
3435
|
+
await p19__namespace.select({
|
|
3436
3436
|
message: "Select target architectural location:",
|
|
3437
3437
|
options: [
|
|
3438
3438
|
{ value: "feature-service", label: "Feature Service (features/<feature>/services/)" },
|
|
@@ -3450,14 +3450,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3450
3450
|
const featureOptions = existingFeatures.map((f) => ({ value: f, label: f }));
|
|
3451
3451
|
featureOptions.push({ value: "__custom__", label: "\u270F\uFE0F Enter custom feature name..." });
|
|
3452
3452
|
const selected = handleCancel(
|
|
3453
|
-
await
|
|
3453
|
+
await p19__namespace.select({
|
|
3454
3454
|
message: "Select target feature:",
|
|
3455
3455
|
options: featureOptions
|
|
3456
3456
|
})
|
|
3457
3457
|
);
|
|
3458
3458
|
if (selected === "__custom__") {
|
|
3459
3459
|
feature = handleCancel(
|
|
3460
|
-
await
|
|
3460
|
+
await p19__namespace.text({
|
|
3461
3461
|
message: "Enter feature name:",
|
|
3462
3462
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3463
3463
|
})
|
|
@@ -3467,7 +3467,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3467
3467
|
}
|
|
3468
3468
|
} else {
|
|
3469
3469
|
feature = handleCancel(
|
|
3470
|
-
await
|
|
3470
|
+
await p19__namespace.text({
|
|
3471
3471
|
message: "Enter target feature name:",
|
|
3472
3472
|
validate: (v) => v && v.trim() ? void 0 : "Feature name cannot be empty"
|
|
3473
3473
|
})
|
|
@@ -3477,7 +3477,7 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3477
3477
|
feature = toCamelCase(feature);
|
|
3478
3478
|
} else if (location === "custom") {
|
|
3479
3479
|
customPath = handleCancel(
|
|
3480
|
-
await
|
|
3480
|
+
await p19__namespace.text({
|
|
3481
3481
|
message: "Enter destination directory path:",
|
|
3482
3482
|
placeholder: "e.g. src/services or lib/utils",
|
|
3483
3483
|
validate: (v) => v && v.trim() ? void 0 : "Path cannot be empty"
|
|
@@ -3488,14 +3488,14 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3488
3488
|
let generateInterface2 = false;
|
|
3489
3489
|
if (!flags.yes) {
|
|
3490
3490
|
generateInterface2 = handleCancel(
|
|
3491
|
-
await
|
|
3491
|
+
await p19__namespace.confirm({
|
|
3492
3492
|
message: `Generate abstract class interface (I${className}) as runtime DI token?`,
|
|
3493
3493
|
initialValue: false
|
|
3494
3494
|
})
|
|
3495
3495
|
);
|
|
3496
3496
|
}
|
|
3497
|
-
const s =
|
|
3498
|
-
s.start(`Generating injectable '${
|
|
3497
|
+
const s = p19__namespace.spinner();
|
|
3498
|
+
s.start(`Generating injectable '${pc14__default.default.cyan(className)}'...`);
|
|
3499
3499
|
const options = {
|
|
3500
3500
|
name: className,
|
|
3501
3501
|
feature,
|
|
@@ -3509,17 +3509,17 @@ async function runInjectableCommand(classNameArg, flags = {}) {
|
|
|
3509
3509
|
const files = generateInjectable(options);
|
|
3510
3510
|
s.stop("Injectable class generated successfully!");
|
|
3511
3511
|
printGeneratedFiles(files, options.dryRun);
|
|
3512
|
-
|
|
3512
|
+
p19__namespace.outro(pc14__default.default.green(`Injectable '${className}' created successfully!`));
|
|
3513
3513
|
}
|
|
3514
|
-
async function
|
|
3514
|
+
async function runEnvInit(flags = {}) {
|
|
3515
3515
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3516
|
-
const s =
|
|
3516
|
+
const s = p19__namespace.spinner();
|
|
3517
3517
|
s.start("Scanning for .example environment files...");
|
|
3518
3518
|
const files = fs4__default.default.readdirSync(projectRoot);
|
|
3519
3519
|
const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
|
|
3520
3520
|
if (exampleFiles.length === 0) {
|
|
3521
3521
|
s.stop("No .example files found.");
|
|
3522
|
-
|
|
3522
|
+
p19__namespace.log.info(pc14__default.default.yellow(`No files matching '.env*.example' were found in ${projectRoot}`));
|
|
3523
3523
|
return [];
|
|
3524
3524
|
}
|
|
3525
3525
|
s.message(`Found ${exampleFiles.length} template(s). Generating...`);
|
|
@@ -3528,28 +3528,141 @@ async function runEnvCommand(flags = {}) {
|
|
|
3528
3528
|
const sourcePath = path2__default.default.join(projectRoot, exampleFile);
|
|
3529
3529
|
const targetFile = exampleFile.replace(/\.example$/, "");
|
|
3530
3530
|
const targetPath = path2__default.default.join(projectRoot, targetFile);
|
|
3531
|
+
let overwrite = flags.overwrite;
|
|
3532
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs4__default.default.existsSync(targetPath)) {
|
|
3533
|
+
overwrite = await p19__namespace.confirm({
|
|
3534
|
+
message: `${targetFile} already exists. Overwrite?`,
|
|
3535
|
+
initialValue: false
|
|
3536
|
+
});
|
|
3537
|
+
if (p19__namespace.isCancel(overwrite)) {
|
|
3538
|
+
p19__namespace.cancel("Operation cancelled.");
|
|
3539
|
+
process.exit(0);
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3531
3542
|
const content = fs4__default.default.readFileSync(sourcePath, "utf-8");
|
|
3532
3543
|
const genFile = writeFileSafely(targetPath, content, {
|
|
3533
3544
|
projectRoot,
|
|
3534
3545
|
dryRun: Boolean(flags.dryRun),
|
|
3535
|
-
overwrite: Boolean(
|
|
3546
|
+
overwrite: Boolean(overwrite)
|
|
3536
3547
|
});
|
|
3537
3548
|
generatedFiles.push(genFile);
|
|
3538
3549
|
}
|
|
3539
3550
|
s.stop("Environment files processed!");
|
|
3540
3551
|
printGeneratedFiles(generatedFiles, Boolean(flags.dryRun));
|
|
3541
|
-
|
|
3552
|
+
p19__namespace.outro(pc14__default.default.green("\u2713 Local environment files are ready!"));
|
|
3542
3553
|
return generatedFiles;
|
|
3543
3554
|
}
|
|
3544
|
-
|
|
3555
|
+
var runEnvCommand = runEnvInit;
|
|
3556
|
+
var EnvEditor = class {
|
|
3557
|
+
lines;
|
|
3558
|
+
filePath;
|
|
3559
|
+
constructor(filePath) {
|
|
3560
|
+
this.filePath = filePath;
|
|
3561
|
+
this.lines = fs4__default.default.existsSync(filePath) ? fs4__default.default.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
|
|
3562
|
+
}
|
|
3563
|
+
getLines() {
|
|
3564
|
+
return [...this.lines];
|
|
3565
|
+
}
|
|
3566
|
+
getKeys() {
|
|
3567
|
+
return this.lines.filter((line) => line.trim() && !line.trim().startsWith("#")).map((line) => {
|
|
3568
|
+
const eqIdx = line.indexOf("=");
|
|
3569
|
+
return eqIdx !== -1 ? line.substring(0, eqIdx).trim() : line.trim();
|
|
3570
|
+
}).filter(Boolean);
|
|
3571
|
+
}
|
|
3572
|
+
getValue(key) {
|
|
3573
|
+
const line = this.lines.find((l) => {
|
|
3574
|
+
const trimmed = l.trim();
|
|
3575
|
+
if (trimmed.startsWith("#") || !trimmed) return false;
|
|
3576
|
+
const eqIdx = l.indexOf("=");
|
|
3577
|
+
const k = eqIdx !== -1 ? l.substring(0, eqIdx).trim() : l.trim();
|
|
3578
|
+
return k === key;
|
|
3579
|
+
});
|
|
3580
|
+
if (!line) return null;
|
|
3581
|
+
const eqIndex = line.indexOf("=");
|
|
3582
|
+
return eqIndex !== -1 ? line.substring(eqIndex + 1).trim() : "";
|
|
3583
|
+
}
|
|
3584
|
+
has(key) {
|
|
3585
|
+
return this.getKeys().includes(key);
|
|
3586
|
+
}
|
|
3587
|
+
set(key, value) {
|
|
3588
|
+
const newLine = `${key}=${value}`;
|
|
3589
|
+
const index = this.lines.findIndex((l) => {
|
|
3590
|
+
const trimmed = l.trim();
|
|
3591
|
+
if (trimmed.startsWith("#") || !trimmed) return false;
|
|
3592
|
+
const eqIdx = l.indexOf("=");
|
|
3593
|
+
const k = eqIdx !== -1 ? l.substring(0, eqIdx).trim() : l.trim();
|
|
3594
|
+
return k === key;
|
|
3595
|
+
});
|
|
3596
|
+
if (index !== -1) {
|
|
3597
|
+
this.lines[index] = newLine;
|
|
3598
|
+
} else {
|
|
3599
|
+
if (this.lines.length > 0 && this.lines[this.lines.length - 1] !== "") {
|
|
3600
|
+
this.lines.push("");
|
|
3601
|
+
}
|
|
3602
|
+
this.lines.push(newLine);
|
|
3603
|
+
}
|
|
3604
|
+
}
|
|
3605
|
+
delete(key) {
|
|
3606
|
+
const initialLen = this.lines.length;
|
|
3607
|
+
this.lines = this.lines.filter((l) => {
|
|
3608
|
+
const trimmed = l.trim();
|
|
3609
|
+
if (trimmed.startsWith("#") || !trimmed) return true;
|
|
3610
|
+
const eqIdx = l.indexOf("=");
|
|
3611
|
+
const k = eqIdx !== -1 ? l.substring(0, eqIdx).trim() : l.trim();
|
|
3612
|
+
return k !== key;
|
|
3613
|
+
});
|
|
3614
|
+
return this.lines.length !== initialLen;
|
|
3615
|
+
}
|
|
3616
|
+
redactAll() {
|
|
3617
|
+
this.lines = this.lines.map((line) => {
|
|
3618
|
+
const trimmed = line.trim();
|
|
3619
|
+
if (trimmed.startsWith("#") || !trimmed) return line;
|
|
3620
|
+
const eqIndex = line.indexOf("=");
|
|
3621
|
+
return eqIndex !== -1 ? line.substring(0, eqIndex + 1) : line;
|
|
3622
|
+
});
|
|
3623
|
+
}
|
|
3624
|
+
save() {
|
|
3625
|
+
const dir = path2__default.default.dirname(this.filePath);
|
|
3626
|
+
if (!fs4__default.default.existsSync(dir)) {
|
|
3627
|
+
fs4__default.default.mkdirSync(dir, { recursive: true });
|
|
3628
|
+
}
|
|
3629
|
+
fs4__default.default.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
|
|
3630
|
+
}
|
|
3631
|
+
};
|
|
3632
|
+
function discoverEnvironments(projectRoot) {
|
|
3633
|
+
if (!fs4__default.default.existsSync(projectRoot)) {
|
|
3634
|
+
return ["local", "dev", "prod"];
|
|
3635
|
+
}
|
|
3636
|
+
const files = fs4__default.default.readdirSync(projectRoot);
|
|
3637
|
+
const envs = /* @__PURE__ */ new Set();
|
|
3638
|
+
for (const file of files) {
|
|
3639
|
+
if (file.startsWith(".env")) {
|
|
3640
|
+
const cleanName = file.replace(/\.example$/, "");
|
|
3641
|
+
const match = cleanName.match(/^\.env\.([a-zA-Z0-9_-]+)$/);
|
|
3642
|
+
if (match && match[1]) {
|
|
3643
|
+
envs.add(match[1]);
|
|
3644
|
+
} else if (cleanName === ".env") {
|
|
3645
|
+
envs.add("local");
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
}
|
|
3649
|
+
return envs.size > 0 ? Array.from(envs) : ["local", "dev", "prod"];
|
|
3650
|
+
}
|
|
3651
|
+
function resolveEnvFilename(envName, isExample = false) {
|
|
3652
|
+
const base = envName === "local" ? ".env" : `.env.${envName}`;
|
|
3653
|
+
return isExample ? `${base}.example` : base;
|
|
3654
|
+
}
|
|
3655
|
+
|
|
3656
|
+
// src/commands/env/initExamples.ts
|
|
3657
|
+
async function runEnvInitExamples(flags = {}) {
|
|
3545
3658
|
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3546
|
-
const s =
|
|
3659
|
+
const s = p19__namespace.spinner();
|
|
3547
3660
|
s.start("Scanning for local environment files...");
|
|
3548
3661
|
const files = fs4__default.default.readdirSync(projectRoot, { withFileTypes: true });
|
|
3549
3662
|
const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
|
|
3550
3663
|
if (envFiles.length === 0) {
|
|
3551
3664
|
s.stop("No local .env files found.");
|
|
3552
|
-
|
|
3665
|
+
p19__namespace.log.info(pc14__default.default.yellow(`No files matching '.env*' were found in ${projectRoot}`));
|
|
3553
3666
|
return [];
|
|
3554
3667
|
}
|
|
3555
3668
|
s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
|
|
@@ -3558,34 +3671,526 @@ async function runEnvExamplesCommand(flags = {}) {
|
|
|
3558
3671
|
const sourcePath = path2__default.default.join(projectRoot, envFile);
|
|
3559
3672
|
const targetFile = `${envFile}.example`;
|
|
3560
3673
|
const targetPath = path2__default.default.join(projectRoot, targetFile);
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3674
|
+
let overwrite = flags.overwrite;
|
|
3675
|
+
if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs4__default.default.existsSync(targetPath)) {
|
|
3676
|
+
overwrite = await p19__namespace.confirm({
|
|
3677
|
+
message: `${targetFile} already exists. Overwrite with redacted values?`,
|
|
3678
|
+
initialValue: true
|
|
3679
|
+
});
|
|
3680
|
+
if (p19__namespace.isCancel(overwrite)) {
|
|
3681
|
+
p19__namespace.cancel("Operation cancelled.");
|
|
3682
|
+
process.exit(0);
|
|
3568
3683
|
}
|
|
3569
|
-
|
|
3570
|
-
|
|
3684
|
+
}
|
|
3685
|
+
const editor = new EnvEditor(sourcePath);
|
|
3686
|
+
editor.redactAll();
|
|
3687
|
+
const redactedContent = editor.getLines().join("\n");
|
|
3571
3688
|
const genFile = writeFileSafely(targetPath, redactedContent, {
|
|
3572
3689
|
projectRoot,
|
|
3573
3690
|
dryRun: Boolean(flags.dryRun),
|
|
3574
|
-
overwrite: Boolean(
|
|
3691
|
+
overwrite: Boolean(overwrite)
|
|
3575
3692
|
});
|
|
3576
3693
|
generatedFiles.push(genFile);
|
|
3577
3694
|
}
|
|
3578
3695
|
s.stop("Environment example templates generated!");
|
|
3579
3696
|
printGeneratedFiles(generatedFiles, Boolean(flags.dryRun));
|
|
3580
|
-
|
|
3697
|
+
p19__namespace.outro(pc14__default.default.green("\u2713 .env*.example files are up to date and safely redacted!"));
|
|
3581
3698
|
return generatedFiles;
|
|
3582
3699
|
}
|
|
3700
|
+
var runEnvExamplesCommand = runEnvInitExamples;
|
|
3701
|
+
function isInteractive(flags) {
|
|
3702
|
+
if (flags.yes || Boolean(flags.nonInteractive)) return false;
|
|
3703
|
+
if (process.env.VITEST) return false;
|
|
3704
|
+
return Boolean(process.stdin.isTTY);
|
|
3705
|
+
}
|
|
3706
|
+
function getAllExistingKeys(projectRoot, envs) {
|
|
3707
|
+
const keys = /* @__PURE__ */ new Set();
|
|
3708
|
+
for (const env of envs) {
|
|
3709
|
+
const localPath = path2__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3710
|
+
const examplePath = path2__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3711
|
+
if (fs4__default.default.existsSync(localPath)) {
|
|
3712
|
+
new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
|
|
3713
|
+
}
|
|
3714
|
+
if (fs4__default.default.existsSync(examplePath)) {
|
|
3715
|
+
new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
return Array.from(keys).sort();
|
|
3719
|
+
}
|
|
3720
|
+
async function runEnvAdd(key, value, flags = {}) {
|
|
3721
|
+
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3722
|
+
const allEnvs = discoverEnvironments(projectRoot);
|
|
3723
|
+
let targetKey = key?.trim();
|
|
3724
|
+
if (!targetKey) {
|
|
3725
|
+
if (!isInteractive(flags)) {
|
|
3726
|
+
throw new Error("Variable key is required for 'mason env add <key> [value]'");
|
|
3727
|
+
}
|
|
3728
|
+
targetKey = handleCancel(
|
|
3729
|
+
await p19__namespace.text({
|
|
3730
|
+
message: "Enter the variable key name:",
|
|
3731
|
+
placeholder: "e.g. DATABASE_URL, STRIPE_KEY, PORT",
|
|
3732
|
+
validate: (val) => {
|
|
3733
|
+
if (!val || !val.trim()) return "Variable key name cannot be empty";
|
|
3734
|
+
if (/\s/.test(val.trim())) return "Variable key cannot contain spaces";
|
|
3735
|
+
if (val.includes("=")) return "Variable key cannot contain '='";
|
|
3736
|
+
return void 0;
|
|
3737
|
+
}
|
|
3738
|
+
})
|
|
3739
|
+
).trim();
|
|
3740
|
+
}
|
|
3741
|
+
let targetEnvs = [];
|
|
3742
|
+
if (flags.env) {
|
|
3743
|
+
targetEnvs = String(flags.env).split(",").map((s) => s.trim()).filter(Boolean);
|
|
3744
|
+
} else if (!isInteractive(flags)) {
|
|
3745
|
+
targetEnvs = allEnvs;
|
|
3746
|
+
} else {
|
|
3747
|
+
targetEnvs = handleCancel(
|
|
3748
|
+
await p19__namespace.multiselect({
|
|
3749
|
+
message: `Which environments should receive ${pc14__default.default.bold(targetKey)}?`,
|
|
3750
|
+
options: allEnvs.map((env) => ({
|
|
3751
|
+
value: env,
|
|
3752
|
+
label: env === "local" ? "local (.env)" : `${env} (.env.${env})`
|
|
3753
|
+
})),
|
|
3754
|
+
initialValues: allEnvs,
|
|
3755
|
+
required: true
|
|
3756
|
+
})
|
|
3757
|
+
);
|
|
3758
|
+
}
|
|
3759
|
+
if (targetEnvs.length === 0) {
|
|
3760
|
+
p19__namespace.log.warn("No environments selected. Nothing was changed.");
|
|
3761
|
+
return { key: targetKey, environments: [] };
|
|
3762
|
+
}
|
|
3763
|
+
for (const env of targetEnvs) {
|
|
3764
|
+
let envVal = value;
|
|
3765
|
+
if (envVal === void 0) {
|
|
3766
|
+
if (!isInteractive(flags)) {
|
|
3767
|
+
envVal = "";
|
|
3768
|
+
} else {
|
|
3769
|
+
envVal = handleCancel(
|
|
3770
|
+
await p19__namespace.text({
|
|
3771
|
+
message: `Value for ${pc14__default.default.cyan(targetKey)} in ${pc14__default.default.bold(env)} (${resolveEnvFilename(env)})?`,
|
|
3772
|
+
initialValue: "",
|
|
3773
|
+
placeholder: "Leave empty for blank value"
|
|
3774
|
+
})
|
|
3775
|
+
);
|
|
3776
|
+
}
|
|
3777
|
+
} else if (isInteractive(flags) && !flags.yes) {
|
|
3778
|
+
envVal = handleCancel(
|
|
3779
|
+
await p19__namespace.text({
|
|
3780
|
+
message: `Value for ${pc14__default.default.cyan(targetKey)} in ${pc14__default.default.bold(env)} (${resolveEnvFilename(env)})?`,
|
|
3781
|
+
initialValue: value ?? "",
|
|
3782
|
+
placeholder: "Leave empty for blank value"
|
|
3783
|
+
})
|
|
3784
|
+
);
|
|
3785
|
+
}
|
|
3786
|
+
const localPath = path2__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3787
|
+
const examplePath = path2__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3788
|
+
if (flags.dryRun) {
|
|
3789
|
+
p19__namespace.log.info(
|
|
3790
|
+
pc14__default.default.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
|
|
3791
|
+
);
|
|
3792
|
+
p19__namespace.log.info(
|
|
3793
|
+
pc14__default.default.cyan(`[DRY RUN] Would add ${targetKey}= to ${resolveEnvFilename(env, true)}`)
|
|
3794
|
+
);
|
|
3795
|
+
} else {
|
|
3796
|
+
const localEditor = new EnvEditor(localPath);
|
|
3797
|
+
localEditor.set(targetKey, envVal);
|
|
3798
|
+
localEditor.save();
|
|
3799
|
+
const exampleEditor = new EnvEditor(examplePath);
|
|
3800
|
+
exampleEditor.set(targetKey, "");
|
|
3801
|
+
exampleEditor.save();
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
if (!flags.dryRun) {
|
|
3805
|
+
p19__namespace.log.success(
|
|
3806
|
+
pc14__default.default.green(
|
|
3807
|
+
`\u2713 Added ${pc14__default.default.bold(targetKey)} to [${targetEnvs.join(", ")}] and updated .example templates.`
|
|
3808
|
+
)
|
|
3809
|
+
);
|
|
3810
|
+
}
|
|
3811
|
+
return { key: targetKey, environments: targetEnvs };
|
|
3812
|
+
}
|
|
3813
|
+
async function runEnvUpdate(key, value, flags = {}) {
|
|
3814
|
+
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3815
|
+
const allEnvs = discoverEnvironments(projectRoot);
|
|
3816
|
+
const existingKeys = getAllExistingKeys(projectRoot, allEnvs);
|
|
3817
|
+
let targetKey = key?.trim();
|
|
3818
|
+
if (!targetKey) {
|
|
3819
|
+
if (!isInteractive(flags)) {
|
|
3820
|
+
throw new Error("Variable key is required for 'mason env update <key> [value]'");
|
|
3821
|
+
}
|
|
3822
|
+
if (existingKeys.length === 0) {
|
|
3823
|
+
p19__namespace.log.error(pc14__default.default.red("No environment variables found to update."));
|
|
3824
|
+
return { key: "", environments: [] };
|
|
3825
|
+
}
|
|
3826
|
+
targetKey = handleCancel(
|
|
3827
|
+
await p19__namespace.select({
|
|
3828
|
+
message: "Select a variable to update:",
|
|
3829
|
+
options: existingKeys.map((k) => ({ value: k, label: k }))
|
|
3830
|
+
})
|
|
3831
|
+
);
|
|
3832
|
+
}
|
|
3833
|
+
const envsWithKey = allEnvs.filter((env) => {
|
|
3834
|
+
const localPath = path2__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3835
|
+
return fs4__default.default.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
|
|
3836
|
+
});
|
|
3837
|
+
let targetEnvs = [];
|
|
3838
|
+
if (flags.env) {
|
|
3839
|
+
targetEnvs = String(flags.env).split(",").map((s) => s.trim()).filter(Boolean);
|
|
3840
|
+
} else if (!isInteractive(flags)) {
|
|
3841
|
+
targetEnvs = envsWithKey.length > 0 ? envsWithKey : allEnvs;
|
|
3842
|
+
} else {
|
|
3843
|
+
targetEnvs = handleCancel(
|
|
3844
|
+
await p19__namespace.multiselect({
|
|
3845
|
+
message: `Which environments should be updated for ${pc14__default.default.bold(targetKey)}?`,
|
|
3846
|
+
options: allEnvs.map((env) => ({
|
|
3847
|
+
value: env,
|
|
3848
|
+
label: env === "local" ? "local (.env)" : `${env} (.env.${env})`,
|
|
3849
|
+
hint: envsWithKey.includes(env) ? "currently exists" : "not set"
|
|
3850
|
+
})),
|
|
3851
|
+
initialValues: envsWithKey.length > 0 ? envsWithKey : allEnvs,
|
|
3852
|
+
required: true
|
|
3853
|
+
})
|
|
3854
|
+
);
|
|
3855
|
+
}
|
|
3856
|
+
if (targetEnvs.length === 0) {
|
|
3857
|
+
p19__namespace.log.warn("No environments selected. Nothing was changed.");
|
|
3858
|
+
return { key: targetKey, environments: [] };
|
|
3859
|
+
}
|
|
3860
|
+
for (const env of targetEnvs) {
|
|
3861
|
+
const localPath = path2__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3862
|
+
const localEditor = new EnvEditor(localPath);
|
|
3863
|
+
const currentVal = localEditor.getValue(targetKey);
|
|
3864
|
+
let newVal = value;
|
|
3865
|
+
if (newVal === void 0) {
|
|
3866
|
+
if (!isInteractive(flags)) {
|
|
3867
|
+
newVal = currentVal ?? "";
|
|
3868
|
+
} else {
|
|
3869
|
+
newVal = handleCancel(
|
|
3870
|
+
await p19__namespace.text({
|
|
3871
|
+
message: `New value for ${pc14__default.default.cyan(targetKey)} in ${pc14__default.default.bold(env)}? (Current: "${currentVal ?? ""}")`,
|
|
3872
|
+
initialValue: currentVal ?? ""
|
|
3873
|
+
})
|
|
3874
|
+
);
|
|
3875
|
+
}
|
|
3876
|
+
} else if (isInteractive(flags) && !flags.yes) {
|
|
3877
|
+
newVal = handleCancel(
|
|
3878
|
+
await p19__namespace.text({
|
|
3879
|
+
message: `New value for ${pc14__default.default.cyan(targetKey)} in ${pc14__default.default.bold(env)}? (Current: "${currentVal ?? ""}")`,
|
|
3880
|
+
initialValue: value ?? currentVal ?? ""
|
|
3881
|
+
})
|
|
3882
|
+
);
|
|
3883
|
+
}
|
|
3884
|
+
if (flags.dryRun) {
|
|
3885
|
+
p19__namespace.log.info(
|
|
3886
|
+
pc14__default.default.cyan(`[DRY RUN] Would update ${targetKey}=${newVal} in ${resolveEnvFilename(env, false)}`)
|
|
3887
|
+
);
|
|
3888
|
+
} else {
|
|
3889
|
+
localEditor.set(targetKey, newVal);
|
|
3890
|
+
localEditor.save();
|
|
3891
|
+
const examplePath = path2__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3892
|
+
const exampleEditor = new EnvEditor(examplePath);
|
|
3893
|
+
if (!exampleEditor.has(targetKey)) {
|
|
3894
|
+
exampleEditor.set(targetKey, "");
|
|
3895
|
+
exampleEditor.save();
|
|
3896
|
+
}
|
|
3897
|
+
}
|
|
3898
|
+
}
|
|
3899
|
+
if (!flags.dryRun) {
|
|
3900
|
+
p19__namespace.log.success(
|
|
3901
|
+
pc14__default.default.green(`\u2713 Updated ${pc14__default.default.bold(targetKey)} in [${targetEnvs.join(", ")}].`)
|
|
3902
|
+
);
|
|
3903
|
+
}
|
|
3904
|
+
return { key: targetKey, environments: targetEnvs };
|
|
3905
|
+
}
|
|
3906
|
+
async function runEnvDelete(key, flags = {}) {
|
|
3907
|
+
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3908
|
+
const allEnvs = discoverEnvironments(projectRoot);
|
|
3909
|
+
const existingKeys = getAllExistingKeys(projectRoot, allEnvs);
|
|
3910
|
+
let targetKey = key?.trim();
|
|
3911
|
+
if (!targetKey) {
|
|
3912
|
+
if (!isInteractive(flags)) {
|
|
3913
|
+
throw new Error("Variable key is required for 'mason env delete <key>'");
|
|
3914
|
+
}
|
|
3915
|
+
if (existingKeys.length === 0) {
|
|
3916
|
+
p19__namespace.log.error(pc14__default.default.red("No environment variables found to delete."));
|
|
3917
|
+
return { key: "", environments: [] };
|
|
3918
|
+
}
|
|
3919
|
+
targetKey = handleCancel(
|
|
3920
|
+
await p19__namespace.select({
|
|
3921
|
+
message: "Select a variable to delete:",
|
|
3922
|
+
options: existingKeys.map((k) => ({ value: k, label: k }))
|
|
3923
|
+
})
|
|
3924
|
+
);
|
|
3925
|
+
}
|
|
3926
|
+
const envsWithKey = allEnvs.filter((env) => {
|
|
3927
|
+
const localPath = path2__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3928
|
+
const examplePath = path2__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3929
|
+
return fs4__default.default.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs4__default.default.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
|
|
3930
|
+
});
|
|
3931
|
+
let targetEnvs = [];
|
|
3932
|
+
if (flags.env) {
|
|
3933
|
+
targetEnvs = String(flags.env).split(",").map((s) => s.trim()).filter(Boolean);
|
|
3934
|
+
} else if (!isInteractive(flags)) {
|
|
3935
|
+
targetEnvs = envsWithKey.length > 0 ? envsWithKey : allEnvs;
|
|
3936
|
+
} else {
|
|
3937
|
+
targetEnvs = handleCancel(
|
|
3938
|
+
await p19__namespace.multiselect({
|
|
3939
|
+
message: `Which environments should remove ${pc14__default.default.bold(targetKey)}?`,
|
|
3940
|
+
options: allEnvs.map((env) => ({
|
|
3941
|
+
value: env,
|
|
3942
|
+
label: env === "local" ? "local (.env)" : `${env} (.env.${env})`,
|
|
3943
|
+
...envsWithKey.includes(env) ? { hint: "currently present" } : {}
|
|
3944
|
+
})),
|
|
3945
|
+
initialValues: envsWithKey.length > 0 ? envsWithKey : allEnvs,
|
|
3946
|
+
required: true
|
|
3947
|
+
})
|
|
3948
|
+
);
|
|
3949
|
+
}
|
|
3950
|
+
if (targetEnvs.length === 0) {
|
|
3951
|
+
p19__namespace.log.warn("No environments selected. Nothing was deleted.");
|
|
3952
|
+
return { key: targetKey, environments: [] };
|
|
3953
|
+
}
|
|
3954
|
+
if (isInteractive(flags) && !flags.yes) {
|
|
3955
|
+
const confirm13 = handleCancel(
|
|
3956
|
+
await p19__namespace.confirm({
|
|
3957
|
+
message: `Are you sure you want to delete ${pc14__default.default.bold(targetKey)} from ${targetEnvs.join(", ")} and their .example templates?`,
|
|
3958
|
+
initialValue: false
|
|
3959
|
+
})
|
|
3960
|
+
);
|
|
3961
|
+
if (!confirm13) {
|
|
3962
|
+
p19__namespace.log.warn("Operation cancelled.");
|
|
3963
|
+
return { key: targetKey, environments: [] };
|
|
3964
|
+
}
|
|
3965
|
+
}
|
|
3966
|
+
for (const env of targetEnvs) {
|
|
3967
|
+
const localPath = path2__default.default.join(projectRoot, resolveEnvFilename(env, false));
|
|
3968
|
+
const examplePath = path2__default.default.join(projectRoot, resolveEnvFilename(env, true));
|
|
3969
|
+
if (flags.dryRun) {
|
|
3970
|
+
p19__namespace.log.info(
|
|
3971
|
+
pc14__default.default.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
|
|
3972
|
+
);
|
|
3973
|
+
} else {
|
|
3974
|
+
if (fs4__default.default.existsSync(localPath)) {
|
|
3975
|
+
const localEditor = new EnvEditor(localPath);
|
|
3976
|
+
localEditor.delete(targetKey);
|
|
3977
|
+
localEditor.save();
|
|
3978
|
+
}
|
|
3979
|
+
if (fs4__default.default.existsSync(examplePath)) {
|
|
3980
|
+
const exampleEditor = new EnvEditor(examplePath);
|
|
3981
|
+
exampleEditor.delete(targetKey);
|
|
3982
|
+
exampleEditor.save();
|
|
3983
|
+
}
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
if (!flags.dryRun) {
|
|
3987
|
+
p19__namespace.log.success(
|
|
3988
|
+
pc14__default.default.green(`\u2713 Deleted ${pc14__default.default.bold(targetKey)} from [${targetEnvs.join(", ")}] and .example templates.`)
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
return { key: targetKey, environments: targetEnvs };
|
|
3992
|
+
}
|
|
3993
|
+
async function runEnvAudit(flags = {}) {
|
|
3994
|
+
const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
|
|
3995
|
+
const envs = discoverEnvironments(projectRoot);
|
|
3996
|
+
p19__namespace.intro(pc14__default.default.bgCyan(pc14__default.default.black(" mason env audit ")));
|
|
3997
|
+
const items = [];
|
|
3998
|
+
let totalIssues = 0;
|
|
3999
|
+
let hasDrift = false;
|
|
4000
|
+
let hasErrors = false;
|
|
4001
|
+
for (const env of envs) {
|
|
4002
|
+
const localFile = resolveEnvFilename(env, false);
|
|
4003
|
+
const exampleFile = resolveEnvFilename(env, true);
|
|
4004
|
+
const localPath = path2__default.default.join(projectRoot, localFile);
|
|
4005
|
+
const examplePath = path2__default.default.join(projectRoot, exampleFile);
|
|
4006
|
+
const localExists = fs4__default.default.existsSync(localPath);
|
|
4007
|
+
const exampleExists = fs4__default.default.existsSync(examplePath);
|
|
4008
|
+
const localEditor = new EnvEditor(localPath);
|
|
4009
|
+
const exampleEditor = new EnvEditor(examplePath);
|
|
4010
|
+
const localKeys = localExists ? localEditor.getKeys() : [];
|
|
4011
|
+
const exampleKeys = exampleExists ? exampleEditor.getKeys() : [];
|
|
4012
|
+
const missingInLocal = exampleKeys.filter((k) => !localKeys.includes(k));
|
|
4013
|
+
const missingInExample = localKeys.filter((k) => !exampleKeys.includes(k));
|
|
4014
|
+
const emptyLocals = localKeys.filter((k) => {
|
|
4015
|
+
const val = localEditor.getValue(k);
|
|
4016
|
+
return val === "" || val === null;
|
|
4017
|
+
});
|
|
4018
|
+
const envIssues = (!localExists ? 1 : 0) + (!exampleExists ? 1 : 0) + missingInLocal.length + missingInExample.length + emptyLocals.length;
|
|
4019
|
+
totalIssues += envIssues;
|
|
4020
|
+
if (missingInExample.length > 0 || !exampleExists) hasDrift = true;
|
|
4021
|
+
if (missingInLocal.length > 0 || !localExists) hasErrors = true;
|
|
4022
|
+
items.push({
|
|
4023
|
+
env,
|
|
4024
|
+
localFile,
|
|
4025
|
+
exampleFile,
|
|
4026
|
+
localExists,
|
|
4027
|
+
exampleExists,
|
|
4028
|
+
missingInLocal,
|
|
4029
|
+
missingInExample,
|
|
4030
|
+
emptyLocals
|
|
4031
|
+
});
|
|
4032
|
+
if (!localExists && !exampleExists) {
|
|
4033
|
+
p19__namespace.log.warn(
|
|
4034
|
+
pc14__default.default.yellow(`\u25B2 ${pc14__default.default.bold(env)}: Neither '${localFile}' nor '${exampleFile}' exist.`)
|
|
4035
|
+
);
|
|
4036
|
+
continue;
|
|
4037
|
+
}
|
|
4038
|
+
if (!localExists) {
|
|
4039
|
+
p19__namespace.log.error(
|
|
4040
|
+
pc14__default.default.red(`\u2717 ${pc14__default.default.bold(env)}: Local file '${localFile}' is missing! Run 'mason env init' to create it.`)
|
|
4041
|
+
);
|
|
4042
|
+
continue;
|
|
4043
|
+
}
|
|
4044
|
+
if (!exampleExists) {
|
|
4045
|
+
p19__namespace.log.warn(
|
|
4046
|
+
pc14__default.default.yellow(`\u25B2 ${pc14__default.default.bold(env)}: Template '${exampleFile}' is missing! Run 'mason env init-examples' to generate it.`)
|
|
4047
|
+
);
|
|
4048
|
+
continue;
|
|
4049
|
+
}
|
|
4050
|
+
if (envIssues === 0) {
|
|
4051
|
+
p19__namespace.log.success(
|
|
4052
|
+
pc14__default.default.green(`\u2713 ${pc14__default.default.bold(env)} (${localFile} \u2194 ${exampleFile}): All ${localKeys.length} keys in sync!`)
|
|
4053
|
+
);
|
|
4054
|
+
} else {
|
|
4055
|
+
p19__namespace.log.step(
|
|
4056
|
+
pc14__default.default.bold(pc14__default.default.cyan(`Environment: ${env} (${localFile} \u2194 ${exampleFile})`))
|
|
4057
|
+
);
|
|
4058
|
+
if (missingInLocal.length > 0) {
|
|
4059
|
+
p19__namespace.log.error(
|
|
4060
|
+
pc14__default.default.red(` \u25CF Missing locally (${missingInLocal.length} in template but missing in ${localFile}):`)
|
|
4061
|
+
);
|
|
4062
|
+
for (const k of missingInLocal) {
|
|
4063
|
+
p19__namespace.log.message(pc14__default.default.red(` - ${k}`));
|
|
4064
|
+
}
|
|
4065
|
+
}
|
|
4066
|
+
if (missingInExample.length > 0) {
|
|
4067
|
+
p19__namespace.log.warn(
|
|
4068
|
+
pc14__default.default.yellow(` \u25CF Template drift (${missingInExample.length} local keys missing in ${exampleFile}):`)
|
|
4069
|
+
);
|
|
4070
|
+
for (const k of missingInExample) {
|
|
4071
|
+
p19__namespace.log.message(pc14__default.default.yellow(` - ${k}`));
|
|
4072
|
+
}
|
|
4073
|
+
}
|
|
4074
|
+
if (emptyLocals.length > 0) {
|
|
4075
|
+
p19__namespace.log.info(
|
|
4076
|
+
pc14__default.default.dim(` \u25B2 Empty values (${emptyLocals.length} defined with blank value in ${localFile}):`)
|
|
4077
|
+
);
|
|
4078
|
+
for (const k of emptyLocals) {
|
|
4079
|
+
p19__namespace.log.message(pc14__default.default.dim(` - ${k}`));
|
|
4080
|
+
}
|
|
4081
|
+
}
|
|
4082
|
+
}
|
|
4083
|
+
}
|
|
4084
|
+
if (totalIssues === 0) {
|
|
4085
|
+
p19__namespace.outro(pc14__default.default.green("\u2713 All environment files are in sync! No drift detected."));
|
|
4086
|
+
} else {
|
|
4087
|
+
p19__namespace.outro(
|
|
4088
|
+
pc14__default.default.yellow(`Found ${totalIssues} environment issue(s) across inspected environments.`)
|
|
4089
|
+
);
|
|
4090
|
+
}
|
|
4091
|
+
return {
|
|
4092
|
+
hasDrift,
|
|
4093
|
+
hasErrors,
|
|
4094
|
+
totalIssues,
|
|
4095
|
+
items
|
|
4096
|
+
};
|
|
4097
|
+
}
|
|
4098
|
+
|
|
4099
|
+
// src/commands/env/index.ts
|
|
4100
|
+
async function runEnvMenu(flags = {}) {
|
|
4101
|
+
const task = handleCancel(
|
|
4102
|
+
await p19__namespace.select({
|
|
4103
|
+
message: "What environment task do you want to run?",
|
|
4104
|
+
options: [
|
|
4105
|
+
{
|
|
4106
|
+
value: "add",
|
|
4107
|
+
label: "Add Variable",
|
|
4108
|
+
hint: "Add a new key across selected environments and sync .example templates"
|
|
4109
|
+
},
|
|
4110
|
+
{
|
|
4111
|
+
value: "update",
|
|
4112
|
+
label: "Update Variable",
|
|
4113
|
+
hint: "Update an existing variable value across environments"
|
|
4114
|
+
},
|
|
4115
|
+
{
|
|
4116
|
+
value: "delete",
|
|
4117
|
+
label: "Delete Variable",
|
|
4118
|
+
hint: "Delete a variable from .env and .example files"
|
|
4119
|
+
},
|
|
4120
|
+
{
|
|
4121
|
+
value: "audit",
|
|
4122
|
+
label: "Audit Drift",
|
|
4123
|
+
hint: "Scan for missing keys, uncommitted secrets, or template drift"
|
|
4124
|
+
},
|
|
4125
|
+
{
|
|
4126
|
+
value: "init",
|
|
4127
|
+
label: "Init Files (.env)",
|
|
4128
|
+
hint: "Clone .env*.example templates to local .env files"
|
|
4129
|
+
},
|
|
4130
|
+
{
|
|
4131
|
+
value: "init-examples",
|
|
4132
|
+
label: "Init Templates (.env.example)",
|
|
4133
|
+
hint: "Safely clone and redact local .env files to .example templates"
|
|
4134
|
+
}
|
|
4135
|
+
]
|
|
4136
|
+
})
|
|
4137
|
+
);
|
|
4138
|
+
switch (task) {
|
|
4139
|
+
case "add":
|
|
4140
|
+
return await runEnvAdd(void 0, void 0, flags);
|
|
4141
|
+
case "update":
|
|
4142
|
+
return await runEnvUpdate(void 0, void 0, flags);
|
|
4143
|
+
case "delete":
|
|
4144
|
+
return await runEnvDelete(void 0, flags);
|
|
4145
|
+
case "audit":
|
|
4146
|
+
return await runEnvAudit(flags);
|
|
4147
|
+
case "init":
|
|
4148
|
+
return await runEnvInit(flags);
|
|
4149
|
+
case "init-examples":
|
|
4150
|
+
return await runEnvInitExamples(flags);
|
|
4151
|
+
}
|
|
4152
|
+
}
|
|
4153
|
+
async function handleEnv(subcommand, args = [], flags = {}) {
|
|
4154
|
+
if (!subcommand) {
|
|
4155
|
+
return await runEnvMenu(flags);
|
|
4156
|
+
}
|
|
4157
|
+
const normalized = subcommand.trim().toLowerCase();
|
|
4158
|
+
switch (normalized) {
|
|
4159
|
+
case "init":
|
|
4160
|
+
return await runEnvInit(flags);
|
|
4161
|
+
case "init-examples":
|
|
4162
|
+
case "initexamples":
|
|
4163
|
+
return await runEnvInitExamples(flags);
|
|
4164
|
+
case "add":
|
|
4165
|
+
return await runEnvAdd(args[0], args[1], flags);
|
|
4166
|
+
case "update":
|
|
4167
|
+
return await runEnvUpdate(args[0], args[1], flags);
|
|
4168
|
+
case "delete":
|
|
4169
|
+
case "remove":
|
|
4170
|
+
case "rm":
|
|
4171
|
+
return await runEnvDelete(args[0], flags);
|
|
4172
|
+
case "audit":
|
|
4173
|
+
return await runEnvAudit(flags);
|
|
4174
|
+
default:
|
|
4175
|
+
p19__namespace.log.error(
|
|
4176
|
+
pc14__default.default.red(
|
|
4177
|
+
`Unknown env subcommand: "${subcommand}". Allowed subcommands: init, init-examples, add, update, delete, audit.`
|
|
4178
|
+
)
|
|
4179
|
+
);
|
|
4180
|
+
if (process.env.VITEST) {
|
|
4181
|
+
throw new Error(
|
|
4182
|
+
`Unknown env subcommand: "${subcommand}". Allowed subcommands: init, init-examples, add, update, delete, audit.`
|
|
4183
|
+
);
|
|
4184
|
+
}
|
|
4185
|
+
process.exit(1);
|
|
4186
|
+
}
|
|
4187
|
+
}
|
|
3583
4188
|
|
|
3584
4189
|
// src/commands/menu.ts
|
|
3585
4190
|
async function runInteractiveMenu(flags = {}) {
|
|
3586
4191
|
printBanner();
|
|
3587
4192
|
const choice = handleCancel(
|
|
3588
|
-
await
|
|
4193
|
+
await p19__namespace.select({
|
|
3589
4194
|
message: "What would you like to create in your architecture?",
|
|
3590
4195
|
options: [
|
|
3591
4196
|
{
|
|
@@ -3630,13 +4235,8 @@ async function runInteractiveMenu(flags = {}) {
|
|
|
3630
4235
|
},
|
|
3631
4236
|
{
|
|
3632
4237
|
value: "env",
|
|
3633
|
-
label: "
|
|
3634
|
-
hint: "
|
|
3635
|
-
},
|
|
3636
|
-
{
|
|
3637
|
-
value: "envExamples",
|
|
3638
|
-
label: "Sync Env Examples",
|
|
3639
|
-
hint: "Safely clone and redact local .env files to .example templates (Sync)"
|
|
4238
|
+
label: "Environment Variables",
|
|
4239
|
+
hint: "Manage .env files, add/delete keys, and sync templates"
|
|
3640
4240
|
}
|
|
3641
4241
|
]
|
|
3642
4242
|
})
|
|
@@ -3667,10 +4267,7 @@ async function runInteractiveMenu(flags = {}) {
|
|
|
3667
4267
|
await runInjectableCommand(void 0, flags);
|
|
3668
4268
|
break;
|
|
3669
4269
|
case "env":
|
|
3670
|
-
await
|
|
3671
|
-
break;
|
|
3672
|
-
case "envExamples":
|
|
3673
|
-
await runEnvExamplesCommand({ ...flags, overwrite: flags.overwrite ?? true });
|
|
4270
|
+
await runEnvMenu(flags);
|
|
3674
4271
|
break;
|
|
3675
4272
|
}
|
|
3676
4273
|
}
|
|
@@ -3709,8 +4306,8 @@ async function handleCreate(type, name, flags = {}) {
|
|
|
3709
4306
|
await runInjectableCommand(name, flags);
|
|
3710
4307
|
break;
|
|
3711
4308
|
default:
|
|
3712
|
-
|
|
3713
|
-
|
|
4309
|
+
p19__namespace.log.error(
|
|
4310
|
+
pc14__default.default.red(
|
|
3714
4311
|
`Unknown component type: "${type}". Allowed types: feature, shared, usecase, domain, infrastructure, interface, httpHandler, injectable.`
|
|
3715
4312
|
)
|
|
3716
4313
|
);
|
|
@@ -3718,9 +4315,9 @@ async function handleCreate(type, name, flags = {}) {
|
|
|
3718
4315
|
}
|
|
3719
4316
|
}
|
|
3720
4317
|
var importPrompts = {
|
|
3721
|
-
select:
|
|
3722
|
-
text:
|
|
3723
|
-
confirm:
|
|
4318
|
+
select: p19__namespace.select,
|
|
4319
|
+
text: p19__namespace.text,
|
|
4320
|
+
confirm: p19__namespace.confirm
|
|
3724
4321
|
};
|
|
3725
4322
|
function isTestFile(filePath) {
|
|
3726
4323
|
const normalized = filePath.replace(/\\/g, "/");
|
|
@@ -3830,19 +4427,19 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3830
4427
|
);
|
|
3831
4428
|
}
|
|
3832
4429
|
if (type !== "shared" && type !== "feature") {
|
|
3833
|
-
|
|
4430
|
+
p19__namespace.log.error(pc14__default.default.red(`Invalid module type: "${typeArg}". Must be "shared" or "feature".`));
|
|
3834
4431
|
process.exit(1);
|
|
3835
4432
|
}
|
|
3836
4433
|
const registryBase = getRegistryBase(projectRoot, flags.registry);
|
|
3837
|
-
const s =
|
|
3838
|
-
s.start(`Connecting to registry at ${
|
|
4434
|
+
const s = p19__namespace.spinner();
|
|
4435
|
+
s.start(`Connecting to registry at ${pc14__default.default.dim(registryBase)}...`);
|
|
3839
4436
|
let index;
|
|
3840
4437
|
try {
|
|
3841
4438
|
index = await fetchRegistryIndex(registryBase);
|
|
3842
4439
|
s.stop("Registry index loaded.");
|
|
3843
4440
|
} catch (err) {
|
|
3844
|
-
s.stop(
|
|
3845
|
-
|
|
4441
|
+
s.stop(pc14__default.default.red("Failed to load registry index."));
|
|
4442
|
+
p19__namespace.log.error(pc14__default.default.red(err.message || String(err)));
|
|
3846
4443
|
process.exit(1);
|
|
3847
4444
|
}
|
|
3848
4445
|
const availableList = type === "shared" ? index.shared : index.features;
|
|
@@ -3850,7 +4447,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3850
4447
|
let name = nameArg?.trim();
|
|
3851
4448
|
if (!name) {
|
|
3852
4449
|
if (availableList.length === 0) {
|
|
3853
|
-
|
|
4450
|
+
p19__namespace.log.warn(`No ${type} modules available in the registry.`);
|
|
3854
4451
|
return;
|
|
3855
4452
|
}
|
|
3856
4453
|
name = handleCancel(
|
|
@@ -3866,9 +4463,9 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3866
4463
|
}
|
|
3867
4464
|
if (!candidateNames.includes(name)) {
|
|
3868
4465
|
const closest = findClosestMatch(name, candidateNames);
|
|
3869
|
-
|
|
4466
|
+
p19__namespace.log.error(pc14__default.default.red(`\u274C Module '${name}' not found in the ${type} registry.`));
|
|
3870
4467
|
if (closest) {
|
|
3871
|
-
|
|
4468
|
+
p19__namespace.log.info(pc14__default.default.yellow(`Did you mean '${pc14__default.default.cyan(closest)}'?`));
|
|
3872
4469
|
}
|
|
3873
4470
|
process.exit(1);
|
|
3874
4471
|
}
|
|
@@ -3880,7 +4477,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3880
4477
|
try {
|
|
3881
4478
|
manifest = await fetchModuleManifest(registryBase, modType, modName);
|
|
3882
4479
|
} catch (err) {
|
|
3883
|
-
|
|
4480
|
+
p19__namespace.log.error(pc14__default.default.red(`Failed to fetch manifest for ${modType} '${modName}': ${err.message}`));
|
|
3884
4481
|
process.exit(1);
|
|
3885
4482
|
}
|
|
3886
4483
|
nodes.set(key, {
|
|
@@ -3920,7 +4517,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3920
4517
|
buildTopologicalOrder(rootKey);
|
|
3921
4518
|
const resolutions = /* @__PURE__ */ new Map();
|
|
3922
4519
|
const globalAliasMap = /* @__PURE__ */ new Map();
|
|
3923
|
-
|
|
4520
|
+
p19__namespace.log.step(`Importing ${type}: ${pc14__default.default.cyan(name)}...`);
|
|
3924
4521
|
for (const key of orderedKeys) {
|
|
3925
4522
|
const node = nodes.get(key);
|
|
3926
4523
|
const { type: modType, name: registryName, manifest } = node;
|
|
@@ -3952,8 +4549,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3952
4549
|
const programmaticPointTo = pointToMap[registryName] || pointToMap[`${modType}/${registryName}`];
|
|
3953
4550
|
if (programmaticPointTo) {
|
|
3954
4551
|
if (!checkModuleExists(projectRoot, modType, programmaticPointTo)) {
|
|
3955
|
-
|
|
3956
|
-
|
|
4552
|
+
p19__namespace.log.error(
|
|
4553
|
+
pc14__default.default.red(
|
|
3957
4554
|
`Module '${programmaticPointTo}' specified in --point-to does not exist in ${folder}.`
|
|
3958
4555
|
)
|
|
3959
4556
|
);
|
|
@@ -3961,7 +4558,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3961
4558
|
}
|
|
3962
4559
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticPointTo}/`);
|
|
3963
4560
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticPointTo}`);
|
|
3964
|
-
|
|
4561
|
+
p19__namespace.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${programmaticPointTo}'`);
|
|
3965
4562
|
resolutions.set(key, {
|
|
3966
4563
|
type: modType,
|
|
3967
4564
|
registryName,
|
|
@@ -3977,7 +4574,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3977
4574
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
|
|
3978
4575
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
3979
4576
|
const displayDest = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, programmaticAlias));
|
|
3980
|
-
|
|
4577
|
+
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
3981
4578
|
resolutions.set(key, {
|
|
3982
4579
|
type: modType,
|
|
3983
4580
|
registryName,
|
|
@@ -3989,7 +4586,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
3989
4586
|
}
|
|
3990
4587
|
const existingDir = getExistingModuleDir(projectRoot, modType, registryName);
|
|
3991
4588
|
if (!existingDir) {
|
|
3992
|
-
|
|
4589
|
+
p19__namespace.log.info(` \u21B3 Resolving required ${modType} dependency: ${pc14__default.default.cyan(registryName)}`);
|
|
3993
4590
|
resolutions.set(key, {
|
|
3994
4591
|
type: modType,
|
|
3995
4592
|
registryName,
|
|
@@ -4005,7 +4602,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4005
4602
|
const remoteIntegrity = manifest.integrity || entry?.integrity;
|
|
4006
4603
|
const localHash = computeDirectoryHash(existingDir);
|
|
4007
4604
|
if (remoteIntegrity && localHash === remoteIntegrity) {
|
|
4008
|
-
|
|
4605
|
+
p19__namespace.log.step(`\u2713 ${modType}/${registryName} is up to date (reusing existing)`);
|
|
4009
4606
|
resolutions.set(key, {
|
|
4010
4607
|
type: modType,
|
|
4011
4608
|
registryName,
|
|
@@ -4016,7 +4613,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4016
4613
|
continue;
|
|
4017
4614
|
}
|
|
4018
4615
|
if (flags.overwrite) {
|
|
4019
|
-
|
|
4616
|
+
p19__namespace.log.step(`Overwriting '${modType}/${registryName}' with registry version (--overwrite)...`);
|
|
4020
4617
|
resolutions.set(key, {
|
|
4021
4618
|
type: modType,
|
|
4022
4619
|
registryName,
|
|
@@ -4027,7 +4624,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4027
4624
|
continue;
|
|
4028
4625
|
}
|
|
4029
4626
|
if (flags.yes) {
|
|
4030
|
-
|
|
4627
|
+
p19__namespace.log.info(`Using existing '${modType}/${registryName}'`);
|
|
4031
4628
|
resolutions.set(key, {
|
|
4032
4629
|
type: modType,
|
|
4033
4630
|
registryName,
|
|
@@ -4061,7 +4658,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4061
4658
|
})
|
|
4062
4659
|
);
|
|
4063
4660
|
if (choice === "keep_existing" || choice === "use_existing") {
|
|
4064
|
-
|
|
4661
|
+
p19__namespace.log.step(`Skipping '${modType}/${registryName}' (keeping existing)`);
|
|
4065
4662
|
resolutions.set(key, {
|
|
4066
4663
|
type: modType,
|
|
4067
4664
|
registryName,
|
|
@@ -4133,7 +4730,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4133
4730
|
chosenName = chosenName.replace(/^@\/(shared|features)\//, "").replace(/^(src\/)?(shared|features)\//, "").replace(/^\/+|\/+$/g, "").trim();
|
|
4134
4731
|
globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${chosenName}/`);
|
|
4135
4732
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${chosenName}`);
|
|
4136
|
-
|
|
4733
|
+
p19__namespace.log.step(`Pointing '${modType}/${registryName}' to existing '${folder}/${chosenName}'`);
|
|
4137
4734
|
resolutions.set(key, {
|
|
4138
4735
|
type: modType,
|
|
4139
4736
|
registryName,
|
|
@@ -4165,7 +4762,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4165
4762
|
globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
|
|
4166
4763
|
const baseDir = getModuleBaseDir(projectRoot, modType);
|
|
4167
4764
|
const displayDest = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, newName));
|
|
4168
|
-
|
|
4765
|
+
p19__namespace.log.step(`Importing '${registryName}' into '${displayDest}'...`);
|
|
4169
4766
|
resolutions.set(key, {
|
|
4170
4767
|
type: modType,
|
|
4171
4768
|
registryName,
|
|
@@ -4176,7 +4773,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4176
4773
|
continue;
|
|
4177
4774
|
}
|
|
4178
4775
|
if (choice === "overwrite") {
|
|
4179
|
-
|
|
4776
|
+
p19__namespace.log.step(`Overwriting '${modType}/${registryName}' with registry version...`);
|
|
4180
4777
|
resolutions.set(key, {
|
|
4181
4778
|
type: modType,
|
|
4182
4779
|
registryName,
|
|
@@ -4219,8 +4816,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4219
4816
|
const targetDir = path2__default.default.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
|
|
4220
4817
|
const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
|
|
4221
4818
|
if (key !== rootKey) {
|
|
4222
|
-
|
|
4223
|
-
`Importing ${res.type}: ${
|
|
4819
|
+
p19__namespace.log.step(
|
|
4820
|
+
`Importing ${res.type}: ${pc14__default.default.cyan(res.targetName)}${res.targetName !== res.registryName ? pc14__default.default.dim(` (from ${res.registryName})`) : ""}...`
|
|
4224
4821
|
);
|
|
4225
4822
|
}
|
|
4226
4823
|
if (res.action === "overwrite" && !flags.dryRun) {
|
|
@@ -4238,8 +4835,8 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4238
4835
|
const remoteFilePath = `${folder}/${res.registryName}/${file}`;
|
|
4239
4836
|
const localFilePath = path2__default.default.join(targetDir, file);
|
|
4240
4837
|
if (fs4__default.default.existsSync(localFilePath) && !isOverwrite) {
|
|
4241
|
-
|
|
4242
|
-
` \u21B3 Skipping existing file: ${
|
|
4838
|
+
p19__namespace.log.warn(
|
|
4839
|
+
` \u21B3 Skipping existing file: ${pc14__default.default.dim(path2__default.default.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
|
|
4243
4840
|
);
|
|
4244
4841
|
continue;
|
|
4245
4842
|
}
|
|
@@ -4251,18 +4848,18 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4251
4848
|
}
|
|
4252
4849
|
const displayPath = path2__default.default.relative(projectRoot, localFilePath);
|
|
4253
4850
|
totalWrittenFiles.push(displayPath);
|
|
4254
|
-
|
|
4851
|
+
p19__namespace.log.message(` ${pc14__default.default.green("+")} ${displayPath}`);
|
|
4255
4852
|
}
|
|
4256
4853
|
}
|
|
4257
4854
|
const missingNpm = getMissingNpmPackages(projectRoot, Array.from(requiredNpmPackages));
|
|
4258
4855
|
if (missingNpm.length > 0) {
|
|
4259
4856
|
if (flags.skipInstall || flags.dryRun) {
|
|
4260
|
-
|
|
4857
|
+
p19__namespace.log.info(`Required npm dependencies to install: ${pc14__default.default.cyan(missingNpm.join(", "))}`);
|
|
4261
4858
|
} else {
|
|
4262
4859
|
try {
|
|
4263
4860
|
installNpmPackages(projectRoot, missingNpm);
|
|
4264
4861
|
} catch (err) {
|
|
4265
|
-
|
|
4862
|
+
p19__namespace.log.warn(
|
|
4266
4863
|
`Could not automatically install packages (${err.message}). Please run: npm install ${missingNpm.join(
|
|
4267
4864
|
" "
|
|
4268
4865
|
)}`
|
|
@@ -4271,13 +4868,13 @@ async function handleImport(typeArg, nameArg, flags = {}) {
|
|
|
4271
4868
|
}
|
|
4272
4869
|
}
|
|
4273
4870
|
if (flags.dryRun) {
|
|
4274
|
-
|
|
4871
|
+
p19__namespace.log.info(pc14__default.default.yellow("Dry run complete: No files were written to disk."));
|
|
4275
4872
|
} else {
|
|
4276
4873
|
const rootRes = resolutions.get(rootKey);
|
|
4277
4874
|
const baseDir = getModuleBaseDir(projectRoot, type);
|
|
4278
4875
|
const displayTarget = path2__default.default.relative(projectRoot, path2__default.default.join(baseDir, rootRes.targetName));
|
|
4279
|
-
|
|
4280
|
-
|
|
4876
|
+
p19__namespace.log.success(
|
|
4877
|
+
pc14__default.default.green(
|
|
4281
4878
|
`Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
|
|
4282
4879
|
)
|
|
4283
4880
|
);
|
|
@@ -4500,10 +5097,10 @@ var FeatureDependencyGraph = class {
|
|
|
4500
5097
|
if (cycle) return cycle;
|
|
4501
5098
|
} else if (inStack.has(next)) {
|
|
4502
5099
|
const cycle = [next];
|
|
4503
|
-
let
|
|
4504
|
-
while (
|
|
4505
|
-
cycle.push(
|
|
4506
|
-
|
|
5100
|
+
let p22 = curr;
|
|
5101
|
+
while (p22 !== next) {
|
|
5102
|
+
cycle.push(p22);
|
|
5103
|
+
p22 = parent.get(p22) || next;
|
|
4507
5104
|
}
|
|
4508
5105
|
cycle.push(next);
|
|
4509
5106
|
cycle.reverse();
|
|
@@ -5405,15 +6002,15 @@ ${compiledDetails}
|
|
|
5405
6002
|
// src/commands/lint.ts
|
|
5406
6003
|
function formatViolation(v) {
|
|
5407
6004
|
const lines = [];
|
|
5408
|
-
lines.push(
|
|
6005
|
+
lines.push(pc14__default.default.underline(pc14__default.default.cyan(v.relativePath)) + pc14__default.default.dim(":") + pc14__default.default.yellow(String(v.line)));
|
|
5409
6006
|
const gutter = `${String(v.line).padStart(4, " ")} | `;
|
|
5410
|
-
lines.push(
|
|
6007
|
+
lines.push(pc14__default.default.dim(gutter) + v.code);
|
|
5411
6008
|
const categoryShort = v.category.replace(" Violation", "");
|
|
5412
6009
|
lines.push(
|
|
5413
|
-
" " +
|
|
6010
|
+
" " + pc14__default.default.red(pc14__default.default.bold("\u2716")) + " " + pc14__default.default.red(`[${categoryShort}]`) + " " + v.advice
|
|
5414
6011
|
);
|
|
5415
6012
|
if (v.fixExample?.good) {
|
|
5416
|
-
lines.push(" " +
|
|
6013
|
+
lines.push(" " + pc14__default.default.dim("\u2192") + " " + pc14__default.default.dim(v.fixExample.good));
|
|
5417
6014
|
}
|
|
5418
6015
|
return lines.join("\n");
|
|
5419
6016
|
}
|
|
@@ -5438,7 +6035,7 @@ async function handleLint(flags = {}) {
|
|
|
5438
6035
|
console.log("");
|
|
5439
6036
|
}
|
|
5440
6037
|
console.log(
|
|
5441
|
-
|
|
6038
|
+
pc14__default.default.red(
|
|
5442
6039
|
`\u2716 Dependency rules check failed with ${summary.totalViolations} error${summary.totalViolations > 1 ? "s" : ""} across ${summary.violatedFeatures} feature${summary.violatedFeatures > 1 ? "s" : ""} (${summary.totalFeatures} features scanned).`
|
|
5443
6040
|
)
|
|
5444
6041
|
);
|
|
@@ -5457,7 +6054,7 @@ ${compiledDetails}
|
|
|
5457
6054
|
}
|
|
5458
6055
|
if (!quiet) {
|
|
5459
6056
|
console.log(
|
|
5460
|
-
|
|
6057
|
+
pc14__default.default.green(
|
|
5461
6058
|
`\u2714 Dependency rules verified across ${summary.totalFeatures} feature${summary.totalFeatures === 1 ? "" : "s"}.`
|
|
5462
6059
|
)
|
|
5463
6060
|
);
|
|
@@ -5472,7 +6069,7 @@ ${compiledDetails}
|
|
|
5472
6069
|
console.log(formatViolation(v));
|
|
5473
6070
|
console.log("");
|
|
5474
6071
|
}
|
|
5475
|
-
console.log(
|
|
6072
|
+
console.log(pc14__default.default.red(`\u2716 Dependency rules check failed (1 error).`));
|
|
5476
6073
|
console.log("");
|
|
5477
6074
|
}
|
|
5478
6075
|
}
|
|
@@ -5482,7 +6079,7 @@ ${compiledDetails}
|
|
|
5482
6079
|
async function handleInit(type, flags = {}) {
|
|
5483
6080
|
if (!type) {
|
|
5484
6081
|
const choice = handleCancel(
|
|
5485
|
-
await
|
|
6082
|
+
await p19__namespace.select({
|
|
5486
6083
|
message: "What would you like to initialize?",
|
|
5487
6084
|
options: [
|
|
5488
6085
|
{
|
|
@@ -5499,20 +6096,20 @@ async function handleInit(type, flags = {}) {
|
|
|
5499
6096
|
})
|
|
5500
6097
|
);
|
|
5501
6098
|
if (choice === "env") {
|
|
5502
|
-
await
|
|
6099
|
+
await runEnvInit(flags);
|
|
5503
6100
|
} else if (choice === "env-examples") {
|
|
5504
|
-
await
|
|
6101
|
+
await runEnvInitExamples(flags);
|
|
5505
6102
|
}
|
|
5506
6103
|
return;
|
|
5507
6104
|
}
|
|
5508
6105
|
const normalized = type.trim().toLowerCase();
|
|
5509
6106
|
if (normalized === "env") {
|
|
5510
|
-
await
|
|
6107
|
+
await runEnvInit(flags);
|
|
5511
6108
|
} else if (normalized === "env-examples" || normalized === "envexamples") {
|
|
5512
|
-
await
|
|
6109
|
+
await runEnvInitExamples(flags);
|
|
5513
6110
|
} else {
|
|
5514
|
-
|
|
5515
|
-
|
|
6111
|
+
p19__namespace.log.error(
|
|
6112
|
+
pc14__default.default.red(
|
|
5516
6113
|
`Unknown init target: "${type}". Allowed targets: env, env-examples.`
|
|
5517
6114
|
)
|
|
5518
6115
|
);
|
|
@@ -5528,8 +6125,8 @@ function createCli() {
|
|
|
5528
6125
|
const cli = cac.cac("mason");
|
|
5529
6126
|
cli.command("[...args]", "Interactive scaffolding and import wizard").option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-y, --yes", "Accept defaults and skip interactive prompts").option("--registry", "Run in registry mode").option("--point-to <mapping>", "Point a dependency to an existing local module (e.g. --point-to time:mytime)").option("--alias <mapping>", "Import a dependency fresh under an alias name (e.g. --alias time:time-mason)").option("--tests", "Include test files (use --no-tests to exclude)").action(async (args, flags) => {
|
|
5530
6127
|
if (args && args.length > 0) {
|
|
5531
|
-
|
|
5532
|
-
|
|
6128
|
+
p19__namespace.log.error(
|
|
6129
|
+
pc14__default.default.red(
|
|
5533
6130
|
`Unknown command: "${args.join(" ")}". Use --help to list available commands.`
|
|
5534
6131
|
)
|
|
5535
6132
|
);
|
|
@@ -5540,7 +6137,7 @@ function createCli() {
|
|
|
5540
6137
|
const mode = getEffectiveMode(projectRoot, flags);
|
|
5541
6138
|
if (mode === "registry") {
|
|
5542
6139
|
const choice2 = handleCancel(
|
|
5543
|
-
await
|
|
6140
|
+
await p19__namespace.select({
|
|
5544
6141
|
message: "\u{1F3DB}\uFE0F Mason (Registry Mode): What would you like to scaffold?",
|
|
5545
6142
|
options: [
|
|
5546
6143
|
{
|
|
@@ -5583,7 +6180,7 @@ function createCli() {
|
|
|
5583
6180
|
return;
|
|
5584
6181
|
}
|
|
5585
6182
|
const choice = handleCancel(
|
|
5586
|
-
await
|
|
6183
|
+
await p19__namespace.select({
|
|
5587
6184
|
message: "\u{1F9F1} Mason: What would you like to do?",
|
|
5588
6185
|
options: [
|
|
5589
6186
|
{
|
|
@@ -5602,9 +6199,9 @@ function createCli() {
|
|
|
5602
6199
|
hint: "Scan codebase and verify Clean Architecture dependency rules"
|
|
5603
6200
|
},
|
|
5604
6201
|
{
|
|
5605
|
-
value: "
|
|
5606
|
-
label: "
|
|
5607
|
-
hint: "
|
|
6202
|
+
value: "env",
|
|
6203
|
+
label: "Environment variables",
|
|
6204
|
+
hint: "Manage .env files, add/delete keys, and sync templates"
|
|
5608
6205
|
}
|
|
5609
6206
|
]
|
|
5610
6207
|
})
|
|
@@ -5615,8 +6212,8 @@ function createCli() {
|
|
|
5615
6212
|
await handleImport(void 0, void 0, flags);
|
|
5616
6213
|
} else if (choice === "lint") {
|
|
5617
6214
|
await handleLint(flags);
|
|
5618
|
-
} else if (choice === "
|
|
5619
|
-
await
|
|
6215
|
+
} else if (choice === "env") {
|
|
6216
|
+
await handleEnv(void 0, [], flags);
|
|
5620
6217
|
}
|
|
5621
6218
|
} catch (err) {
|
|
5622
6219
|
handleCliError(err);
|
|
@@ -5664,10 +6261,20 @@ function createCli() {
|
|
|
5664
6261
|
handleCliError(err);
|
|
5665
6262
|
}
|
|
5666
6263
|
});
|
|
6264
|
+
cli.command(
|
|
6265
|
+
"env [subcommand] [...args]",
|
|
6266
|
+
"Environment variable management and template synchronization (init, init-examples, add, update, delete, audit)"
|
|
6267
|
+
).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").option("-e, --env <environments>", "Comma-separated target environments (e.g. dev,prod)").example("mason env").example("mason env init").example("mason env init-examples").example("mason env add API_KEY secret123").example("mason env update API_KEY newsecret").example("mason env delete API_KEY").example("mason env audit").action(async (subcommand, args = [], flags = {}) => {
|
|
6268
|
+
try {
|
|
6269
|
+
await handleEnv(subcommand, args, flags);
|
|
6270
|
+
} catch (err) {
|
|
6271
|
+
handleCliError(err);
|
|
6272
|
+
}
|
|
6273
|
+
});
|
|
5667
6274
|
cli.command(
|
|
5668
6275
|
"init [type]",
|
|
5669
|
-
"Initialize project configurations
|
|
5670
|
-
).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").example("mason init env").example("mason init env
|
|
6276
|
+
"Initialize project configurations (legacy shorthand: use 'mason env init')"
|
|
6277
|
+
).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing files").example("mason init env").example("mason init env-examples").action(async (type, flags = {}) => {
|
|
5671
6278
|
try {
|
|
5672
6279
|
await handleInit(type, flags);
|
|
5673
6280
|
} catch (err) {
|
|
@@ -5676,10 +6283,10 @@ function createCli() {
|
|
|
5676
6283
|
});
|
|
5677
6284
|
cli.command(
|
|
5678
6285
|
"env-examples",
|
|
5679
|
-
"Shorthand for 'mason init
|
|
6286
|
+
"Shorthand for 'mason env init-examples'"
|
|
5680
6287
|
).option("-d, --dry-run", "Preview changes without writing to disk").option("-o, --overwrite", "Overwrite existing .env*.example files").action(async (flags = {}) => {
|
|
5681
6288
|
try {
|
|
5682
|
-
await
|
|
6289
|
+
await handleEnv("init-examples", [], flags);
|
|
5683
6290
|
} catch (err) {
|
|
5684
6291
|
handleCliError(err);
|
|
5685
6292
|
}
|
|
@@ -5715,7 +6322,7 @@ function handleCliError(err) {
|
|
|
5715
6322
|
process.exit(0);
|
|
5716
6323
|
}
|
|
5717
6324
|
const message = err instanceof Error ? err.message : String(err);
|
|
5718
|
-
|
|
6325
|
+
p19__namespace.log.error(pc14__default.default.red(`Error: ${message}`));
|
|
5719
6326
|
if (process.env.VITEST) {
|
|
5720
6327
|
throw err;
|
|
5721
6328
|
}
|
|
@@ -5728,6 +6335,7 @@ async function runCli(argv = process.argv) {
|
|
|
5728
6335
|
|
|
5729
6336
|
exports.DEFAULT_REGISTRY_BASE = DEFAULT_REGISTRY_BASE;
|
|
5730
6337
|
exports.DependencyRuleError = DependencyRuleError;
|
|
6338
|
+
exports.EnvEditor = EnvEditor;
|
|
5731
6339
|
exports.FeatureDependencyGraph = FeatureDependencyGraph;
|
|
5732
6340
|
exports.PromptCancelledError = PromptCancelledError;
|
|
5733
6341
|
exports.computeDirectoryHash = computeDirectoryHash;
|
|
@@ -5735,6 +6343,7 @@ exports.concreteInfraTemplate = concreteInfraTemplate;
|
|
|
5735
6343
|
exports.createCli = createCli;
|
|
5736
6344
|
exports.detectPackageManager = detectPackageManager;
|
|
5737
6345
|
exports.diProviderTemplate = diProviderTemplate;
|
|
6346
|
+
exports.discoverEnvironments = discoverEnvironments;
|
|
5738
6347
|
exports.domainClassTemplate = domainClassTemplate;
|
|
5739
6348
|
exports.domainInterfaceTemplate = domainInterfaceTemplate;
|
|
5740
6349
|
exports.domainValueObjectTemplate = domainValueObjectTemplate;
|
|
@@ -5772,6 +6381,7 @@ exports.getRegistryBase = getRegistryBase;
|
|
|
5772
6381
|
exports.getTemplatesDir = getTemplatesDir;
|
|
5773
6382
|
exports.handleCancel = handleCancel;
|
|
5774
6383
|
exports.handleCreate = handleCreate;
|
|
6384
|
+
exports.handleEnv = handleEnv;
|
|
5775
6385
|
exports.handleImport = handleImport;
|
|
5776
6386
|
exports.handleInit = handleInit;
|
|
5777
6387
|
exports.handleLint = handleLint;
|
|
@@ -5800,11 +6410,19 @@ exports.promptProperties = promptProperties;
|
|
|
5800
6410
|
exports.removeInterfacePrefix = removeInterfacePrefix;
|
|
5801
6411
|
exports.renderTemplate = renderTemplate;
|
|
5802
6412
|
exports.renderTemplateFile = renderTemplateFile;
|
|
6413
|
+
exports.resolveEnvFilename = resolveEnvFilename;
|
|
5803
6414
|
exports.resolveFeaturesDir = resolveFeaturesDir;
|
|
5804
6415
|
exports.resolveSharedDir = resolveSharedDir;
|
|
5805
6416
|
exports.runCli = runCli;
|
|
6417
|
+
exports.runEnvAdd = runEnvAdd;
|
|
6418
|
+
exports.runEnvAudit = runEnvAudit;
|
|
5806
6419
|
exports.runEnvCommand = runEnvCommand;
|
|
6420
|
+
exports.runEnvDelete = runEnvDelete;
|
|
5807
6421
|
exports.runEnvExamplesCommand = runEnvExamplesCommand;
|
|
6422
|
+
exports.runEnvInit = runEnvInit;
|
|
6423
|
+
exports.runEnvInitExamples = runEnvInitExamples;
|
|
6424
|
+
exports.runEnvMenu = runEnvMenu;
|
|
6425
|
+
exports.runEnvUpdate = runEnvUpdate;
|
|
5808
6426
|
exports.runSharedCommand = runSharedCommand;
|
|
5809
6427
|
exports.stubInfraTemplate = stubInfraTemplate;
|
|
5810
6428
|
exports.synthesizeCrudMethods = synthesizeCrudMethods;
|