@solid-stack/mason 1.0.32 → 1.0.34

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.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import { cac } from 'cac';
3
3
  import * as p19 from '@clack/prompts';
4
4
  import pc13 from 'picocolors';
5
- import fs14 from 'fs';
6
- import path2 from 'path';
5
+ import fs17 from 'fs';
6
+ import path26 from 'path';
7
7
  import { fileURLToPath } from 'url';
8
8
  import Handlebars from 'handlebars';
9
9
  import { execSync } from 'child_process';
@@ -11,8 +11,8 @@ import { createHash } from 'crypto';
11
11
 
12
12
  function writeFileSafely(absolutePath, content, options = {}) {
13
13
  const projectRoot = options.projectRoot || process.cwd();
14
- const relPath = path2.relative(projectRoot, absolutePath).replace(/\\/g, "/");
15
- const existedBefore = fs14.existsSync(absolutePath);
14
+ const relPath = path26.relative(projectRoot, absolutePath).replace(/\\/g, "/");
15
+ const existedBefore = fs17.existsSync(absolutePath);
16
16
  if (options.dryRun) {
17
17
  return {
18
18
  relativePath: relPath,
@@ -31,11 +31,11 @@ function writeFileSafely(absolutePath, content, options = {}) {
31
31
  existedBefore: true
32
32
  };
33
33
  }
34
- const dir = path2.dirname(absolutePath);
35
- if (!fs14.existsSync(dir)) {
36
- fs14.mkdirSync(dir, { recursive: true });
34
+ const dir = path26.dirname(absolutePath);
35
+ if (!fs17.existsSync(dir)) {
36
+ fs17.mkdirSync(dir, { recursive: true });
37
37
  }
38
- fs14.writeFileSync(absolutePath, content, "utf-8");
38
+ fs17.writeFileSync(absolutePath, content, "utf-8");
39
39
  return {
40
40
  relativePath: relPath,
41
41
  absolutePath,
@@ -126,17 +126,17 @@ function toStubName(ifaceName) {
126
126
  return `Stub${base}`;
127
127
  }
128
128
  function loadMasonConfig(startDir = process.cwd()) {
129
- let current = path2.resolve(startDir);
129
+ let current = path26.resolve(startDir);
130
130
  while (true) {
131
- const configPath = path2.join(current, "mason.config.json");
132
- if (fs14.existsSync(configPath)) {
131
+ const configPath = path26.join(current, "mason.config.json");
132
+ if (fs17.existsSync(configPath)) {
133
133
  try {
134
- return JSON.parse(fs14.readFileSync(configPath, "utf-8"));
134
+ return JSON.parse(fs17.readFileSync(configPath, "utf-8"));
135
135
  } catch {
136
136
  return null;
137
137
  }
138
138
  }
139
- const parent = path2.dirname(current);
139
+ const parent = path26.dirname(current);
140
140
  if (parent === current) break;
141
141
  current = parent;
142
142
  }
@@ -146,60 +146,91 @@ function getEffectiveMode(projectRoot, flags) {
146
146
  if (flags?.registry) return "registry";
147
147
  const config = loadMasonConfig(projectRoot);
148
148
  if (config?.mode) return config.mode;
149
- if (fs14.existsSync(path2.join(projectRoot, "index.json")) && (fs14.existsSync(path2.join(projectRoot, "features")) || fs14.existsSync(path2.join(projectRoot, "shared")) || fs14.existsSync(path2.join(projectRoot, "src/features")) || fs14.existsSync(path2.join(projectRoot, "src/shared")))) {
149
+ if (fs17.existsSync(path26.join(projectRoot, "index.json")) && (fs17.existsSync(path26.join(projectRoot, "features")) || fs17.existsSync(path26.join(projectRoot, "shared")) || fs17.existsSync(path26.join(projectRoot, "src/features")) || fs17.existsSync(path26.join(projectRoot, "src/shared")))) {
150
150
  return "registry";
151
151
  }
152
152
  return "app";
153
153
  }
154
154
  function getConfiguredFeaturesDir(projectRoot, flags) {
155
- if (flags?.target) return path2.resolve(projectRoot, flags.target);
156
- if (flags?.dest) return path2.resolve(projectRoot, flags.dest);
155
+ if (flags?.target) return path26.resolve(projectRoot, flags.target);
156
+ if (flags?.dest) return path26.resolve(projectRoot, flags.dest);
157
157
  const config = loadMasonConfig(projectRoot);
158
158
  if (config?.paths?.features) {
159
- return path2.resolve(projectRoot, config.paths.features);
159
+ return path26.resolve(projectRoot, config.paths.features);
160
160
  }
161
161
  const mode = getEffectiveMode(projectRoot, flags);
162
162
  if (mode === "registry") {
163
- if (fs14.existsSync(path2.join(projectRoot, "src/features"))) {
164
- return path2.join(projectRoot, "src/features");
163
+ if (fs17.existsSync(path26.join(projectRoot, "src/features"))) {
164
+ return path26.join(projectRoot, "src/features");
165
165
  }
166
- return path2.join(projectRoot, "features");
166
+ return path26.join(projectRoot, "features");
167
167
  }
168
- if (fs14.existsSync(path2.join(projectRoot, "src/features"))) {
169
- return path2.join(projectRoot, "src/features");
168
+ if (fs17.existsSync(path26.join(projectRoot, "src/features"))) {
169
+ return path26.join(projectRoot, "src/features");
170
170
  }
171
- return path2.join(projectRoot, "features");
171
+ return path26.join(projectRoot, "features");
172
172
  }
173
173
  function getConfiguredSharedDir(projectRoot, flags) {
174
- if (flags?.target) return path2.resolve(projectRoot, flags.target);
175
- if (flags?.dest) return path2.resolve(projectRoot, flags.dest);
174
+ if (flags?.target) return path26.resolve(projectRoot, flags.target);
175
+ if (flags?.dest) return path26.resolve(projectRoot, flags.dest);
176
176
  const config = loadMasonConfig(projectRoot);
177
177
  if (config?.paths?.shared) {
178
- return path2.resolve(projectRoot, config.paths.shared);
178
+ return path26.resolve(projectRoot, config.paths.shared);
179
179
  }
180
180
  const mode = getEffectiveMode(projectRoot, flags);
181
181
  if (mode === "registry") {
182
- if (fs14.existsSync(path2.join(projectRoot, "src/shared"))) {
183
- return path2.join(projectRoot, "src/shared");
182
+ if (fs17.existsSync(path26.join(projectRoot, "src/shared"))) {
183
+ return path26.join(projectRoot, "src/shared");
184
184
  }
185
- return path2.join(projectRoot, "shared");
185
+ return path26.join(projectRoot, "shared");
186
186
  }
187
- if (fs14.existsSync(path2.join(projectRoot, "src/shared"))) {
188
- return path2.join(projectRoot, "src/shared");
187
+ if (fs17.existsSync(path26.join(projectRoot, "src/shared"))) {
188
+ return path26.join(projectRoot, "src/shared");
189
189
  }
190
- return path2.join(projectRoot, "shared");
190
+ return path26.join(projectRoot, "shared");
191
+ }
192
+ function loadLintConfig(projectRoot) {
193
+ const configPath = path26.join(projectRoot, "mason-lint.config.json");
194
+ if (fs17.existsSync(configPath)) {
195
+ try {
196
+ return JSON.parse(fs17.readFileSync(configPath, "utf-8"));
197
+ } catch {
198
+ }
199
+ }
200
+ return {};
201
+ }
202
+ function getResolvedLintPaths(projectRoot, config) {
203
+ const masonConfig = loadMasonConfig(projectRoot);
204
+ const configuredFeatures = config.paths?.features ?? masonConfig?.paths?.features;
205
+ const configuredShared = config.paths?.shared ?? masonConfig?.paths?.shared;
206
+ const configuredErrors = config.paths?.errors;
207
+ const resolveBase = (configuredPath, defaultSrc, defaultRoot) => {
208
+ if (configuredPath !== void 0) {
209
+ return path26.resolve(projectRoot, configuredPath);
210
+ }
211
+ const srcPath = path26.join(projectRoot, defaultSrc);
212
+ if (fs17.existsSync(srcPath)) return srcPath;
213
+ const rootPath = path26.join(projectRoot, defaultRoot);
214
+ if (fs17.existsSync(rootPath)) return rootPath;
215
+ return srcPath;
216
+ };
217
+ return {
218
+ featuresBase: resolveBase(configuredFeatures, "src/features", "features"),
219
+ sharedBase: resolveBase(configuredShared, "src/shared", "shared"),
220
+ errorsBase: resolveBase(configuredErrors, "src/errors", "errors")
221
+ };
191
222
  }
192
223
 
193
224
  // src/utils/project.ts
194
225
  function findProjectRoot(startDir = process.cwd()) {
195
- let current = path2.resolve(startDir);
226
+ let current = path26.resolve(startDir);
196
227
  while (true) {
197
- if (fs14.existsSync(path2.join(current, "package.json"))) {
228
+ if (fs17.existsSync(path26.join(current, "package.json"))) {
198
229
  return current;
199
230
  }
200
- const parent = path2.dirname(current);
231
+ const parent = path26.dirname(current);
201
232
  if (parent === current) {
202
- return path2.resolve(startDir);
233
+ return path26.resolve(startDir);
203
234
  }
204
235
  current = parent;
205
236
  }
@@ -212,9 +243,9 @@ function resolveSharedDir(projectRoot, flags) {
212
243
  }
213
244
  function listExistingFeatures(projectRoot) {
214
245
  const featuresDir = resolveFeaturesDir(projectRoot);
215
- if (!fs14.existsSync(featuresDir)) return [];
246
+ if (!fs17.existsSync(featuresDir)) return [];
216
247
  try {
217
- const entries = fs14.readdirSync(featuresDir, { withFileTypes: true });
248
+ const entries = fs17.readdirSync(featuresDir, { withFileTypes: true });
218
249
  return entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
219
250
  } catch {
220
251
  return [];
@@ -224,14 +255,14 @@ function listDomainInterfaces(projectRoot, featureName) {
224
255
  const results = [];
225
256
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
226
257
  for (const feat of features) {
227
- const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
228
- if (!fs14.existsSync(domainDir)) continue;
258
+ const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
259
+ if (!fs17.existsSync(domainDir)) continue;
229
260
  try {
230
- const files = fs14.readdirSync(domainDir);
261
+ const files = fs17.readdirSync(domainDir);
231
262
  for (const file of files) {
232
263
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
233
264
  const name = file.replace(/\.ts$/, "");
234
- const fullPath = path2.join(domainDir, file);
265
+ const fullPath = path26.join(domainDir, file);
235
266
  if (/^I[A-Z]/.test(name)) {
236
267
  results.push({
237
268
  name,
@@ -240,7 +271,7 @@ function listDomainInterfaces(projectRoot, featureName) {
240
271
  });
241
272
  } else {
242
273
  try {
243
- const content = fs14.readFileSync(fullPath, "utf-8");
274
+ const content = fs17.readFileSync(fullPath, "utf-8");
244
275
  if (/export\s+(?:abstract\s+class|interface)\s+(\w+)/.test(content)) {
245
276
  results.push({
246
277
  name,
@@ -262,17 +293,17 @@ function listDomainEntities(projectRoot, featureName) {
262
293
  const results = [];
263
294
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
264
295
  for (const feat of features) {
265
- const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
266
- if (!fs14.existsSync(domainDir)) continue;
296
+ const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
297
+ if (!fs17.existsSync(domainDir)) continue;
267
298
  try {
268
- const files = fs14.readdirSync(domainDir);
299
+ const files = fs17.readdirSync(domainDir);
269
300
  for (const file of files) {
270
301
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
271
302
  const name = file.replace(/\.ts$/, "");
272
303
  if (!/^I[A-Z]/.test(name)) {
273
304
  results.push({
274
305
  name,
275
- fullPath: path2.join(domainDir, file),
306
+ fullPath: path26.join(domainDir, file),
276
307
  feature: feat
277
308
  });
278
309
  }
@@ -287,16 +318,16 @@ function listUseCases(projectRoot, featureName) {
287
318
  const results = [];
288
319
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
289
320
  for (const feat of features) {
290
- const useCasesDir = path2.join(resolveFeaturesDir(projectRoot), feat, "useCases");
291
- if (!fs14.existsSync(useCasesDir)) continue;
321
+ const useCasesDir = path26.join(resolveFeaturesDir(projectRoot), feat, "useCases");
322
+ if (!fs17.existsSync(useCasesDir)) continue;
292
323
  try {
293
- const files = fs14.readdirSync(useCasesDir);
324
+ const files = fs17.readdirSync(useCasesDir);
294
325
  for (const file of files) {
295
326
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
296
327
  const name = file.replace(/\.ts$/, "");
297
328
  results.push({
298
329
  name,
299
- fullPath: path2.join(useCasesDir, file),
330
+ fullPath: path26.join(useCasesDir, file),
300
331
  feature: feat
301
332
  });
302
333
  }
@@ -322,33 +353,33 @@ hbs.registerHelper("join", (arr, sep) => {
322
353
  });
323
354
  var cachedTemplatesDir = null;
324
355
  function getTemplatesDir() {
325
- if (cachedTemplatesDir && fs14.existsSync(cachedTemplatesDir)) {
356
+ if (cachedTemplatesDir && fs17.existsSync(cachedTemplatesDir)) {
326
357
  return cachedTemplatesDir;
327
358
  }
328
- const currentDir = typeof __dirname !== "undefined" ? __dirname : path2.dirname(fileURLToPath(import.meta.url));
329
- const cand1 = path2.resolve(currentDir, "../../templates");
330
- if (fs14.existsSync(cand1)) {
359
+ const currentDir = typeof __dirname !== "undefined" ? __dirname : path26.dirname(fileURLToPath(import.meta.url));
360
+ const cand1 = path26.resolve(currentDir, "../../templates");
361
+ if (fs17.existsSync(cand1)) {
331
362
  cachedTemplatesDir = cand1;
332
363
  return cand1;
333
364
  }
334
- const cand2 = path2.resolve(currentDir, "../templates");
335
- if (fs14.existsSync(cand2)) {
365
+ const cand2 = path26.resolve(currentDir, "../templates");
366
+ if (fs17.existsSync(cand2)) {
336
367
  cachedTemplatesDir = cand2;
337
368
  return cand2;
338
369
  }
339
- const cand3 = path2.resolve(currentDir, "templates");
340
- if (fs14.existsSync(cand3)) {
370
+ const cand3 = path26.resolve(currentDir, "templates");
371
+ if (fs17.existsSync(cand3)) {
341
372
  cachedTemplatesDir = cand3;
342
373
  return cand3;
343
374
  }
344
375
  let curr = currentDir;
345
376
  while (true) {
346
- const candTemplates = path2.join(curr, "templates");
347
- if (fs14.existsSync(candTemplates)) {
377
+ const candTemplates = path26.join(curr, "templates");
378
+ if (fs17.existsSync(candTemplates)) {
348
379
  cachedTemplatesDir = candTemplates;
349
380
  return candTemplates;
350
381
  }
351
- const parent = path2.dirname(curr);
382
+ const parent = path26.dirname(curr);
352
383
  if (parent === curr) break;
353
384
  curr = parent;
354
385
  }
@@ -356,8 +387,8 @@ function getTemplatesDir() {
356
387
  }
357
388
  var templateCache = /* @__PURE__ */ new Map();
358
389
  function loadTemplate(relativePath) {
359
- const fullPath = path2.isAbsolute(relativePath) ? relativePath : path2.join(getTemplatesDir(), relativePath);
360
- return fs14.readFileSync(fullPath, "utf-8");
390
+ const fullPath = path26.isAbsolute(relativePath) ? relativePath : path26.join(getTemplatesDir(), relativePath);
391
+ return fs17.readFileSync(fullPath, "utf-8");
361
392
  }
362
393
  function renderTemplateFile(relativePath, context) {
363
394
  let compiled = templateCache.get(relativePath);
@@ -444,8 +475,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
444
475
  function generateDomain(options) {
445
476
  const projectRoot = options.projectRoot || findProjectRoot();
446
477
  const entityName = toPascalCase(options.name);
447
- const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
448
- const filePath = path2.join(targetDir, `${entityName}.ts`);
478
+ const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
479
+ const filePath = path26.join(targetDir, `${entityName}.ts`);
449
480
  const code = generateDomainEntityCode(
450
481
  entityName,
451
482
  options.kind || "interface",
@@ -494,8 +525,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
494
525
  function generateInterface(options) {
495
526
  const projectRoot = options.projectRoot || findProjectRoot();
496
527
  const ifaceName = ensureInterfacePrefix(options.name);
497
- const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
498
- const filePath = path2.join(targetDir, `${ifaceName}.ts`);
528
+ const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
529
+ const filePath = path26.join(targetDir, `${ifaceName}.ts`);
499
530
  const code = generateInterfaceCode(
500
531
  ifaceName,
501
532
  options.kind || "abstractClass",
@@ -730,7 +761,7 @@ function parseInterfaceMethodsAndImports(content, targetIfaceName) {
730
761
  };
731
762
  }
732
763
  function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
733
- if (customPath && fs14.existsSync(customPath)) {
764
+ if (customPath && fs17.existsSync(customPath)) {
734
765
  return { filePath: customPath, ifaceName };
735
766
  }
736
767
  const candidateNames = [
@@ -740,22 +771,22 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
740
771
  ];
741
772
  const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
742
773
  for (const feat of features) {
743
- const domainDir = path2.join(resolveFeaturesDir(projectRoot), feat, "domain");
744
- if (!fs14.existsSync(domainDir)) continue;
774
+ const domainDir = path26.join(resolveFeaturesDir(projectRoot), feat, "domain");
775
+ if (!fs17.existsSync(domainDir)) continue;
745
776
  for (const cand of candidateNames) {
746
- const p1 = path2.join(domainDir, `${cand}.ts`);
747
- if (fs14.existsSync(p1)) {
777
+ const p1 = path26.join(domainDir, `${cand}.ts`);
778
+ if (fs17.existsSync(p1)) {
748
779
  return { filePath: p1, ifaceName: cand, feature: feat };
749
780
  }
750
781
  }
751
782
  }
752
783
  for (const cand of candidateNames) {
753
- const p1 = path2.join(projectRoot, "domain", `${cand}.ts`);
754
- if (fs14.existsSync(p1)) return { filePath: p1, ifaceName: cand };
755
- const p22 = path2.join(projectRoot, "src", "domain", `${cand}.ts`);
756
- if (fs14.existsSync(p22)) return { filePath: p22, ifaceName: cand };
757
- const p32 = path2.join(projectRoot, "interfaces", `${cand}.ts`);
758
- if (fs14.existsSync(p32)) return { filePath: p32, ifaceName: cand };
784
+ const p1 = path26.join(projectRoot, "domain", `${cand}.ts`);
785
+ if (fs17.existsSync(p1)) return { filePath: p1, ifaceName: cand };
786
+ const p22 = path26.join(projectRoot, "src", "domain", `${cand}.ts`);
787
+ if (fs17.existsSync(p22)) return { filePath: p22, ifaceName: cand };
788
+ const p32 = path26.join(projectRoot, "interfaces", `${cand}.ts`);
789
+ if (fs17.existsSync(p32)) return { filePath: p32, ifaceName: cand };
759
790
  }
760
791
  return null;
761
792
  }
@@ -778,7 +809,7 @@ function loadInterfaceHierarchy(projectRoot, initialIfaceName, featureName, cust
778
809
  while (currentFile && !visited.has(currentFile)) {
779
810
  visited.add(currentFile);
780
811
  try {
781
- const content = fs14.readFileSync(currentFile, "utf-8");
812
+ const content = fs17.readFileSync(currentFile, "utf-8");
782
813
  const parsed = parseInterfaceMethodsAndImports(content, currentName);
783
814
  for (const m of parsed.methods) {
784
815
  if (!allMethods.some((existing) => existing.name === m.name)) {
@@ -854,7 +885,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
854
885
  const entitiesVar = `${entityVar}s`;
855
886
  const idKey = `${entityVar}Id`;
856
887
  const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
857
- const entityImportPath = ifaceImportPath ? `${path2.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
888
+ const entityImportPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
858
889
  const importsByFrom = /* @__PURE__ */ new Map();
859
890
  for (const imp of domainImports) {
860
891
  if (imp.name === ifaceName || imp.name === baseEntity) continue;
@@ -903,7 +934,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
903
934
  if (baseEntity) {
904
935
  finalMethods = synthesizeCrudMethods(baseEntity);
905
936
  if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
906
- const importPath = ifaceImportPath ? `${path2.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
937
+ const importPath = ifaceImportPath ? `${path26.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
907
938
  finalDomainImports.push({
908
939
  name: baseEntity,
909
940
  from: importPath,
@@ -948,8 +979,8 @@ function generateInfrastructure(options) {
948
979
  const projectRoot = options.projectRoot || findProjectRoot();
949
980
  const className = toPascalCase(options.name);
950
981
  const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
951
- const targetDir = options.targetDir || (isFeatureScope ? path2.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path2.join(projectRoot, "infrastructure"));
952
- const filePath = path2.join(targetDir, `${className}.ts`);
982
+ const targetDir = options.targetDir || (isFeatureScope ? path26.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path26.join(projectRoot, "infrastructure"));
983
+ const filePath = path26.join(targetDir, `${className}.ts`);
953
984
  let code;
954
985
  if (options.implementsInterface) {
955
986
  const rawIfaceName = options.implementsInterface;
@@ -963,7 +994,7 @@ function generateInfrastructure(options) {
963
994
  const ifaceFilePath = hierarchy.ifaceFilePath;
964
995
  let ifaceImportPath;
965
996
  if (ifaceFilePath) {
966
- let rel = path2.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
997
+ let rel = path26.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
967
998
  if (!rel.startsWith(".")) rel = "./" + rel;
968
999
  ifaceImportPath = rel;
969
1000
  } else if (isFeatureScope) {
@@ -976,8 +1007,8 @@ function generateInfrastructure(options) {
976
1007
  if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
977
1008
  let fromPath;
978
1009
  if (imp.specifier.startsWith(".") && ifaceFilePath) {
979
- const absTarget = path2.resolve(path2.dirname(ifaceFilePath), imp.specifier);
980
- let rel = path2.relative(targetDir, absTarget).replace(/\\/g, "/");
1010
+ const absTarget = path26.resolve(path26.dirname(ifaceFilePath), imp.specifier);
1011
+ let rel = path26.relative(targetDir, absTarget).replace(/\\/g, "/");
981
1012
  if (!rel.startsWith(".")) rel = "./" + rel;
982
1013
  fromPath = rel;
983
1014
  } else {
@@ -1164,8 +1195,8 @@ function generateUseCase(options) {
1164
1195
  const files = [];
1165
1196
  const projectRoot = options.projectRoot || findProjectRoot();
1166
1197
  const ucName = toPascalCase(options.name);
1167
- const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1168
- const filePath = path2.join(targetDir, `${ucName}.ts`);
1198
+ const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1199
+ const filePath = path26.join(targetDir, `${ucName}.ts`);
1169
1200
  const code = generateUseCaseCode(
1170
1201
  ucName,
1171
1202
  options.dependencies || [],
@@ -1181,7 +1212,7 @@ function generateUseCase(options) {
1181
1212
  })
1182
1213
  );
1183
1214
  if (options.includeTest) {
1184
- const testPath = path2.join(targetDir, `${ucName}.test.ts`);
1215
+ const testPath = path26.join(targetDir, `${ucName}.test.ts`);
1185
1216
  const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
1186
1217
  files.push(
1187
1218
  writeFileSafely(testPath, testCode, {
@@ -1249,10 +1280,10 @@ function generateHttpHandler(options) {
1249
1280
  const files = [];
1250
1281
  const projectRoot = options.projectRoot || findProjectRoot();
1251
1282
  const className = toPascalCase(options.name);
1252
- const pxDir = path2.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1253
- const handlersDir = path2.join(pxDir, "handlers");
1254
- const pxIndexPath = path2.join(pxDir, "index.ts");
1255
- if (!fs14.existsSync(pxIndexPath)) {
1283
+ const pxDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1284
+ const handlersDir = path26.join(pxDir, "handlers");
1285
+ const pxIndexPath = path26.join(pxDir, "index.ts");
1286
+ if (!fs17.existsSync(pxIndexPath)) {
1256
1287
  const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
1257
1288
  const pxIndexContent = renderTemplate(expressIndexTemplate, {
1258
1289
  routePath: featurePath
@@ -1265,7 +1296,7 @@ function generateHttpHandler(options) {
1265
1296
  })
1266
1297
  );
1267
1298
  }
1268
- const handlerFilePath = path2.join(handlersDir, `${className}.ts`);
1299
+ const handlerFilePath = path26.join(handlersDir, `${className}.ts`);
1269
1300
  const code = generateHttpHandlerCode(
1270
1301
  className,
1271
1302
  options.method || "get",
@@ -1286,11 +1317,11 @@ function generateHttpHandler(options) {
1286
1317
  return files;
1287
1318
  }
1288
1319
  function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
1289
- const providerPath = path2.join(featureDir, "diProvider.ts");
1320
+ const providerPath = path26.join(featureDir, "diProvider.ts");
1290
1321
  const pascalFeature = toPascalCase(featureName);
1291
1322
  const providerModuleName = `${pascalFeature}Provider`;
1292
- if (fs14.existsSync(providerPath) && !options.overwrite) {
1293
- let content = fs14.readFileSync(providerPath, "utf-8");
1323
+ if (fs17.existsSync(providerPath) && !options.overwrite) {
1324
+ let content = fs17.readFileSync(providerPath, "utf-8");
1294
1325
  content = content.replace(/\n*export\s+const\s+ScaffoldProvider\s*=[^;]+;?/g, "");
1295
1326
  for (const b of bindings) {
1296
1327
  const ifaceImp = b.ifaceImportPath || `./domain/${b.ifaceName}.js`;
@@ -1349,7 +1380,7 @@ function generateFeature(options) {
1349
1380
  const projectRoot = options.projectRoot || findProjectRoot();
1350
1381
  const rawName = options.name.trim();
1351
1382
  const featureName = toCamelCase(rawName);
1352
- const targetDir = options.targetDir || path2.join(resolveFeaturesDir(projectRoot), featureName);
1383
+ const targetDir = options.targetDir || path26.join(resolveFeaturesDir(projectRoot), featureName);
1353
1384
  const routePath = options.routePath || `/${featureName}`;
1354
1385
  const rawEntities = [];
1355
1386
  if (options.entityNames && options.entityNames.length > 0) {
@@ -1370,7 +1401,7 @@ function generateFeature(options) {
1370
1401
  const domainFile = generateDomain({
1371
1402
  name: ent,
1372
1403
  feature: featureName,
1373
- targetDir: path2.join(targetDir, "domain"),
1404
+ targetDir: path26.join(targetDir, "domain"),
1374
1405
  projectRoot,
1375
1406
  dryRun: options.dryRun,
1376
1407
  overwrite: options.overwrite,
@@ -1390,7 +1421,7 @@ function generateFeature(options) {
1390
1421
  const ifaceFile = generateInterface({
1391
1422
  name: ifaceName,
1392
1423
  feature: featureName,
1393
- targetDir: path2.join(targetDir, "domain"),
1424
+ targetDir: path26.join(targetDir, "domain"),
1394
1425
  entityName: ent,
1395
1426
  projectRoot,
1396
1427
  dryRun: options.dryRun,
@@ -1407,7 +1438,7 @@ function generateFeature(options) {
1407
1438
  const stubFile = generateInfrastructure({
1408
1439
  name: stubName,
1409
1440
  feature: featureName,
1410
- targetDir: path2.join(targetDir, "infrastructure"),
1441
+ targetDir: path26.join(targetDir, "infrastructure"),
1411
1442
  kind: "stub",
1412
1443
  implementsInterface: ifaceName,
1413
1444
  entityName: ent,
@@ -1449,7 +1480,7 @@ function generateFeature(options) {
1449
1480
  const ucFiles = generateUseCase({
1450
1481
  name: ucName,
1451
1482
  feature: featureName,
1452
- targetDir: path2.join(targetDir, "useCases"),
1483
+ targetDir: path26.join(targetDir, "useCases"),
1453
1484
  dependencies: options.includeInterface !== false ? [ifaceName] : [],
1454
1485
  inputProperties: [{ name: idParam, type: "string" }],
1455
1486
  outputProperties: [{ name: entVar, type: primaryEntity }],
@@ -1494,7 +1525,7 @@ function generateFeature(options) {
1494
1525
  const ucFiles = generateUseCase({
1495
1526
  name: ucName,
1496
1527
  feature: featureName,
1497
- targetDir: path2.join(targetDir, "useCases"),
1528
+ targetDir: path26.join(targetDir, "useCases"),
1498
1529
  dependencies: deps,
1499
1530
  inputProperties: [{ name: entVar, type: ent }],
1500
1531
  outputProperties: [{ name: entVar, type: ent }],
@@ -1527,7 +1558,7 @@ function generateFeature(options) {
1527
1558
  const ucFiles = generateUseCase({
1528
1559
  name: ucName,
1529
1560
  feature: featureName,
1530
- targetDir: path2.join(targetDir, "useCases"),
1561
+ targetDir: path26.join(targetDir, "useCases"),
1531
1562
  dependencies: deps,
1532
1563
  inputProperties: [{ name: idParam, type: "string" }],
1533
1564
  outputProperties: [{ name: entVar, type: ent }],
@@ -1560,7 +1591,7 @@ function generateFeature(options) {
1560
1591
  const ucFiles = generateUseCase({
1561
1592
  name: ucName,
1562
1593
  feature: featureName,
1563
- targetDir: path2.join(targetDir, "useCases"),
1594
+ targetDir: path26.join(targetDir, "useCases"),
1564
1595
  dependencies: deps,
1565
1596
  inputProperties: [
1566
1597
  { name: "page", type: "number", optional: true },
@@ -1604,7 +1635,7 @@ function generateFeature(options) {
1604
1635
  const ucFiles = generateUseCase({
1605
1636
  name: ucName,
1606
1637
  feature: featureName,
1607
- targetDir: path2.join(targetDir, "useCases"),
1638
+ targetDir: path26.join(targetDir, "useCases"),
1608
1639
  dependencies: deps,
1609
1640
  inputProperties: [{ name: entVar, type: ent }],
1610
1641
  outputProperties: [{ name: entVar, type: ent }],
@@ -1637,7 +1668,7 @@ function generateFeature(options) {
1637
1668
  const ucFiles = generateUseCase({
1638
1669
  name: ucName,
1639
1670
  feature: featureName,
1640
- targetDir: path2.join(targetDir, "useCases"),
1671
+ targetDir: path26.join(targetDir, "useCases"),
1641
1672
  dependencies: deps,
1642
1673
  inputProperties: [{ name: idParam, type: "string" }],
1643
1674
  outputProperties: [{ name: "success", type: "boolean" }],
@@ -1674,7 +1705,7 @@ function generateFeature(options) {
1674
1705
  const ucFiles = generateUseCase({
1675
1706
  name: ucName,
1676
1707
  feature: featureName,
1677
- targetDir: path2.join(targetDir, "useCases"),
1708
+ targetDir: path26.join(targetDir, "useCases"),
1678
1709
  dependencies: customUc.dependencies || [],
1679
1710
  inputProperties: customUc.inputProperties || [],
1680
1711
  outputProperties: customUc.outputProperties || [],
@@ -1706,8 +1737,8 @@ function generateFeature(options) {
1706
1737
  }
1707
1738
  }
1708
1739
  if (options.includeExpress !== false) {
1709
- const pxDir = path2.join(targetDir, "pxExpress");
1710
- const pxIndexPath = path2.join(pxDir, "index.ts");
1740
+ const pxDir = path26.join(targetDir, "pxExpress");
1741
+ const pxIndexPath = path26.join(pxDir, "index.ts");
1711
1742
  if (!files.some((f) => f.absolutePath === pxIndexPath)) {
1712
1743
  const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
1713
1744
  files.push(
@@ -1837,7 +1868,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1837
1868
  }
1838
1869
  const normalizedName = toCamelCase(name);
1839
1870
  const baseFeaturesDir = resolveFeaturesDir(projectRoot, flags);
1840
- const defaultDir = path2.relative(projectRoot, path2.join(baseFeaturesDir, normalizedName));
1871
+ const defaultDir = path26.relative(projectRoot, path26.join(baseFeaturesDir, normalizedName));
1841
1872
  let targetDirRel = defaultDir;
1842
1873
  if (!flags.yes && !flags.target && !flags.dest) {
1843
1874
  targetDirRel = handleCancel(
@@ -1848,7 +1879,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1848
1879
  })
1849
1880
  ).trim();
1850
1881
  }
1851
- const targetDir = flags.dest ? path2.resolve(projectRoot, flags.dest) : flags.target ? path2.resolve(projectRoot, flags.target) : path2.resolve(projectRoot, targetDirRel);
1882
+ const targetDir = flags.dest ? path26.resolve(projectRoot, flags.dest) : flags.target ? path26.resolve(projectRoot, flags.target) : path26.resolve(projectRoot, targetDirRel);
1852
1883
  let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
1853
1884
  let routePath = `/${normalizedName}`;
1854
1885
  let includeDomain = true;
@@ -2090,8 +2121,8 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2090
2121
  };
2091
2122
  const files = generateFeature(options);
2092
2123
  if (isRegistry) {
2093
- const registryPath = path2.join(targetDir, "registry.json");
2094
- if (!fs14.existsSync(registryPath)) {
2124
+ const registryPath = path26.join(targetDir, "registry.json");
2125
+ if (!fs17.existsSync(registryPath)) {
2095
2126
  const manifest = {
2096
2127
  name: toKebabCase(normalizedName),
2097
2128
  type: "feature",
@@ -2117,7 +2148,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
2117
2148
  printGeneratedFiles(files, options.dryRun);
2118
2149
  p19.outro(
2119
2150
  pc13.green(
2120
- `\u2713 Feature '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2151
+ `\u2713 Feature '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2121
2152
  )
2122
2153
  );
2123
2154
  }
@@ -2128,7 +2159,7 @@ function generateShared(options) {
2128
2159
  const pascalName = toPascalCase(rawName);
2129
2160
  const camelName = toCamelCase(rawName);
2130
2161
  const kebabName = toKebabCase(rawName);
2131
- const targetDir = options.targetDir || path2.join(resolveSharedDir(projectRoot), kebabName);
2162
+ const targetDir = options.targetDir || path26.join(resolveSharedDir(projectRoot), kebabName);
2132
2163
  const fileDefs = [];
2133
2164
  const facadeContent = `import { MakeInjectable, type DepsType } from "@solid-stack/di";
2134
2165
  import { I${pascalName}Engine } from "./ports/I${pascalName}Engine.js";
@@ -2140,7 +2171,7 @@ export class ${pascalName} {
2140
2171
  }
2141
2172
  `;
2142
2173
  fileDefs.push({
2143
- absolutePath: path2.join(targetDir, `${pascalName}.ts`),
2174
+ absolutePath: path26.join(targetDir, `${pascalName}.ts`),
2144
2175
  content: facadeContent
2145
2176
  });
2146
2177
  const portContent = `export abstract class I${pascalName}Engine {
@@ -2148,7 +2179,7 @@ export class ${pascalName} {
2148
2179
  }
2149
2180
  `;
2150
2181
  fileDefs.push({
2151
- absolutePath: path2.join(targetDir, "ports", `I${pascalName}Engine.ts`),
2182
+ absolutePath: path26.join(targetDir, "ports", `I${pascalName}Engine.ts`),
2152
2183
  content: portContent
2153
2184
  });
2154
2185
  const concreteContent = `import { MakeInjectable } from "@solid-stack/di";
@@ -2160,7 +2191,7 @@ export class Default${pascalName}Engine implements I${pascalName}Engine {
2160
2191
  }
2161
2192
  `;
2162
2193
  fileDefs.push({
2163
- absolutePath: path2.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
2194
+ absolutePath: path26.join(targetDir, "infrastructure", `Default${pascalName}Engine.ts`),
2164
2195
  content: concreteContent
2165
2196
  });
2166
2197
  if (options.includeStub !== false) {
@@ -2173,7 +2204,7 @@ export class Stub${pascalName}Engine implements I${pascalName}Engine {
2173
2204
  }
2174
2205
  `;
2175
2206
  fileDefs.push({
2176
- absolutePath: path2.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
2207
+ absolutePath: path26.join(targetDir, "infrastructure", `Stub${pascalName}Engine.ts`),
2177
2208
  content: stubContent
2178
2209
  });
2179
2210
  }
@@ -2188,7 +2219,7 @@ export const diProvider = (c: Container): void => {
2188
2219
  export default diProvider;
2189
2220
  `;
2190
2221
  fileDefs.push({
2191
- absolutePath: path2.join(targetDir, "diProvider.ts"),
2222
+ absolutePath: path26.join(targetDir, "diProvider.ts"),
2192
2223
  content: providerContent
2193
2224
  });
2194
2225
  if (options.isRegistry) {
@@ -2205,7 +2236,7 @@ export default diProvider;
2205
2236
  files: []
2206
2237
  };
2207
2238
  fileDefs.push({
2208
- absolutePath: path2.join(targetDir, "registry.json"),
2239
+ absolutePath: path26.join(targetDir, "registry.json"),
2209
2240
  content: JSON.stringify(registryManifest, null, 2) + "\n"
2210
2241
  });
2211
2242
  }
@@ -2242,7 +2273,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2242
2273
  }
2243
2274
  const normalizedName = toKebabCase(name);
2244
2275
  const baseSharedDir = resolveSharedDir(projectRoot, flags);
2245
- const defaultDir = path2.relative(projectRoot, path2.join(baseSharedDir, normalizedName));
2276
+ const defaultDir = path26.relative(projectRoot, path26.join(baseSharedDir, normalizedName));
2246
2277
  let targetDirRel = defaultDir;
2247
2278
  if (!flags.yes && !flags.target && !flags.dest) {
2248
2279
  targetDirRel = handleCancel(
@@ -2253,7 +2284,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2253
2284
  })
2254
2285
  ).trim();
2255
2286
  }
2256
- const targetDir = flags.dest ? path2.resolve(projectRoot, flags.dest) : flags.target ? path2.resolve(projectRoot, flags.target) : path2.resolve(projectRoot, targetDirRel);
2287
+ const targetDir = flags.dest ? path26.resolve(projectRoot, flags.dest) : flags.target ? path26.resolve(projectRoot, flags.target) : path26.resolve(projectRoot, targetDirRel);
2257
2288
  let description = flags.description || "";
2258
2289
  let includeStub = true;
2259
2290
  if (!flags.yes) {
@@ -2287,7 +2318,7 @@ async function runSharedCommand(sharedNameArg, flags = {}) {
2287
2318
  printGeneratedFiles(files, Boolean(flags.dryRun));
2288
2319
  p19.outro(
2289
2320
  pc13.green(
2290
- `\u2713 Shared module '${normalizedName}' initialized in ${path2.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2321
+ `\u2713 Shared module '${normalizedName}' initialized in ${path26.relative(projectRoot, targetDir)} (uncommitted, unregistered)`
2291
2322
  )
2292
2323
  );
2293
2324
  }
@@ -2714,7 +2745,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2714
2745
  const infraFile = generateInfrastructure(options);
2715
2746
  files.push(infraFile);
2716
2747
  if (registerInProvider && scope === "feature" && feature && implementsInterface) {
2717
- const featureDir = path2.join(resolveFeaturesDir(projectRoot), feature);
2748
+ const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
2718
2749
  const providerFile = updateOrGenerateDiProvider(
2719
2750
  featureDir,
2720
2751
  feature,
@@ -2876,7 +2907,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2876
2907
  files.push(stubFile);
2877
2908
  }
2878
2909
  if (registerInProvider && generateStub) {
2879
- const featureDir = path2.join(resolveFeaturesDir(projectRoot), feature);
2910
+ const featureDir = path26.join(resolveFeaturesDir(projectRoot), feature);
2880
2911
  const providerFile = updateOrGenerateDiProvider(
2881
2912
  featureDir,
2882
2913
  feature,
@@ -3064,21 +3095,21 @@ function generateInjectable(options) {
3064
3095
  const className = toPascalCase(options.name);
3065
3096
  let targetDir = "";
3066
3097
  if (options.customPath) {
3067
- targetDir = path2.isAbsolute(options.customPath) ? options.customPath : path2.join(projectRoot, options.customPath);
3098
+ targetDir = path26.isAbsolute(options.customPath) ? options.customPath : path26.join(projectRoot, options.customPath);
3068
3099
  } else if (options.location === "feature-service" && options.feature) {
3069
- targetDir = path2.join(resolveFeaturesDir(projectRoot), options.feature, "services");
3100
+ targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "services");
3070
3101
  } else if (options.location === "feature-transformer" && options.feature) {
3071
- targetDir = path2.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
3102
+ targetDir = path26.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
3072
3103
  } else if (options.location === "module") {
3073
- targetDir = path2.join(projectRoot, "modules", className);
3104
+ targetDir = path26.join(projectRoot, "modules", className);
3074
3105
  } else {
3075
- targetDir = options.feature ? path2.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path2.join(projectRoot, "shared", className.toLowerCase());
3106
+ targetDir = options.feature ? path26.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path26.join(projectRoot, "shared", className.toLowerCase());
3076
3107
  }
3077
- const filePath = path2.join(targetDir, `${className}.ts`);
3108
+ const filePath = path26.join(targetDir, `${className}.ts`);
3078
3109
  let ifaceName;
3079
3110
  if (options.generateInterface) {
3080
3111
  ifaceName = `I${className}`;
3081
- const ifacePath = path2.join(targetDir, `${ifaceName}.ts`);
3112
+ const ifacePath = path26.join(targetDir, `${ifaceName}.ts`);
3082
3113
  const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
3083
3114
  files.push(
3084
3115
  writeFileSafely(ifacePath, ifaceContent, {
@@ -3207,7 +3238,7 @@ async function runEnvInit(flags = {}) {
3207
3238
  const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3208
3239
  const s = p19.spinner();
3209
3240
  s.start("Scanning for .example environment files...");
3210
- const files = fs14.readdirSync(projectRoot);
3241
+ const files = fs17.readdirSync(projectRoot);
3211
3242
  const exampleFiles = files.filter((file) => file.startsWith(".env") && file.endsWith(".example")).sort();
3212
3243
  if (exampleFiles.length === 0) {
3213
3244
  s.stop("No .example files found.");
@@ -3217,11 +3248,11 @@ async function runEnvInit(flags = {}) {
3217
3248
  s.message(`Found ${exampleFiles.length} template(s). Generating...`);
3218
3249
  const generatedFiles = [];
3219
3250
  for (const exampleFile of exampleFiles) {
3220
- const sourcePath = path2.join(projectRoot, exampleFile);
3251
+ const sourcePath = path26.join(projectRoot, exampleFile);
3221
3252
  const targetFile = exampleFile.replace(/\.example$/, "");
3222
- const targetPath = path2.join(projectRoot, targetFile);
3253
+ const targetPath = path26.join(projectRoot, targetFile);
3223
3254
  let overwrite = flags.overwrite;
3224
- if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs14.existsSync(targetPath)) {
3255
+ if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17.existsSync(targetPath)) {
3225
3256
  overwrite = await p19.confirm({
3226
3257
  message: `${targetFile} already exists. Overwrite?`,
3227
3258
  initialValue: false
@@ -3231,7 +3262,7 @@ async function runEnvInit(flags = {}) {
3231
3262
  process.exit(0);
3232
3263
  }
3233
3264
  }
3234
- const content = fs14.readFileSync(sourcePath, "utf-8");
3265
+ const content = fs17.readFileSync(sourcePath, "utf-8");
3235
3266
  const genFile = writeFileSafely(targetPath, content, {
3236
3267
  projectRoot,
3237
3268
  dryRun: Boolean(flags.dryRun),
@@ -3249,7 +3280,7 @@ var EnvEditor = class {
3249
3280
  filePath;
3250
3281
  constructor(filePath) {
3251
3282
  this.filePath = filePath;
3252
- this.lines = fs14.existsSync(filePath) ? fs14.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
3283
+ this.lines = fs17.existsSync(filePath) ? fs17.readFileSync(filePath, "utf-8").split(/\r?\n/) : [];
3253
3284
  }
3254
3285
  getLines() {
3255
3286
  return [...this.lines];
@@ -3313,18 +3344,18 @@ var EnvEditor = class {
3313
3344
  });
3314
3345
  }
3315
3346
  save() {
3316
- const dir = path2.dirname(this.filePath);
3317
- if (!fs14.existsSync(dir)) {
3318
- fs14.mkdirSync(dir, { recursive: true });
3347
+ const dir = path26.dirname(this.filePath);
3348
+ if (!fs17.existsSync(dir)) {
3349
+ fs17.mkdirSync(dir, { recursive: true });
3319
3350
  }
3320
- fs14.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
3351
+ fs17.writeFileSync(this.filePath, this.lines.join("\n"), "utf-8");
3321
3352
  }
3322
3353
  };
3323
3354
  function discoverEnvironments(projectRoot) {
3324
- if (!fs14.existsSync(projectRoot)) {
3355
+ if (!fs17.existsSync(projectRoot)) {
3325
3356
  return ["local", "dev", "prod"];
3326
3357
  }
3327
- const files = fs14.readdirSync(projectRoot);
3358
+ const files = fs17.readdirSync(projectRoot);
3328
3359
  const envs = /* @__PURE__ */ new Set();
3329
3360
  for (const file of files) {
3330
3361
  if (file.startsWith(".env")) {
@@ -3349,7 +3380,7 @@ async function runEnvInitExamples(flags = {}) {
3349
3380
  const projectRoot = flags.cwd || flags.targetDir || findProjectRoot();
3350
3381
  const s = p19.spinner();
3351
3382
  s.start("Scanning for local environment files...");
3352
- const files = fs14.readdirSync(projectRoot, { withFileTypes: true });
3383
+ const files = fs17.readdirSync(projectRoot, { withFileTypes: true });
3353
3384
  const envFiles = files.filter((dirent) => dirent.isFile() && dirent.name.startsWith(".env") && !dirent.name.endsWith(".example")).map((dirent) => dirent.name).sort();
3354
3385
  if (envFiles.length === 0) {
3355
3386
  s.stop("No local .env files found.");
@@ -3359,11 +3390,11 @@ async function runEnvInitExamples(flags = {}) {
3359
3390
  s.message(`Found ${envFiles.length} local .env file(s). Generating templates...`);
3360
3391
  const generatedFiles = [];
3361
3392
  for (const envFile of envFiles) {
3362
- const sourcePath = path2.join(projectRoot, envFile);
3393
+ const sourcePath = path26.join(projectRoot, envFile);
3363
3394
  const targetFile = `${envFile}.example`;
3364
- const targetPath = path2.join(projectRoot, targetFile);
3395
+ const targetPath = path26.join(projectRoot, targetFile);
3365
3396
  let overwrite = flags.overwrite;
3366
- if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs14.existsSync(targetPath)) {
3397
+ if (overwrite === void 0 && !flags.dryRun && !flags.yes && !process.env.VITEST && process.stdin.isTTY && fs17.existsSync(targetPath)) {
3367
3398
  overwrite = await p19.confirm({
3368
3399
  message: `${targetFile} already exists. Overwrite with redacted values?`,
3369
3400
  initialValue: true
@@ -3396,12 +3427,12 @@ function isInteractive(flags) {
3396
3427
  function getAllExistingKeys(projectRoot, envs) {
3397
3428
  const keys = /* @__PURE__ */ new Set();
3398
3429
  for (const env of envs) {
3399
- const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3400
- const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3401
- if (fs14.existsSync(localPath)) {
3430
+ const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
3431
+ const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
3432
+ if (fs17.existsSync(localPath)) {
3402
3433
  new EnvEditor(localPath).getKeys().forEach((k) => keys.add(k));
3403
3434
  }
3404
- if (fs14.existsSync(examplePath)) {
3435
+ if (fs17.existsSync(examplePath)) {
3405
3436
  new EnvEditor(examplePath).getKeys().forEach((k) => keys.add(k));
3406
3437
  }
3407
3438
  }
@@ -3473,8 +3504,8 @@ async function runEnvAdd(key, value, flags = {}) {
3473
3504
  })
3474
3505
  );
3475
3506
  }
3476
- const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3477
- const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3507
+ const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
3508
+ const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
3478
3509
  if (flags.dryRun) {
3479
3510
  p19.log.info(
3480
3511
  pc13.cyan(`[DRY RUN] Would set ${targetKey}=${envVal} in ${resolveEnvFilename(env, false)}`)
@@ -3521,8 +3552,8 @@ async function runEnvUpdate(key, value, flags = {}) {
3521
3552
  );
3522
3553
  }
3523
3554
  const envsWithKey = allEnvs.filter((env) => {
3524
- const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3525
- return fs14.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
3555
+ const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
3556
+ return fs17.existsSync(localPath) && new EnvEditor(localPath).has(targetKey);
3526
3557
  });
3527
3558
  let targetEnvs = [];
3528
3559
  if (flags.env) {
@@ -3548,7 +3579,7 @@ async function runEnvUpdate(key, value, flags = {}) {
3548
3579
  return { key: targetKey, environments: [] };
3549
3580
  }
3550
3581
  for (const env of targetEnvs) {
3551
- const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3582
+ const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
3552
3583
  const localEditor = new EnvEditor(localPath);
3553
3584
  const currentVal = localEditor.getValue(targetKey);
3554
3585
  let newVal = value;
@@ -3578,7 +3609,7 @@ async function runEnvUpdate(key, value, flags = {}) {
3578
3609
  } else {
3579
3610
  localEditor.set(targetKey, newVal);
3580
3611
  localEditor.save();
3581
- const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3612
+ const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
3582
3613
  const exampleEditor = new EnvEditor(examplePath);
3583
3614
  if (!exampleEditor.has(targetKey)) {
3584
3615
  exampleEditor.set(targetKey, "");
@@ -3614,9 +3645,9 @@ async function runEnvDelete(key, flags = {}) {
3614
3645
  );
3615
3646
  }
3616
3647
  const envsWithKey = allEnvs.filter((env) => {
3617
- const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3618
- const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3619
- return fs14.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs14.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
3648
+ const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
3649
+ const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
3650
+ return fs17.existsSync(localPath) && new EnvEditor(localPath).has(targetKey) || fs17.existsSync(examplePath) && new EnvEditor(examplePath).has(targetKey);
3620
3651
  });
3621
3652
  let targetEnvs = [];
3622
3653
  if (flags.env) {
@@ -3654,19 +3685,19 @@ async function runEnvDelete(key, flags = {}) {
3654
3685
  }
3655
3686
  }
3656
3687
  for (const env of targetEnvs) {
3657
- const localPath = path2.join(projectRoot, resolveEnvFilename(env, false));
3658
- const examplePath = path2.join(projectRoot, resolveEnvFilename(env, true));
3688
+ const localPath = path26.join(projectRoot, resolveEnvFilename(env, false));
3689
+ const examplePath = path26.join(projectRoot, resolveEnvFilename(env, true));
3659
3690
  if (flags.dryRun) {
3660
3691
  p19.log.info(
3661
3692
  pc13.cyan(`[DRY RUN] Would delete ${targetKey} from ${resolveEnvFilename(env, false)} and ${resolveEnvFilename(env, true)}`)
3662
3693
  );
3663
3694
  } else {
3664
- if (fs14.existsSync(localPath)) {
3695
+ if (fs17.existsSync(localPath)) {
3665
3696
  const localEditor = new EnvEditor(localPath);
3666
3697
  localEditor.delete(targetKey);
3667
3698
  localEditor.save();
3668
3699
  }
3669
- if (fs14.existsSync(examplePath)) {
3700
+ if (fs17.existsSync(examplePath)) {
3670
3701
  const exampleEditor = new EnvEditor(examplePath);
3671
3702
  exampleEditor.delete(targetKey);
3672
3703
  exampleEditor.save();
@@ -3691,10 +3722,10 @@ async function runEnvAudit(flags = {}) {
3691
3722
  for (const env of envs) {
3692
3723
  const localFile = resolveEnvFilename(env, false);
3693
3724
  const exampleFile = resolveEnvFilename(env, true);
3694
- const localPath = path2.join(projectRoot, localFile);
3695
- const examplePath = path2.join(projectRoot, exampleFile);
3696
- const localExists = fs14.existsSync(localPath);
3697
- const exampleExists = fs14.existsSync(examplePath);
3725
+ const localPath = path26.join(projectRoot, localFile);
3726
+ const examplePath = path26.join(projectRoot, exampleFile);
3727
+ const localExists = fs17.existsSync(localPath);
3728
+ const exampleExists = fs17.existsSync(examplePath);
3698
3729
  const localEditor = new EnvEditor(localPath);
3699
3730
  const exampleEditor = new EnvEditor(examplePath);
3700
3731
  const localKeys = localExists ? localEditor.getKeys() : [];
@@ -4021,25 +4052,25 @@ function normalizeRegistryUrl(urlOrPath, baseDir) {
4021
4052
  return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
4022
4053
  }
4023
4054
  if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
4024
- return path2.resolve(baseDir, trimmed);
4055
+ return path26.resolve(baseDir, trimmed);
4025
4056
  }
4026
4057
  return trimmed;
4027
4058
  }
4028
4059
  function findLocalRegistry(startDir = process.cwd()) {
4029
- let current = path2.resolve(startDir);
4060
+ let current = path26.resolve(startDir);
4030
4061
  while (true) {
4031
- const candidate = path2.join(current, "mason-registry");
4032
- if (fs14.existsSync(candidate) && fs14.existsSync(path2.join(candidate, "index.json"))) {
4062
+ const candidate = path26.join(current, "mason-registry");
4063
+ if (fs17.existsSync(candidate) && fs17.existsSync(path26.join(candidate, "index.json"))) {
4033
4064
  return candidate;
4034
4065
  }
4035
- const parent = path2.dirname(current);
4066
+ const parent = path26.dirname(current);
4036
4067
  if (parent === current) break;
4037
4068
  current = parent;
4038
4069
  }
4039
4070
  return null;
4040
4071
  }
4041
4072
  function getRegistryBase(projectRoot, override) {
4042
- const rootDir = projectRoot ? path2.resolve(projectRoot) : process.cwd();
4073
+ const rootDir = projectRoot ? path26.resolve(projectRoot) : process.cwd();
4043
4074
  if (override && override.trim()) {
4044
4075
  return normalizeRegistryUrl(override.trim(), rootDir);
4045
4076
  }
@@ -4047,30 +4078,30 @@ function getRegistryBase(projectRoot, override) {
4047
4078
  return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
4048
4079
  }
4049
4080
  const configLocations = [
4050
- path2.join(rootDir, "mason.config.json"),
4051
- path2.join(process.cwd(), "mason.config.json")
4081
+ path26.join(rootDir, "mason.config.json"),
4082
+ path26.join(process.cwd(), "mason.config.json")
4052
4083
  ];
4053
4084
  for (const configPath of configLocations) {
4054
- if (fs14.existsSync(configPath)) {
4085
+ if (fs17.existsSync(configPath)) {
4055
4086
  try {
4056
- const config = JSON.parse(fs14.readFileSync(configPath, "utf-8"));
4087
+ const config = JSON.parse(fs17.readFileSync(configPath, "utf-8"));
4057
4088
  if (config.registry && typeof config.registry === "string") {
4058
- return normalizeRegistryUrl(config.registry.trim(), path2.dirname(configPath));
4089
+ return normalizeRegistryUrl(config.registry.trim(), path26.dirname(configPath));
4059
4090
  }
4060
4091
  } catch {
4061
4092
  }
4062
4093
  }
4063
4094
  }
4064
4095
  const pkgLocations = [
4065
- path2.join(rootDir, "package.json"),
4066
- path2.join(process.cwd(), "package.json")
4096
+ path26.join(rootDir, "package.json"),
4097
+ path26.join(process.cwd(), "package.json")
4067
4098
  ];
4068
4099
  for (const pkgPath of pkgLocations) {
4069
- if (fs14.existsSync(pkgPath)) {
4100
+ if (fs17.existsSync(pkgPath)) {
4070
4101
  try {
4071
- const pkg = JSON.parse(fs14.readFileSync(pkgPath, "utf-8"));
4102
+ const pkg = JSON.parse(fs17.readFileSync(pkgPath, "utf-8"));
4072
4103
  if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
4073
- return normalizeRegistryUrl(pkg.mason.registry.trim(), path2.dirname(pkgPath));
4104
+ return normalizeRegistryUrl(pkg.mason.registry.trim(), path26.dirname(pkgPath));
4074
4105
  }
4075
4106
  } catch {
4076
4107
  }
@@ -4118,16 +4149,16 @@ async function fetchRegistryFile(registryBase, relPath) {
4118
4149
  if (baseDir.startsWith("file://")) {
4119
4150
  baseDir = fileURLToPath(baseDir);
4120
4151
  }
4121
- let filePath = path2.resolve(baseDir, cleanRelPath);
4122
- if (!fs14.existsSync(filePath)) {
4123
- const inSrcPath = path2.resolve(baseDir, "src", cleanRelPath);
4124
- if (fs14.existsSync(inSrcPath)) {
4152
+ let filePath = path26.resolve(baseDir, cleanRelPath);
4153
+ if (!fs17.existsSync(filePath)) {
4154
+ const inSrcPath = path26.resolve(baseDir, "src", cleanRelPath);
4155
+ if (fs17.existsSync(inSrcPath)) {
4125
4156
  filePath = inSrcPath;
4126
4157
  } else {
4127
4158
  throw new Error(`Registry file not found at ${filePath}`);
4128
4159
  }
4129
4160
  }
4130
- return fs14.promises.readFile(filePath, "utf-8");
4161
+ return fs17.promises.readFile(filePath, "utf-8");
4131
4162
  }
4132
4163
  async function fetchRegistryIndex(registryBase) {
4133
4164
  const content = await fetchRegistryFile(registryBase, "index.json");
@@ -4189,19 +4220,19 @@ function findClosestMatch(target, candidates) {
4189
4220
  return null;
4190
4221
  }
4191
4222
  function detectPackageManager(projectRoot) {
4192
- if (fs14.existsSync(path2.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
4193
- if (fs14.existsSync(path2.join(projectRoot, "yarn.lock"))) return "yarn";
4194
- if (fs14.existsSync(path2.join(projectRoot, "bun.lockb")) || fs14.existsSync(path2.join(projectRoot, "bun.lock"))) return "bun";
4223
+ if (fs17.existsSync(path26.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
4224
+ if (fs17.existsSync(path26.join(projectRoot, "yarn.lock"))) return "yarn";
4225
+ if (fs17.existsSync(path26.join(projectRoot, "bun.lockb")) || fs17.existsSync(path26.join(projectRoot, "bun.lock"))) return "bun";
4195
4226
  return "npm";
4196
4227
  }
4197
4228
  function getMissingNpmPackages(projectRoot, requiredPackages) {
4198
4229
  if (!requiredPackages || requiredPackages.length === 0) return [];
4199
- const pkgJsonPath = path2.join(projectRoot, "package.json");
4200
- if (!fs14.existsSync(pkgJsonPath)) {
4230
+ const pkgJsonPath = path26.join(projectRoot, "package.json");
4231
+ if (!fs17.existsSync(pkgJsonPath)) {
4201
4232
  return requiredPackages;
4202
4233
  }
4203
4234
  try {
4204
- const pkg = JSON.parse(fs14.readFileSync(pkgJsonPath, "utf-8"));
4235
+ const pkg = JSON.parse(fs17.readFileSync(pkgJsonPath, "utf-8"));
4205
4236
  const installed = /* @__PURE__ */ new Set([
4206
4237
  ...Object.keys(pkg.dependencies || {}),
4207
4238
  ...Object.keys(pkg.devDependencies || {}),
@@ -4240,12 +4271,12 @@ function installNpmPackages(projectRoot, packages, pm) {
4240
4271
  });
4241
4272
  }
4242
4273
  function getAllFiles(dir, baseDir = dir) {
4243
- if (!fs14.existsSync(dir)) return [];
4244
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
4274
+ if (!fs17.existsSync(dir)) return [];
4275
+ const entries = fs17.readdirSync(dir, { withFileTypes: true });
4245
4276
  const files = [];
4246
4277
  for (const entry of entries) {
4247
- const fullPath = path2.join(dir, entry.name);
4248
- const relPath = path2.relative(baseDir, fullPath).replace(/\\/g, "/");
4278
+ const fullPath = path26.join(dir, entry.name);
4279
+ const relPath = path26.relative(baseDir, fullPath).replace(/\\/g, "/");
4249
4280
  if (entry.isDirectory()) {
4250
4281
  if (entry.name === "__tests__" || entry.name.startsWith(".")) {
4251
4282
  continue;
@@ -4261,13 +4292,13 @@ function getAllFiles(dir, baseDir = dir) {
4261
4292
  return files.sort();
4262
4293
  }
4263
4294
  function computeDirectoryHash(dirPath, files) {
4264
- if (!fs14.existsSync(dirPath)) return "";
4295
+ if (!fs17.existsSync(dirPath)) return "";
4265
4296
  const hash = createHash("sha256");
4266
4297
  const fileList = (getAllFiles(dirPath)).sort();
4267
4298
  for (const relativePath of fileList) {
4268
- const absolutePath = path2.join(dirPath, relativePath);
4269
- if (!fs14.existsSync(absolutePath)) continue;
4270
- const content = fs14.readFileSync(absolutePath, "utf8");
4299
+ const absolutePath = path26.join(dirPath, relativePath);
4300
+ if (!fs17.existsSync(absolutePath)) continue;
4301
+ const content = fs17.readFileSync(absolutePath, "utf8");
4271
4302
  const normalizedContent = content.replace(/\r\n/g, "\n");
4272
4303
  hash.update(relativePath);
4273
4304
  hash.update(normalizedContent);
@@ -4305,17 +4336,17 @@ function getModuleBaseDir(projectRoot, modType) {
4305
4336
  return modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
4306
4337
  }
4307
4338
  const folder = modType === "shared" ? "shared" : "features";
4308
- return path2.join(projectRoot, "src", folder);
4339
+ return path26.join(projectRoot, "src", folder);
4309
4340
  }
4310
4341
  function getExistingModuleDir(projectRoot, modType, modName) {
4311
4342
  const folder = modType === "shared" ? "shared" : "features";
4312
4343
  const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
4313
- const inConfigured = path2.join(configuredDir, modName);
4314
- if (fs14.existsSync(inConfigured)) return inConfigured;
4315
- const inSrc = path2.join(projectRoot, "src", folder, modName);
4316
- if (fs14.existsSync(inSrc)) return inSrc;
4317
- const inRoot = path2.join(projectRoot, folder, modName);
4318
- if (fs14.existsSync(inRoot)) return inRoot;
4344
+ const inConfigured = path26.join(configuredDir, modName);
4345
+ if (fs17.existsSync(inConfigured)) return inConfigured;
4346
+ const inSrc = path26.join(projectRoot, "src", folder, modName);
4347
+ if (fs17.existsSync(inSrc)) return inSrc;
4348
+ const inRoot = path26.join(projectRoot, folder, modName);
4349
+ if (fs17.existsSync(inRoot)) return inRoot;
4319
4350
  return null;
4320
4351
  }
4321
4352
  function checkModuleExists(projectRoot, modType, modName) {
@@ -4326,14 +4357,14 @@ function getAvailableLocalModules(projectRoot, modType) {
4326
4357
  const configuredDir = modType === "shared" ? getConfiguredSharedDir(projectRoot) : getConfiguredFeaturesDir(projectRoot);
4327
4358
  const dirs = [
4328
4359
  configuredDir,
4329
- path2.join(projectRoot, "src", folder),
4330
- path2.join(projectRoot, folder)
4360
+ path26.join(projectRoot, "src", folder),
4361
+ path26.join(projectRoot, folder)
4331
4362
  ];
4332
4363
  const set = /* @__PURE__ */ new Set();
4333
4364
  for (const dir of dirs) {
4334
- if (fs14.existsSync(dir)) {
4365
+ if (fs17.existsSync(dir)) {
4335
4366
  try {
4336
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
4367
+ const entries = fs17.readdirSync(dir, { withFileTypes: true });
4337
4368
  for (const e of entries) {
4338
4369
  if (e.isDirectory() && !e.name.startsWith(".")) {
4339
4370
  set.add(e.name);
@@ -4535,7 +4566,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4535
4566
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${programmaticAlias}/`);
4536
4567
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${programmaticAlias}`);
4537
4568
  const baseDir = getModuleBaseDir(projectRoot, modType);
4538
- const displayDest = path2.relative(projectRoot, path2.join(baseDir, programmaticAlias));
4569
+ const displayDest = path26.relative(projectRoot, path26.join(baseDir, programmaticAlias));
4539
4570
  p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
4540
4571
  resolutions.set(key, {
4541
4572
  type: modType,
@@ -4723,7 +4754,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4723
4754
  globalAliasMap.set(`@/${folder}/${registryName}/`, `@/${folder}/${newName}/`);
4724
4755
  globalAliasMap.set(`@/${folder}/${registryName}`, `@/${folder}/${newName}`);
4725
4756
  const baseDir = getModuleBaseDir(projectRoot, modType);
4726
- const displayDest = path2.relative(projectRoot, path2.join(baseDir, newName));
4757
+ const displayDest = path26.relative(projectRoot, path26.join(baseDir, newName));
4727
4758
  p19.log.step(`Importing '${registryName}' into '${displayDest}'...`);
4728
4759
  resolutions.set(key, {
4729
4760
  type: modType,
@@ -4775,7 +4806,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4775
4806
  continue;
4776
4807
  }
4777
4808
  const folder = res.type === "shared" ? "shared" : "features";
4778
- const targetDir = path2.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
4809
+ const targetDir = path26.join(getModuleBaseDir(projectRoot, res.type), res.targetName);
4779
4810
  const isOverwrite = Boolean(flags.overwrite || res.action === "overwrite");
4780
4811
  if (key !== rootKey) {
4781
4812
  p19.log.step(
@@ -4785,30 +4816,30 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4785
4816
  if (res.action === "overwrite" && !flags.dryRun) {
4786
4817
  const existingDir = getExistingModuleDir(projectRoot, res.type, res.targetName);
4787
4818
  if (existingDir && existingDir !== targetDir) {
4788
- fs14.rmSync(existingDir, { recursive: true, force: true });
4819
+ fs17.rmSync(existingDir, { recursive: true, force: true });
4789
4820
  }
4790
- if (fs14.existsSync(targetDir)) {
4791
- fs14.rmSync(targetDir, { recursive: true, force: true });
4821
+ if (fs17.existsSync(targetDir)) {
4822
+ fs17.rmSync(targetDir, { recursive: true, force: true });
4792
4823
  }
4793
4824
  }
4794
4825
  for (const file of res.manifest.files || []) {
4795
4826
  if (file === "registry.json") continue;
4796
4827
  if (!includeTests && isTestFile(file)) continue;
4797
4828
  const remoteFilePath = `${folder}/${res.registryName}/${file}`;
4798
- const localFilePath = path2.join(targetDir, file);
4799
- if (fs14.existsSync(localFilePath) && !isOverwrite) {
4829
+ const localFilePath = path26.join(targetDir, file);
4830
+ if (fs17.existsSync(localFilePath) && !isOverwrite) {
4800
4831
  p19.log.warn(
4801
- ` \u21B3 Skipping existing file: ${pc13.dim(path2.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4832
+ ` \u21B3 Skipping existing file: ${pc13.dim(path26.relative(projectRoot, localFilePath))} (use --overwrite to replace)`
4802
4833
  );
4803
4834
  continue;
4804
4835
  }
4805
4836
  let content = await fetchRegistryFile(registryBase, remoteFilePath);
4806
4837
  content = rewriteImports(content, globalAliasMap);
4807
4838
  if (!flags.dryRun) {
4808
- fs14.mkdirSync(path2.dirname(localFilePath), { recursive: true });
4809
- fs14.writeFileSync(localFilePath, content, "utf-8");
4839
+ fs17.mkdirSync(path26.dirname(localFilePath), { recursive: true });
4840
+ fs17.writeFileSync(localFilePath, content, "utf-8");
4810
4841
  }
4811
- const displayPath = path2.relative(projectRoot, localFilePath);
4842
+ const displayPath = path26.relative(projectRoot, localFilePath);
4812
4843
  totalWrittenFiles.push(displayPath);
4813
4844
  p19.log.message(` ${pc13.green("+")} ${displayPath}`);
4814
4845
  }
@@ -4834,7 +4865,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
4834
4865
  } else {
4835
4866
  const rootRes = resolutions.get(rootKey);
4836
4867
  const baseDir = getModuleBaseDir(projectRoot, type);
4837
- const displayTarget = path2.relative(projectRoot, path2.join(baseDir, rootRes.targetName));
4868
+ const displayTarget = path26.relative(projectRoot, path26.join(baseDir, rootRes.targetName));
4838
4869
  p19.log.success(
4839
4870
  pc13.green(
4840
4871
  `Successfully imported ${type} '${name}' (${totalWrittenFiles.length} file${totalWrittenFiles.length === 1 ? "" : "s"} placed in ${displayTarget})`
@@ -4853,19 +4884,19 @@ var DependencyRuleError = class extends Error {
4853
4884
  }
4854
4885
  };
4855
4886
  function isTestFile2(filePath, projectRoot) {
4856
- const norm = (projectRoot ? path2.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
4857
- const base = path2.basename(norm);
4887
+ const norm = (projectRoot ? path26.relative(projectRoot, filePath) : filePath).replace(/\\/g, "/");
4888
+ const base = path26.basename(norm);
4858
4889
  return base.endsWith(".test.ts") || base.endsWith(".test.js") || base.endsWith(".spec.ts") || base.endsWith(".spec.js") || base.endsWith(".d.ts") || norm.includes("/__tests__/") || norm.startsWith("__tests__/") || /(?:^|\/)tests?\//.test(norm);
4859
4890
  }
4860
4891
  function scanDirFiles(dir, projectRoot) {
4861
- if (!fs14.existsSync(dir)) return [];
4862
- const entries = fs14.readdirSync(dir, { withFileTypes: true });
4892
+ if (!fs17.existsSync(dir)) return [];
4893
+ const entries = fs17.readdirSync(dir, { withFileTypes: true });
4863
4894
  const files = [];
4864
4895
  for (const entry of entries) {
4865
4896
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "__tests__" || entry.name.startsWith(".")) {
4866
4897
  continue;
4867
4898
  }
4868
- const fullPath = path2.join(dir, entry.name);
4899
+ const fullPath = path26.join(dir, entry.name);
4869
4900
  if (entry.isDirectory()) {
4870
4901
  files.push(...scanDirFiles(fullPath, projectRoot));
4871
4902
  } else if (entry.isFile() && (entry.name.endsWith(".ts") || entry.name.endsWith(".js")) && !isTestFile2(fullPath, projectRoot)) {
@@ -4874,101 +4905,194 @@ function scanDirFiles(dir, projectRoot) {
4874
4905
  }
4875
4906
  return files;
4876
4907
  }
4877
- function classifyFile(fullPath, projectRoot) {
4878
- const rel = path2.relative(projectRoot, fullPath).replace(/\\/g, "/");
4879
- const featureMatch = rel.match(/(?:^|\/)(?:src\/)?features\/([^/]+)\/([^/]+)(?:\/.*)?$/);
4880
- if (featureMatch && featureMatch[1] && featureMatch[2]) {
4881
- const feature = featureMatch[1];
4882
- const subfolder = featureMatch[2];
4883
- let layer;
4884
- if (subfolder === "domain" || subfolder === "errors") {
4885
- layer = "domain";
4886
- } else if (subfolder === "useCases" || subfolder === "usecases") {
4887
- layer = "useCases";
4888
- } else if (subfolder === "services") {
4889
- layer = "services";
4890
- } else if (subfolder === "infrastructure") {
4891
- layer = "infrastructure";
4892
- } else if (subfolder === "pxExpress" || subfolder === "presentation" || subfolder === "handlers" || subfolder.startsWith("px")) {
4893
- layer = "presentation";
4894
- }
4895
- return { moduleType: "feature", moduleName: feature, feature, layer };
4896
- }
4897
- const sharedMatch = rel.match(/(?:^|\/)(?:src\/)?shared\/([^/]+)(?:\/(.*))?$/);
4898
- if (sharedMatch && sharedMatch[1]) {
4899
- const moduleName = sharedMatch[1];
4900
- const rest = sharedMatch[2] || "";
4901
- let layer = "root";
4902
- if (rest.startsWith("domain/") || rest === "domain" || rest.startsWith("errors/") || rest === "errors") {
4903
- layer = "domain";
4904
- } else if (rest.startsWith("ports/") || rest === "ports") {
4905
- layer = "ports";
4906
- } else if (rest.startsWith("infrastructure/") || rest === "infrastructure") {
4907
- layer = "infrastructure";
4908
- }
4909
- return { moduleType: "shared", moduleName, feature: moduleName, layer };
4908
+ function classifyFile(fullPathAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
4909
+ if (!sharedBaseAbs || !errorsBaseAbs) {
4910
+ const paths = getResolvedLintPaths(featuresBaseAbs, loadLintConfig(featuresBaseAbs));
4911
+ featuresBaseAbs = paths.featuresBase;
4912
+ sharedBaseAbs = paths.sharedBase;
4913
+ errorsBaseAbs = paths.errorsBase;
4914
+ }
4915
+ const normFullPath = path26.resolve(fullPathAbs);
4916
+ const normErrorsBase = path26.resolve(errorsBaseAbs);
4917
+ const normSharedBase = path26.resolve(sharedBaseAbs);
4918
+ const normFeaturesBase = path26.resolve(featuresBaseAbs);
4919
+ const relErrors = path26.relative(normErrorsBase, normFullPath).replace(/\\/g, "/");
4920
+ if (!relErrors.startsWith("..") && !path26.isAbsolute(relErrors)) {
4921
+ return {
4922
+ moduleType: "error",
4923
+ moduleName: "global-errors",
4924
+ feature: "global-errors",
4925
+ layer: "domain"
4926
+ };
4927
+ }
4928
+ const relShared = path26.relative(normSharedBase, normFullPath).replace(/\\/g, "/");
4929
+ if (!relShared.startsWith("..") && !path26.isAbsolute(relShared)) {
4930
+ const parts = relShared.split("/").filter(Boolean);
4931
+ if (parts.length >= 1) {
4932
+ const moduleName = parts[0];
4933
+ const rest = parts.slice(1).join("/");
4934
+ let layer = "root";
4935
+ if (rest.startsWith("domain/") || rest === "domain" || rest.startsWith("errors/") || rest === "errors") {
4936
+ layer = "domain";
4937
+ } else if (rest.startsWith("ports/") || rest === "ports") {
4938
+ layer = "ports";
4939
+ } else if (rest.startsWith("infrastructure/") || rest === "infrastructure") {
4940
+ layer = "infrastructure";
4941
+ }
4942
+ return { moduleType: "shared", moduleName, feature: moduleName, layer };
4943
+ }
4944
+ }
4945
+ const relFeatures = path26.relative(normFeaturesBase, normFullPath).replace(/\\/g, "/");
4946
+ if (!relFeatures.startsWith("..") && !path26.isAbsolute(relFeatures)) {
4947
+ const parts = relFeatures.split("/").filter(Boolean);
4948
+ if (parts.length >= 2) {
4949
+ const moduleName = parts[0];
4950
+ const subfolder = parts[1];
4951
+ let layer;
4952
+ if (subfolder === "domain" || subfolder === "errors") {
4953
+ layer = "domain";
4954
+ } else if (subfolder === "useCases" || subfolder === "usecases") {
4955
+ layer = "useCases";
4956
+ } else if (subfolder === "services") {
4957
+ layer = "services";
4958
+ } else if (subfolder === "infrastructure") {
4959
+ layer = "infrastructure";
4960
+ } else if (subfolder === "pxExpress" || subfolder === "presentation" || subfolder === "handlers" || subfolder.startsWith("px")) {
4961
+ layer = "presentation";
4962
+ }
4963
+ return { moduleType: "feature", moduleName, feature: moduleName, layer };
4964
+ }
4910
4965
  }
4911
4966
  return {};
4912
4967
  }
4913
- function classifyTarget(specifier, sourceFileAbs, projectRoot) {
4968
+ function classifyTarget(specifier, sourceFileAbs, projectRoot, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs) {
4914
4969
  if (specifier === "@solid-stack/di" || specifier.startsWith("@solid-stack/di/")) {
4915
4970
  return { isExternal: true, isDiFramework: true };
4916
4971
  }
4917
- if (specifier.startsWith("node:") || ["crypto", "fs", "path", "http", "https", "url", "os", "child_process", "stream", "express", "cors", "dotenv"].includes(specifier)) {
4972
+ if (specifier.startsWith("node:") || [
4973
+ "crypto",
4974
+ "fs",
4975
+ "path",
4976
+ "http",
4977
+ "https",
4978
+ "url",
4979
+ "os",
4980
+ "child_process",
4981
+ "stream",
4982
+ "express",
4983
+ "cors",
4984
+ "dotenv"
4985
+ ].includes(specifier)) {
4918
4986
  return { isExternal: true, isDiFramework: false };
4919
4987
  }
4920
- let resolvedRel = null;
4988
+ if (!featuresBaseAbs || !sharedBaseAbs || !errorsBaseAbs) {
4989
+ const paths = getResolvedLintPaths(projectRoot, loadLintConfig(projectRoot));
4990
+ featuresBaseAbs = featuresBaseAbs || paths.featuresBase;
4991
+ sharedBaseAbs = sharedBaseAbs || paths.sharedBase;
4992
+ errorsBaseAbs = errorsBaseAbs || paths.errorsBase;
4993
+ }
4994
+ const isGlobalConfig = specifier === "@configs" || specifier.startsWith("@configs/") || specifier === "@/configs" || specifier.startsWith("@/configs/") || specifier === "~/configs" || specifier.startsWith("~/configs/") || specifier === "configs" || specifier.startsWith("configs/");
4995
+ let targetAbs = null;
4921
4996
  if (specifier.startsWith(".")) {
4922
- const abs = path2.resolve(path2.dirname(sourceFileAbs), specifier);
4923
- resolvedRel = path2.relative(projectRoot, abs).replace(/\\/g, "/");
4924
- } else if (specifier.startsWith("@/features/") || specifier.startsWith("~/features/")) {
4925
- resolvedRel = specifier.replace(/^[@~]\//, "src/");
4926
- } else if (specifier.startsWith("@features/")) {
4927
- resolvedRel = "src/" + specifier.slice(1);
4928
- } else if (specifier.startsWith("@/shared/") || specifier.startsWith("~/shared/")) {
4929
- resolvedRel = specifier.replace(/^[@~]\//, "src/");
4930
- } else if (specifier.startsWith("@shared/")) {
4931
- resolvedRel = "src/" + specifier.slice(1);
4932
- } else if (specifier.startsWith("@/configs/") || specifier.startsWith("~/configs/") || specifier === "@/configs" || specifier === "~/configs") {
4933
- resolvedRel = specifier.replace(/^[@~]\//, "src/");
4934
- } else if (specifier === "@configs" || specifier.startsWith("@configs/")) {
4935
- resolvedRel = "src/" + specifier.slice(1);
4936
- } else if (specifier.startsWith("@/") || specifier.startsWith("~/")) {
4937
- resolvedRel = "src/" + specifier.slice(2);
4938
- } else if (specifier.startsWith("features/") || specifier.startsWith("shared/") || specifier.startsWith("configs/") || specifier === "configs") {
4939
- resolvedRel = specifier;
4940
- }
4941
- if (!resolvedRel) {
4942
- return { isExternal: true, isDiFramework: false };
4997
+ targetAbs = path26.resolve(path26.dirname(sourceFileAbs), specifier);
4998
+ } else {
4999
+ const featRel = path26.relative(projectRoot, featuresBaseAbs).replace(/\\/g, "/");
5000
+ const sharedRel = path26.relative(projectRoot, sharedBaseAbs).replace(/\\/g, "/");
5001
+ const errorsRel = path26.relative(projectRoot, errorsBaseAbs).replace(/\\/g, "/");
5002
+ const featPrefixes = ["features"];
5003
+ if (featRel && featRel !== "." && !featRel.startsWith("..")) {
5004
+ featPrefixes.push(featRel);
5005
+ const b = path26.basename(featRel);
5006
+ if (!featPrefixes.includes(b)) featPrefixes.push(b);
5007
+ }
5008
+ const sharedPrefixes = ["shared"];
5009
+ if (sharedRel && sharedRel !== "." && !sharedRel.startsWith("..")) {
5010
+ sharedPrefixes.push(sharedRel);
5011
+ const b = path26.basename(sharedRel);
5012
+ if (!sharedPrefixes.includes(b)) sharedPrefixes.push(b);
5013
+ }
5014
+ const errorsPrefixes = ["errors"];
5015
+ if (errorsRel && errorsRel !== "." && !errorsRel.startsWith("..")) {
5016
+ errorsPrefixes.push(errorsRel);
5017
+ const b = path26.basename(errorsRel);
5018
+ if (!errorsPrefixes.includes(b)) errorsPrefixes.push(b);
5019
+ }
5020
+ for (const prefix of featPrefixes) {
5021
+ const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
5022
+ const m = specifier.match(aliasRegex);
5023
+ if (m && m[2]) {
5024
+ targetAbs = path26.resolve(featuresBaseAbs, m[2]);
5025
+ break;
5026
+ }
5027
+ }
5028
+ if (!targetAbs) {
5029
+ for (const prefix of sharedPrefixes) {
5030
+ const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}\\/(.*)$`);
5031
+ const m = specifier.match(aliasRegex);
5032
+ if (m && m[2]) {
5033
+ targetAbs = path26.resolve(sharedBaseAbs, m[2]);
5034
+ break;
5035
+ }
5036
+ }
5037
+ }
5038
+ if (!targetAbs) {
5039
+ for (const prefix of errorsPrefixes) {
5040
+ const aliasRegex = new RegExp(`^([@~]\\/|@)?${prefix}(?:\\/(.*))?$`);
5041
+ const m = specifier.match(aliasRegex);
5042
+ if (m) {
5043
+ targetAbs = path26.resolve(errorsBaseAbs, m[2] || "");
5044
+ break;
5045
+ }
5046
+ }
5047
+ }
5048
+ if (!targetAbs && (specifier.startsWith("@/") || specifier.startsWith("~/"))) {
5049
+ const sub = specifier.slice(2);
5050
+ const srcCandidate = path26.resolve(projectRoot, "src", sub);
5051
+ const rootCandidate = path26.resolve(projectRoot, sub);
5052
+ if (fs17.existsSync(srcCandidate)) {
5053
+ targetAbs = srcCandidate;
5054
+ } else if (fs17.existsSync(rootCandidate)) {
5055
+ targetAbs = rootCandidate;
5056
+ } else {
5057
+ targetAbs = srcCandidate;
5058
+ }
5059
+ }
5060
+ if (!targetAbs) {
5061
+ const featCandidate = path26.resolve(featuresBaseAbs, specifier);
5062
+ if (fs17.existsSync(featCandidate) || fs17.existsSync(featCandidate + ".ts") || fs17.existsSync(featCandidate + ".js")) {
5063
+ targetAbs = featCandidate;
5064
+ }
5065
+ }
5066
+ }
5067
+ if (!targetAbs) {
5068
+ return { isExternal: true, isDiFramework: false, isGlobalConfig };
5069
+ }
5070
+ const normTarget = path26.resolve(targetAbs);
5071
+ const srcConfig = path26.resolve(projectRoot, "src/configs");
5072
+ const rootConfig = path26.resolve(projectRoot, "configs");
5073
+ const isTargetConfig = isGlobalConfig || normTarget === srcConfig || normTarget.startsWith(srcConfig + path26.sep) || normTarget === rootConfig || normTarget.startsWith(rootConfig + path26.sep);
5074
+ const classification = classifyFile(targetAbs, featuresBaseAbs, sharedBaseAbs, errorsBaseAbs);
5075
+ if (classification.moduleType === "error") {
5076
+ return {
5077
+ isExternal: false,
5078
+ isDiFramework: false,
5079
+ isGlobalError: true,
5080
+ isTargetError: true
5081
+ };
4943
5082
  }
4944
- const isGlobalConfig = resolvedRel.startsWith("src/configs/") || resolvedRel === "src/configs" || resolvedRel.startsWith("configs/") || resolvedRel === "configs";
4945
- const featMatch = resolvedRel.match(/(?:^|\/)(?:src\/)?features\/([^/]+)\/([^/]+)(?:\/(.*))?$/);
4946
- if (featMatch && featMatch[1] && featMatch[2]) {
4947
- const targetFeature = featMatch[1];
4948
- const targetSubfolder = featMatch[2];
4949
- const rest = featMatch[3] || "";
4950
- let targetLayer;
4951
- if (targetSubfolder === "domain" || targetSubfolder === "errors") {
4952
- targetLayer = "domain";
4953
- } else if (targetSubfolder === "useCases" || targetSubfolder === "usecases") {
4954
- targetLayer = "useCases";
4955
- } else if (targetSubfolder === "services") {
4956
- targetLayer = "services";
4957
- } else if (targetSubfolder === "infrastructure") {
4958
- targetLayer = "infrastructure";
4959
- } else if (targetSubfolder === "pxExpress" || targetSubfolder === "presentation" || targetSubfolder === "handlers" || targetSubfolder.startsWith("px")) {
4960
- targetLayer = "presentation";
4961
- }
4962
- const basePart = rest ? path2.basename(rest) : targetSubfolder;
4963
- const targetFileName = basePart.replace(/\.(js|ts|mjs|cjs)$/, "");
5083
+ if (classification.moduleType === "feature") {
5084
+ const rel = path26.relative(featuresBaseAbs, targetAbs).replace(/\\/g, "/");
5085
+ const parts = rel.split("/").filter(Boolean);
5086
+ const targetSubfolder = parts[1];
5087
+ const targetFileName = path26.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
4964
5088
  const isTargetPort = /^I[A-Z]/.test(targetFileName);
4965
5089
  const isTargetRepo = isTargetPort && targetFileName.includes("Repo");
4966
- const isTargetError = targetSubfolder === "errors" || rest.startsWith("errors/") || rest.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
5090
+ const isTargetError = targetSubfolder === "errors" || rel.includes("/errors/") || targetFileName.endsWith("Error") || targetFileName.endsWith("Errors");
4967
5091
  return {
4968
5092
  isExternal: false,
4969
5093
  isDiFramework: false,
4970
- targetFeature,
4971
- targetLayer,
5094
+ targetFeature: classification.moduleName,
5095
+ targetLayer: classification.layer,
4972
5096
  targetSubfolder,
4973
5097
  targetFileName,
4974
5098
  isTargetPort,
@@ -4976,25 +5100,21 @@ function classifyTarget(specifier, sourceFileAbs, projectRoot) {
4976
5100
  isTargetError
4977
5101
  };
4978
5102
  }
4979
- const sharedMatch = resolvedRel.match(/(?:^|\/)(?:src\/)?shared\/([^/]+)(?:\/(.*))?$/);
4980
- if (sharedMatch) {
4981
- const targetSharedModule = sharedMatch[1];
4982
- const sharedRest = sharedMatch[2] || "";
4983
- const isSharedRoot = !sharedRest.includes("/");
4984
- const isSharedPorts = sharedRest.startsWith("ports/") || sharedRest === "ports";
4985
- const isSharedInfra = sharedRest.startsWith("infrastructure/") || sharedRest === "infrastructure";
4986
- const isSharedDomain = sharedRest.startsWith("domain/") || sharedRest === "domain";
5103
+ if (classification.moduleType === "shared") {
5104
+ const targetFileName = path26.basename(targetAbs).replace(/\.(js|ts|mjs|cjs)$/, "");
5105
+ const isTargetError = classification.layer === "domain" && (targetFileName.endsWith("Error") || targetFileName.endsWith("Errors") || targetAbs.includes("/errors/"));
4987
5106
  return {
4988
5107
  isExternal: false,
4989
5108
  isDiFramework: false,
4990
- targetSharedModule,
4991
- isSharedRoot,
4992
- isSharedPorts,
4993
- isSharedInfra,
4994
- isSharedDomain
5109
+ targetSharedModule: classification.moduleName,
5110
+ isSharedRoot: classification.layer === "root",
5111
+ isSharedPorts: classification.layer === "ports",
5112
+ isSharedInfra: classification.layer === "infrastructure",
5113
+ isSharedDomain: classification.layer === "domain",
5114
+ isTargetError
4995
5115
  };
4996
5116
  }
4997
- return { isExternal: true, isDiFramework: false, isGlobalConfig };
5117
+ return { isExternal: true, isDiFramework: false, isGlobalConfig: isTargetConfig };
4998
5118
  }
4999
5119
  function extractImportStatements(content) {
5000
5120
  const statements = [];
@@ -5116,22 +5236,50 @@ var FeatureDependencyGraph = class {
5116
5236
  return dfs(start);
5117
5237
  }
5118
5238
  };
5119
- function discoverAllFeatures(projectRoot) {
5120
- const featuresBase = resolveFeaturesDir(projectRoot);
5121
- if (!fs14.existsSync(featuresBase)) return [];
5122
- const entries = fs14.readdirSync(featuresBase, { withFileTypes: true });
5123
- return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "__tests__" && e.name !== "tests").map((e) => ({
5239
+ function discoverAllFeatures(featuresBaseAbs, excludeDirs = []) {
5240
+ const base = featuresBaseAbs;
5241
+ if (!fs17.existsSync(base)) return [];
5242
+ const excludeSet = new Set(excludeDirs.map((d) => path26.resolve(d)));
5243
+ const entries = fs17.readdirSync(base, { withFileTypes: true });
5244
+ return entries.filter((e) => {
5245
+ if (!e.isDirectory()) return false;
5246
+ if (e.name.startsWith(".")) return false;
5247
+ if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
5248
+ return false;
5249
+ }
5250
+ const resolved = path26.resolve(base, e.name);
5251
+ for (const ex of excludeSet) {
5252
+ if (ex === resolved || ex.startsWith(resolved + path26.sep) || resolved.startsWith(ex + path26.sep)) {
5253
+ return false;
5254
+ }
5255
+ }
5256
+ return true;
5257
+ }).map((e) => ({
5124
5258
  name: e.name,
5125
- dir: path2.join(featuresBase, e.name)
5259
+ dir: path26.join(base, e.name)
5126
5260
  })).sort((a, b) => a.name.localeCompare(b.name));
5127
5261
  }
5128
- function discoverAllShared(projectRoot) {
5129
- const sharedBase = resolveSharedDir(projectRoot);
5130
- if (!fs14.existsSync(sharedBase)) return [];
5131
- const entries = fs14.readdirSync(sharedBase, { withFileTypes: true });
5132
- return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && e.name !== "__tests__" && e.name !== "tests").map((e) => ({
5262
+ function discoverAllShared(sharedBaseAbs, excludeDirs = []) {
5263
+ const base = sharedBaseAbs;
5264
+ if (!fs17.existsSync(base)) return [];
5265
+ const excludeSet = new Set(excludeDirs.map((d) => path26.resolve(d)));
5266
+ const entries = fs17.readdirSync(base, { withFileTypes: true });
5267
+ return entries.filter((e) => {
5268
+ if (!e.isDirectory()) return false;
5269
+ if (e.name.startsWith(".")) return false;
5270
+ if (["__tests__", "tests", "node_modules", "dist", "coverage", "scratch"].includes(e.name)) {
5271
+ return false;
5272
+ }
5273
+ const resolved = path26.resolve(base, e.name);
5274
+ for (const ex of excludeSet) {
5275
+ if (ex === resolved || ex.startsWith(resolved + path26.sep) || resolved.startsWith(ex + path26.sep)) {
5276
+ return false;
5277
+ }
5278
+ }
5279
+ return true;
5280
+ }).map((e) => ({
5133
5281
  name: e.name,
5134
- dir: path2.join(sharedBase, e.name)
5282
+ dir: path26.join(base, e.name)
5135
5283
  })).sort((a, b) => a.name.localeCompare(b.name));
5136
5284
  }
5137
5285
  function extractClassMethods(content, className) {
@@ -5287,7 +5435,7 @@ function extractClassMethods(content, className) {
5287
5435
  return methods;
5288
5436
  }
5289
5437
  function checkUseCaseShape(featureName, filePath, relativePath, content, addViolation) {
5290
- const fileName = path2.basename(filePath);
5438
+ const fileName = path26.basename(filePath);
5291
5439
  const lines = content.split(/\r?\n/);
5292
5440
  const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
5293
5441
  if (!classMatch) {
@@ -5383,7 +5531,7 @@ private ${m.name}(...) { ... }`
5383
5531
  }
5384
5532
  }
5385
5533
  function checkServiceShape(featureName, filePath, relativePath, content, addViolation) {
5386
- const fileName = path2.basename(filePath);
5534
+ const fileName = path26.basename(filePath);
5387
5535
  const lines = content.split(/\r?\n/);
5388
5536
  const classMatch = content.match(/(?:export\s+(?:default\s+)?)?class\s+([A-Za-z0-9_]+)/);
5389
5537
  if (!classMatch) {
@@ -5482,11 +5630,40 @@ function lintCodebase(options = {}) {
5482
5630
  const startTime = Date.now();
5483
5631
  const projectRoot = options.projectRoot || findProjectRoot(options.targetDir || process.cwd());
5484
5632
  const failFast = !options.complete;
5485
- const features = discoverAllFeatures(projectRoot);
5486
- const sharedModules = discoverAllShared(projectRoot);
5633
+ const lintConfig = loadLintConfig(projectRoot);
5634
+ const { featuresBase, sharedBase, errorsBase } = getResolvedLintPaths(projectRoot, lintConfig);
5635
+ const missingFoundations = [];
5636
+ if (!fs17.existsSync(featuresBase)) missingFoundations.push("features/");
5637
+ if (!fs17.existsSync(sharedBase)) missingFoundations.push("shared/");
5638
+ if (!fs17.existsSync(errorsBase)) missingFoundations.push("errors/");
5639
+ if (missingFoundations.length > 0) {
5640
+ throw new DependencyRuleError(
5641
+ `Project Structure Violation: Missing required foundational directories: ${missingFoundations.join(", ")}. Every project must contain 'features/', 'shared/', and 'errors/'.`,
5642
+ [],
5643
+ true
5644
+ );
5645
+ }
5646
+ const baseErrorRelative = lintConfig.baseErrorPath || path26.relative(projectRoot, path26.join(errorsBase, "AppError.ts")).replace(/\\/g, "/");
5647
+ const baseErrorAbs = path26.resolve(projectRoot, baseErrorRelative);
5648
+ if (!fs17.existsSync(baseErrorAbs)) {
5649
+ throw new DependencyRuleError(
5650
+ `Project Structure Violation: Base error class not found at '${baseErrorRelative}'. Create this file or update 'baseErrorPath' in mason-lint.config.json.`,
5651
+ [],
5652
+ true
5653
+ );
5654
+ }
5655
+ const baseErrorContent = fs17.readFileSync(baseErrorAbs, "utf-8");
5656
+ const baseClassMatch = baseErrorContent.match(
5657
+ /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)/
5658
+ );
5659
+ const baseErrorClassName = baseClassMatch ? baseClassMatch[1] : "AppError";
5660
+ const features = discoverAllFeatures(featuresBase, [sharedBase, errorsBase]);
5661
+ const sharedModules = discoverAllShared(sharedBase, [featuresBase, errorsBase]);
5662
+ const errorModule = { name: "global-errors", dir: errorsBase, type: "error" };
5487
5663
  const allModules = [
5488
5664
  ...features.map((f) => ({ ...f, type: "feature" })),
5489
- ...sharedModules.map((s) => ({ ...s, type: "shared" }))
5665
+ ...sharedModules.map((s) => ({ ...s, type: "shared" })),
5666
+ errorModule
5490
5667
  ];
5491
5668
  const graph = new FeatureDependencyGraph();
5492
5669
  const allViolations = [];
@@ -5516,7 +5693,7 @@ Code: ${v.code}`;
5516
5693
  scannedFilesCount: 0,
5517
5694
  passed: true,
5518
5695
  violations: [],
5519
- layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : ["domain", "ports", "infrastructure", "root"]
5696
+ layersChecked: mod.type === "feature" ? ["domain", "useCases", "services", "infrastructure", "presentation"] : mod.type === "shared" ? ["domain", "ports", "infrastructure", "root"] : ["domain"]
5520
5697
  });
5521
5698
  }
5522
5699
  for (const mod of allModules) {
@@ -5524,13 +5701,128 @@ Code: ${v.code}`;
5524
5701
  const allFiles = scanDirFiles(mod.dir, projectRoot);
5525
5702
  modResult.scannedFilesCount = allFiles.length;
5526
5703
  for (const file of allFiles) {
5527
- const relPath = path2.relative(projectRoot, file).replace(/\\/g, "/");
5528
- const { moduleType, moduleName, layer } = classifyFile(file, projectRoot);
5704
+ const relPath = path26.relative(projectRoot, file).replace(/\\/g, "/");
5705
+ const { moduleType, moduleName, layer } = classifyFile(file, featuresBase, sharedBase, errorsBase);
5529
5706
  if (!layer || !moduleType || !moduleName) continue;
5530
- const content = fs14.readFileSync(file, "utf-8");
5707
+ const content = fs17.readFileSync(file, "utf-8");
5531
5708
  const statements = extractImportStatements(content);
5532
- const baseName = path2.basename(file);
5709
+ const baseName = path26.basename(file);
5533
5710
  const isBarrel = baseName === "index.ts" || baseName === "index.js";
5711
+ const throwErrorRegex = /\bthrow\s+(?:new\s+)?Error\s*\(/g;
5712
+ let throwMatch;
5713
+ while ((throwMatch = throwErrorRegex.exec(content)) !== null) {
5714
+ const charIndex = throwMatch.index;
5715
+ const lineNum = content.substring(0, charIndex).split("\n").length;
5716
+ const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || throwMatch[0];
5717
+ if (lineCode.startsWith("//")) continue;
5718
+ addViolation({
5719
+ feature: moduleName,
5720
+ layer,
5721
+ file,
5722
+ relativePath: relPath,
5723
+ line: lineNum,
5724
+ category: "Plain Error Throw Violation",
5725
+ code: lineCode,
5726
+ ruleId: "E3_PLAIN_ERROR_THROW",
5727
+ severity: "error",
5728
+ advice: `Throwing plain 'Error' is prohibited. You must throw '${baseErrorClassName}' or a domain-specific error class that inherits from it.`,
5729
+ fixExample: {
5730
+ bad: lineCode,
5731
+ good: `throw new ${baseErrorClassName}("Something went wrong");`
5732
+ }
5733
+ });
5734
+ }
5735
+ const isErrorFile = relPath.includes("/errors/") || relPath.startsWith("errors/") || relPath.startsWith("src/errors/") || moduleType === "error" || baseName.endsWith("Error.ts") || baseName.endsWith("Errors.ts") || baseName.endsWith("Error.js") || baseName.endsWith("Errors.js");
5736
+ const isBaseErrorFile = path26.resolve(file) === path26.resolve(baseErrorAbs);
5737
+ if (isErrorFile && !isBaseErrorFile) {
5738
+ const classRegex = /(?:export\s+(?:default\s+)?)?(?:abstract\s+)?class\s+([A-Za-z0-9_]+)(?:<[^>]+>)?(?:\s+extends\s+([A-Za-z0-9_]+))?/g;
5739
+ let classMatch;
5740
+ while ((classMatch = classRegex.exec(content)) !== null) {
5741
+ const className = classMatch[1];
5742
+ const extendsName = classMatch[2];
5743
+ const charIndex = classMatch.index;
5744
+ const lineNum = content.substring(0, charIndex).split("\n").length;
5745
+ const lineCode = content.split(/\r?\n/)[lineNum - 1]?.trim() || classMatch[0];
5746
+ if (lineCode.startsWith("//")) continue;
5747
+ if (!extendsName) {
5748
+ addViolation({
5749
+ feature: moduleName,
5750
+ layer,
5751
+ file,
5752
+ relativePath: relPath,
5753
+ line: lineNum,
5754
+ category: "Error Inheritance Violation",
5755
+ code: lineCode,
5756
+ ruleId: "E4_MISSING_INHERITANCE",
5757
+ severity: "error",
5758
+ advice: `Error class '${className}' must extend '${baseErrorClassName}' (or another class that derives from it).`,
5759
+ fixExample: {
5760
+ bad: lineCode,
5761
+ good: `export class ${className} extends ${baseErrorClassName} { ... }`
5762
+ }
5763
+ });
5764
+ } else if (extendsName === "Error") {
5765
+ addViolation({
5766
+ feature: moduleName,
5767
+ layer,
5768
+ file,
5769
+ relativePath: relPath,
5770
+ line: lineNum,
5771
+ category: "Error Inheritance Violation",
5772
+ code: lineCode,
5773
+ ruleId: "E5_NATIVE_ERROR_INHERITANCE",
5774
+ severity: "error",
5775
+ advice: `Error class '${className}' extends the native 'Error'. It must extend your base error class ('${baseErrorClassName}').`,
5776
+ fixExample: {
5777
+ bad: lineCode,
5778
+ good: `export class ${className} extends ${baseErrorClassName} { ... }`
5779
+ }
5780
+ });
5781
+ }
5782
+ }
5783
+ }
5784
+ if (moduleType === "error") {
5785
+ for (const stmt of statements) {
5786
+ const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
5787
+ if (target.targetFeature) {
5788
+ addViolation({
5789
+ feature: "global-errors",
5790
+ layer: "domain",
5791
+ file,
5792
+ relativePath: relPath,
5793
+ line: stmt.line,
5794
+ category: "Global Error Purity Violation",
5795
+ code: stmt.rawCode,
5796
+ ruleId: "E1_FEATURE_DEPENDENCY",
5797
+ severity: "error",
5798
+ advice: `Global errors must not depend on features ('${target.targetFeature}'). Global errors represent pure domain or system states.`,
5799
+ fixExample: {
5800
+ bad: stmt.rawCode,
5801
+ good: "// Global errors must not reference specific features. Keep error types pure."
5802
+ }
5803
+ });
5804
+ }
5805
+ if (target.targetSharedModule && !target.isSharedDomain) {
5806
+ addViolation({
5807
+ feature: "global-errors",
5808
+ layer: "domain",
5809
+ file,
5810
+ relativePath: relPath,
5811
+ line: stmt.line,
5812
+ category: "Global Error Purity Violation",
5813
+ code: stmt.rawCode,
5814
+ ruleId: "E2_SHARED_NON_DOMAIN",
5815
+ severity: "error",
5816
+ advice: `Global errors can only depend on shared domain elements (e.g., value objects). They cannot depend on shared ports or infrastructure.`,
5817
+ fixExample: {
5818
+ bad: stmt.rawCode,
5819
+ good: "// Depend only on pure domain types from shared modules."
5820
+ }
5821
+ });
5822
+ }
5823
+ }
5824
+ continue;
5825
+ }
5534
5826
  if (moduleType === "shared") {
5535
5827
  if (layer === "domain") {
5536
5828
  const lines = content.split(/\r?\n/);
@@ -5557,7 +5849,7 @@ Code: ${v.code}`;
5557
5849
  }
5558
5850
  }
5559
5851
  for (const stmt of statements) {
5560
- const target = classifyTarget(stmt.specifier, file, projectRoot);
5852
+ const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
5561
5853
  if (target.targetFeature) {
5562
5854
  addViolation({
5563
5855
  feature: moduleName,
@@ -5687,7 +5979,7 @@ Code: ${v.code}`;
5687
5979
  }
5688
5980
  }
5689
5981
  for (const stmt of statements) {
5690
- const target = classifyTarget(stmt.specifier, file, projectRoot);
5982
+ const target = classifyTarget(stmt.specifier, file, projectRoot, featuresBase, sharedBase, errorsBase);
5691
5983
  if (target.targetFeature && target.targetFeature !== feat.name) {
5692
5984
  const evidence = {
5693
5985
  fromFeature: feat.name,