@solid-stack/mason 1.0.11 → 1.0.12

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,7 +2,7 @@
2
2
  import { cac } from 'cac';
3
3
  import * as p3 from '@clack/prompts';
4
4
  import pc13 from 'picocolors';
5
- import path6 from 'path';
5
+ import path15 from 'path';
6
6
  import fs7 from 'fs';
7
7
  import { fileURLToPath } from 'url';
8
8
  import Handlebars from 'handlebars';
@@ -10,7 +10,7 @@ import { execSync } from 'child_process';
10
10
 
11
11
  function writeFileSafely(absolutePath, content, options = {}) {
12
12
  const projectRoot = options.projectRoot || process.cwd();
13
- const relPath = path6.relative(projectRoot, absolutePath).replace(/\\/g, "/");
13
+ const relPath = path15.relative(projectRoot, absolutePath).replace(/\\/g, "/");
14
14
  const existedBefore = fs7.existsSync(absolutePath);
15
15
  if (options.dryRun) {
16
16
  return {
@@ -30,7 +30,7 @@ function writeFileSafely(absolutePath, content, options = {}) {
30
30
  existedBefore: true
31
31
  };
32
32
  }
33
- const dir = path6.dirname(absolutePath);
33
+ const dir = path15.dirname(absolutePath);
34
34
  if (!fs7.existsSync(dir)) {
35
35
  fs7.mkdirSync(dir, { recursive: true });
36
36
  }
@@ -125,20 +125,20 @@ function toStubName(ifaceName) {
125
125
  return `Stub${base}`;
126
126
  }
127
127
  function findProjectRoot(startDir = process.cwd()) {
128
- let current = path6.resolve(startDir);
128
+ let current = path15.resolve(startDir);
129
129
  while (true) {
130
- if (fs7.existsSync(path6.join(current, "package.json"))) {
130
+ if (fs7.existsSync(path15.join(current, "package.json"))) {
131
131
  return current;
132
132
  }
133
- const parent = path6.dirname(current);
133
+ const parent = path15.dirname(current);
134
134
  if (parent === current) {
135
- return path6.resolve(startDir);
135
+ return path15.resolve(startDir);
136
136
  }
137
137
  current = parent;
138
138
  }
139
139
  }
140
140
  function resolveFeaturesDir(projectRoot) {
141
- return path6.join(projectRoot, "features");
141
+ return path15.join(projectRoot, "features");
142
142
  }
143
143
  function listExistingFeatures(projectRoot) {
144
144
  const featuresDir = resolveFeaturesDir(projectRoot);
@@ -154,14 +154,14 @@ function listDomainInterfaces(projectRoot, featureName) {
154
154
  const results = [];
155
155
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
156
156
  for (const feat of features) {
157
- const domainDir = path6.join(resolveFeaturesDir(projectRoot), feat, "domain");
157
+ const domainDir = path15.join(resolveFeaturesDir(projectRoot), feat, "domain");
158
158
  if (!fs7.existsSync(domainDir)) continue;
159
159
  try {
160
160
  const files = fs7.readdirSync(domainDir);
161
161
  for (const file of files) {
162
162
  if (file.endsWith(".ts") && !file.endsWith(".test.ts") && !file.endsWith(".d.ts")) {
163
163
  const name = file.replace(/\.ts$/, "");
164
- const fullPath = path6.join(domainDir, file);
164
+ const fullPath = path15.join(domainDir, file);
165
165
  if (/^I[A-Z]/.test(name)) {
166
166
  results.push({
167
167
  name,
@@ -192,7 +192,7 @@ function listDomainEntities(projectRoot, featureName) {
192
192
  const results = [];
193
193
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
194
194
  for (const feat of features) {
195
- const domainDir = path6.join(resolveFeaturesDir(projectRoot), feat, "domain");
195
+ const domainDir = path15.join(resolveFeaturesDir(projectRoot), feat, "domain");
196
196
  if (!fs7.existsSync(domainDir)) continue;
197
197
  try {
198
198
  const files = fs7.readdirSync(domainDir);
@@ -202,7 +202,7 @@ function listDomainEntities(projectRoot, featureName) {
202
202
  if (!/^I[A-Z]/.test(name)) {
203
203
  results.push({
204
204
  name,
205
- fullPath: path6.join(domainDir, file),
205
+ fullPath: path15.join(domainDir, file),
206
206
  feature: feat
207
207
  });
208
208
  }
@@ -217,7 +217,7 @@ function listUseCases(projectRoot, featureName) {
217
217
  const results = [];
218
218
  const features = featureName ? [featureName] : listExistingFeatures(projectRoot);
219
219
  for (const feat of features) {
220
- const useCasesDir = path6.join(resolveFeaturesDir(projectRoot), feat, "useCases");
220
+ const useCasesDir = path15.join(resolveFeaturesDir(projectRoot), feat, "useCases");
221
221
  if (!fs7.existsSync(useCasesDir)) continue;
222
222
  try {
223
223
  const files = fs7.readdirSync(useCasesDir);
@@ -226,7 +226,7 @@ function listUseCases(projectRoot, featureName) {
226
226
  const name = file.replace(/\.ts$/, "");
227
227
  results.push({
228
228
  name,
229
- fullPath: path6.join(useCasesDir, file),
229
+ fullPath: path15.join(useCasesDir, file),
230
230
  feature: feat
231
231
  });
232
232
  }
@@ -255,30 +255,30 @@ function getTemplatesDir() {
255
255
  if (cachedTemplatesDir && fs7.existsSync(cachedTemplatesDir)) {
256
256
  return cachedTemplatesDir;
257
257
  }
258
- const currentDir = typeof __dirname !== "undefined" ? __dirname : path6.dirname(fileURLToPath(import.meta.url));
259
- const cand1 = path6.resolve(currentDir, "../../templates");
258
+ const currentDir = typeof __dirname !== "undefined" ? __dirname : path15.dirname(fileURLToPath(import.meta.url));
259
+ const cand1 = path15.resolve(currentDir, "../../templates");
260
260
  if (fs7.existsSync(cand1)) {
261
261
  cachedTemplatesDir = cand1;
262
262
  return cand1;
263
263
  }
264
- const cand2 = path6.resolve(currentDir, "../templates");
264
+ const cand2 = path15.resolve(currentDir, "../templates");
265
265
  if (fs7.existsSync(cand2)) {
266
266
  cachedTemplatesDir = cand2;
267
267
  return cand2;
268
268
  }
269
- const cand3 = path6.resolve(currentDir, "templates");
269
+ const cand3 = path15.resolve(currentDir, "templates");
270
270
  if (fs7.existsSync(cand3)) {
271
271
  cachedTemplatesDir = cand3;
272
272
  return cand3;
273
273
  }
274
274
  let curr = currentDir;
275
275
  while (true) {
276
- const candTemplates = path6.join(curr, "templates");
276
+ const candTemplates = path15.join(curr, "templates");
277
277
  if (fs7.existsSync(candTemplates)) {
278
278
  cachedTemplatesDir = candTemplates;
279
279
  return candTemplates;
280
280
  }
281
- const parent = path6.dirname(curr);
281
+ const parent = path15.dirname(curr);
282
282
  if (parent === curr) break;
283
283
  curr = parent;
284
284
  }
@@ -286,7 +286,7 @@ function getTemplatesDir() {
286
286
  }
287
287
  var templateCache = /* @__PURE__ */ new Map();
288
288
  function loadTemplate(relativePath) {
289
- const fullPath = path6.isAbsolute(relativePath) ? relativePath : path6.join(getTemplatesDir(), relativePath);
289
+ const fullPath = path15.isAbsolute(relativePath) ? relativePath : path15.join(getTemplatesDir(), relativePath);
290
290
  return fs7.readFileSync(fullPath, "utf-8");
291
291
  }
292
292
  function renderTemplateFile(relativePath, context) {
@@ -374,8 +374,8 @@ function generateDomainEntityCode(name, kind = "interface", properties = []) {
374
374
  function generateDomain(options) {
375
375
  const projectRoot = options.projectRoot || findProjectRoot();
376
376
  const entityName = toPascalCase(options.name);
377
- const targetDir = options.targetDir || path6.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
378
- const filePath = path6.join(targetDir, `${entityName}.ts`);
377
+ const targetDir = options.targetDir || path15.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
378
+ const filePath = path15.join(targetDir, `${entityName}.ts`);
379
379
  const code = generateDomainEntityCode(
380
380
  entityName,
381
381
  options.kind || "interface",
@@ -424,8 +424,8 @@ function generateInterfaceCode(ifaceName, kind = "abstractClass", entityName, me
424
424
  function generateInterface(options) {
425
425
  const projectRoot = options.projectRoot || findProjectRoot();
426
426
  const ifaceName = ensureInterfacePrefix(options.name);
427
- const targetDir = options.targetDir || path6.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
428
- const filePath = path6.join(targetDir, `${ifaceName}.ts`);
427
+ const targetDir = options.targetDir || path15.join(resolveFeaturesDir(projectRoot), options.feature, "domain");
428
+ const filePath = path15.join(targetDir, `${ifaceName}.ts`);
429
429
  const code = generateInterfaceCode(
430
430
  ifaceName,
431
431
  options.kind || "abstractClass",
@@ -670,21 +670,21 @@ function findInterfaceFile(projectRoot, ifaceName, featureName, customPath) {
670
670
  ];
671
671
  const features = featureName ? [featureName, ...listExistingFeatures(projectRoot).filter((f) => f !== featureName)] : listExistingFeatures(projectRoot);
672
672
  for (const feat of features) {
673
- const domainDir = path6.join(resolveFeaturesDir(projectRoot), feat, "domain");
673
+ const domainDir = path15.join(resolveFeaturesDir(projectRoot), feat, "domain");
674
674
  if (!fs7.existsSync(domainDir)) continue;
675
675
  for (const cand of candidateNames) {
676
- const p1 = path6.join(domainDir, `${cand}.ts`);
676
+ const p1 = path15.join(domainDir, `${cand}.ts`);
677
677
  if (fs7.existsSync(p1)) {
678
678
  return { filePath: p1, ifaceName: cand, feature: feat };
679
679
  }
680
680
  }
681
681
  }
682
682
  for (const cand of candidateNames) {
683
- const p1 = path6.join(projectRoot, "domain", `${cand}.ts`);
683
+ const p1 = path15.join(projectRoot, "domain", `${cand}.ts`);
684
684
  if (fs7.existsSync(p1)) return { filePath: p1, ifaceName: cand };
685
- const p22 = path6.join(projectRoot, "src", "domain", `${cand}.ts`);
685
+ const p22 = path15.join(projectRoot, "src", "domain", `${cand}.ts`);
686
686
  if (fs7.existsSync(p22)) return { filePath: p22, ifaceName: cand };
687
- const p32 = path6.join(projectRoot, "interfaces", `${cand}.ts`);
687
+ const p32 = path15.join(projectRoot, "interfaces", `${cand}.ts`);
688
688
  if (fs7.existsSync(p32)) return { filePath: p32, ifaceName: cand };
689
689
  }
690
690
  return null;
@@ -784,7 +784,7 @@ function generateStubCode(stubClassName, ifaceName, entityName, featureName, met
784
784
  const entitiesVar = `${entityVar}s`;
785
785
  const idKey = `${entityVar}Id`;
786
786
  const impPath = ifaceImportPath || `../domain/${ifaceName}.js`;
787
- const entityImportPath = ifaceImportPath ? `${path6.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
787
+ const entityImportPath = ifaceImportPath ? `${path15.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
788
788
  const importsByFrom = /* @__PURE__ */ new Map();
789
789
  for (const imp of domainImports) {
790
790
  if (imp.name === ifaceName || imp.name === baseEntity) continue;
@@ -833,7 +833,7 @@ function generateConcreteInfraCode(className, ifaceName, dependencies = [], meth
833
833
  if (baseEntity) {
834
834
  finalMethods = synthesizeCrudMethods(baseEntity);
835
835
  if (!finalDomainImports.some((imp) => imp.name === baseEntity)) {
836
- const importPath = ifaceImportPath ? `${path6.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
836
+ const importPath = ifaceImportPath ? `${path15.dirname(ifaceImportPath)}/${baseEntity}.js` : `../domain/${baseEntity}.js`;
837
837
  finalDomainImports.push({
838
838
  name: baseEntity,
839
839
  from: importPath,
@@ -878,8 +878,8 @@ function generateInfrastructure(options) {
878
878
  const projectRoot = options.projectRoot || findProjectRoot();
879
879
  const className = toPascalCase(options.name);
880
880
  const isFeatureScope = options.scope !== "global" && Boolean(options.feature);
881
- const targetDir = options.targetDir || (isFeatureScope ? path6.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path6.join(projectRoot, "infrastructure"));
882
- const filePath = path6.join(targetDir, `${className}.ts`);
881
+ const targetDir = options.targetDir || (isFeatureScope ? path15.join(resolveFeaturesDir(projectRoot), options.feature, "infrastructure") : path15.join(projectRoot, "infrastructure"));
882
+ const filePath = path15.join(targetDir, `${className}.ts`);
883
883
  let code;
884
884
  if (options.implementsInterface) {
885
885
  const rawIfaceName = options.implementsInterface;
@@ -893,7 +893,7 @@ function generateInfrastructure(options) {
893
893
  const ifaceFilePath = hierarchy.ifaceFilePath;
894
894
  let ifaceImportPath;
895
895
  if (ifaceFilePath) {
896
- let rel = path6.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
896
+ let rel = path15.relative(targetDir, ifaceFilePath).replace(/\\/g, "/").replace(/\.ts$/, ".js");
897
897
  if (!rel.startsWith(".")) rel = "./" + rel;
898
898
  ifaceImportPath = rel;
899
899
  } else if (isFeatureScope) {
@@ -906,8 +906,8 @@ function generateInfrastructure(options) {
906
906
  if (new RegExp(`\\b${item.name}\\b`).test(allSignatures)) {
907
907
  let fromPath;
908
908
  if (imp.specifier.startsWith(".") && ifaceFilePath) {
909
- const absTarget = path6.resolve(path6.dirname(ifaceFilePath), imp.specifier);
910
- let rel = path6.relative(targetDir, absTarget).replace(/\\/g, "/");
909
+ const absTarget = path15.resolve(path15.dirname(ifaceFilePath), imp.specifier);
910
+ let rel = path15.relative(targetDir, absTarget).replace(/\\/g, "/");
911
911
  if (!rel.startsWith(".")) rel = "./" + rel;
912
912
  fromPath = rel;
913
913
  } else {
@@ -1094,8 +1094,8 @@ function generateUseCase(options) {
1094
1094
  const files = [];
1095
1095
  const projectRoot = options.projectRoot || findProjectRoot();
1096
1096
  const ucName = toPascalCase(options.name);
1097
- const targetDir = options.targetDir || path6.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1098
- const filePath = path6.join(targetDir, `${ucName}.ts`);
1097
+ const targetDir = options.targetDir || path15.join(resolveFeaturesDir(projectRoot), options.feature, "useCases");
1098
+ const filePath = path15.join(targetDir, `${ucName}.ts`);
1099
1099
  const code = generateUseCaseCode(
1100
1100
  ucName,
1101
1101
  options.dependencies || [],
@@ -1111,7 +1111,7 @@ function generateUseCase(options) {
1111
1111
  })
1112
1112
  );
1113
1113
  if (options.includeTest) {
1114
- const testPath = path6.join(targetDir, `${ucName}.test.ts`);
1114
+ const testPath = path15.join(targetDir, `${ucName}.test.ts`);
1115
1115
  const testCode = generateUseCaseTestCode(ucName, options.dependencies || []);
1116
1116
  files.push(
1117
1117
  writeFileSafely(testPath, testCode, {
@@ -1179,9 +1179,9 @@ function generateHttpHandler(options) {
1179
1179
  const files = [];
1180
1180
  const projectRoot = options.projectRoot || findProjectRoot();
1181
1181
  const className = toPascalCase(options.name);
1182
- const pxDir = path6.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1183
- const handlersDir = path6.join(pxDir, "handlers");
1184
- const pxIndexPath = path6.join(pxDir, "index.ts");
1182
+ const pxDir = path15.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress");
1183
+ const handlersDir = path15.join(pxDir, "handlers");
1184
+ const pxIndexPath = path15.join(pxDir, "index.ts");
1185
1185
  if (!fs7.existsSync(pxIndexPath)) {
1186
1186
  const featurePath = options.routePrefix || `/${options.feature.toLowerCase()}`;
1187
1187
  const pxIndexContent = renderTemplate(expressIndexTemplate, {
@@ -1195,7 +1195,7 @@ function generateHttpHandler(options) {
1195
1195
  })
1196
1196
  );
1197
1197
  }
1198
- const handlerFilePath = path6.join(handlersDir, `${className}.ts`);
1198
+ const handlerFilePath = path15.join(handlersDir, `${className}.ts`);
1199
1199
  const code = generateHttpHandlerCode(
1200
1200
  className,
1201
1201
  options.method || "get",
@@ -1216,7 +1216,7 @@ function generateHttpHandler(options) {
1216
1216
  return files;
1217
1217
  }
1218
1218
  function updateOrGenerateDiProvider(featureDir, featureName, bindings = [], options = {}) {
1219
- const providerPath = path6.join(featureDir, "diProvider.ts");
1219
+ const providerPath = path15.join(featureDir, "diProvider.ts");
1220
1220
  const pascalFeature = toPascalCase(featureName);
1221
1221
  const providerModuleName = `${pascalFeature}Provider`;
1222
1222
  if (fs7.existsSync(providerPath) && !options.overwrite) {
@@ -1279,7 +1279,7 @@ function generateFeature(options) {
1279
1279
  const projectRoot = options.projectRoot || findProjectRoot();
1280
1280
  const rawName = options.name.trim();
1281
1281
  const featureName = toCamelCase(rawName);
1282
- const targetDir = options.targetDir || path6.join(resolveFeaturesDir(projectRoot), featureName);
1282
+ const targetDir = options.targetDir || path15.join(resolveFeaturesDir(projectRoot), featureName);
1283
1283
  const routePath = options.routePath || `/${featureName}`;
1284
1284
  const rawEntities = [];
1285
1285
  if (options.entityNames && options.entityNames.length > 0) {
@@ -1300,7 +1300,7 @@ function generateFeature(options) {
1300
1300
  const domainFile = generateDomain({
1301
1301
  name: ent,
1302
1302
  feature: featureName,
1303
- targetDir: path6.join(targetDir, "domain"),
1303
+ targetDir: path15.join(targetDir, "domain"),
1304
1304
  projectRoot,
1305
1305
  dryRun: options.dryRun,
1306
1306
  overwrite: options.overwrite,
@@ -1320,7 +1320,7 @@ function generateFeature(options) {
1320
1320
  const ifaceFile = generateInterface({
1321
1321
  name: ifaceName,
1322
1322
  feature: featureName,
1323
- targetDir: path6.join(targetDir, "domain"),
1323
+ targetDir: path15.join(targetDir, "domain"),
1324
1324
  entityName: ent,
1325
1325
  projectRoot,
1326
1326
  dryRun: options.dryRun,
@@ -1337,7 +1337,7 @@ function generateFeature(options) {
1337
1337
  const stubFile = generateInfrastructure({
1338
1338
  name: stubName,
1339
1339
  feature: featureName,
1340
- targetDir: path6.join(targetDir, "infrastructure"),
1340
+ targetDir: path15.join(targetDir, "infrastructure"),
1341
1341
  kind: "stub",
1342
1342
  implementsInterface: ifaceName,
1343
1343
  entityName: ent,
@@ -1379,7 +1379,7 @@ function generateFeature(options) {
1379
1379
  const ucFiles = generateUseCase({
1380
1380
  name: ucName,
1381
1381
  feature: featureName,
1382
- targetDir: path6.join(targetDir, "useCases"),
1382
+ targetDir: path15.join(targetDir, "useCases"),
1383
1383
  dependencies: options.includeInterface !== false ? [ifaceName] : [],
1384
1384
  inputProperties: [{ name: idParam, type: "string" }],
1385
1385
  outputProperties: [{ name: entVar, type: primaryEntity }],
@@ -1424,7 +1424,7 @@ function generateFeature(options) {
1424
1424
  const ucFiles = generateUseCase({
1425
1425
  name: ucName,
1426
1426
  feature: featureName,
1427
- targetDir: path6.join(targetDir, "useCases"),
1427
+ targetDir: path15.join(targetDir, "useCases"),
1428
1428
  dependencies: deps,
1429
1429
  inputProperties: [{ name: entVar, type: ent }],
1430
1430
  outputProperties: [{ name: entVar, type: ent }],
@@ -1457,7 +1457,7 @@ function generateFeature(options) {
1457
1457
  const ucFiles = generateUseCase({
1458
1458
  name: ucName,
1459
1459
  feature: featureName,
1460
- targetDir: path6.join(targetDir, "useCases"),
1460
+ targetDir: path15.join(targetDir, "useCases"),
1461
1461
  dependencies: deps,
1462
1462
  inputProperties: [{ name: idParam, type: "string" }],
1463
1463
  outputProperties: [{ name: entVar, type: ent }],
@@ -1490,7 +1490,7 @@ function generateFeature(options) {
1490
1490
  const ucFiles = generateUseCase({
1491
1491
  name: ucName,
1492
1492
  feature: featureName,
1493
- targetDir: path6.join(targetDir, "useCases"),
1493
+ targetDir: path15.join(targetDir, "useCases"),
1494
1494
  dependencies: deps,
1495
1495
  inputProperties: [
1496
1496
  { name: "page", type: "number", optional: true },
@@ -1534,7 +1534,7 @@ function generateFeature(options) {
1534
1534
  const ucFiles = generateUseCase({
1535
1535
  name: ucName,
1536
1536
  feature: featureName,
1537
- targetDir: path6.join(targetDir, "useCases"),
1537
+ targetDir: path15.join(targetDir, "useCases"),
1538
1538
  dependencies: deps,
1539
1539
  inputProperties: [{ name: entVar, type: ent }],
1540
1540
  outputProperties: [{ name: entVar, type: ent }],
@@ -1567,7 +1567,7 @@ function generateFeature(options) {
1567
1567
  const ucFiles = generateUseCase({
1568
1568
  name: ucName,
1569
1569
  feature: featureName,
1570
- targetDir: path6.join(targetDir, "useCases"),
1570
+ targetDir: path15.join(targetDir, "useCases"),
1571
1571
  dependencies: deps,
1572
1572
  inputProperties: [{ name: idParam, type: "string" }],
1573
1573
  outputProperties: [{ name: "success", type: "boolean" }],
@@ -1604,7 +1604,7 @@ function generateFeature(options) {
1604
1604
  const ucFiles = generateUseCase({
1605
1605
  name: ucName,
1606
1606
  feature: featureName,
1607
- targetDir: path6.join(targetDir, "useCases"),
1607
+ targetDir: path15.join(targetDir, "useCases"),
1608
1608
  dependencies: customUc.dependencies || [],
1609
1609
  inputProperties: customUc.inputProperties || [],
1610
1610
  outputProperties: customUc.outputProperties || [],
@@ -1636,8 +1636,8 @@ function generateFeature(options) {
1636
1636
  }
1637
1637
  }
1638
1638
  if (options.includeExpress !== false) {
1639
- const pxDir = path6.join(targetDir, "pxExpress");
1640
- const pxIndexPath = path6.join(pxDir, "index.ts");
1639
+ const pxDir = path15.join(targetDir, "pxExpress");
1640
+ const pxIndexPath = path15.join(pxDir, "index.ts");
1641
1641
  if (!files.some((f) => f.absolutePath === pxIndexPath)) {
1642
1642
  const pxIndexContent = renderTemplate(expressIndexTemplate, { routePath });
1643
1643
  files.push(
@@ -1765,7 +1765,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1765
1765
  ).trim();
1766
1766
  }
1767
1767
  const normalizedName = toCamelCase(name);
1768
- const defaultDir = path6.relative(projectRoot, path6.join(resolveFeaturesDir(projectRoot), normalizedName));
1768
+ const defaultDir = path15.relative(projectRoot, path15.join(resolveFeaturesDir(projectRoot), normalizedName));
1769
1769
  let targetDirRel = defaultDir;
1770
1770
  if (!flags.yes && !flags.target) {
1771
1771
  targetDirRel = handleCancel(
@@ -1776,7 +1776,7 @@ async function runFeatureCommand(featureNameArg, flags = {}) {
1776
1776
  })
1777
1777
  ).trim();
1778
1778
  }
1779
- const targetDir = flags.target ? path6.resolve(projectRoot, flags.target) : path6.resolve(projectRoot, targetDirRel);
1779
+ const targetDir = flags.target ? path15.resolve(projectRoot, flags.target) : path15.resolve(projectRoot, targetDirRel);
1780
1780
  let includeExpress = flags.express !== void 0 ? Boolean(flags.express) : true;
1781
1781
  let routePath = `/${normalizedName}`;
1782
1782
  let includeDomain = true;
@@ -2444,7 +2444,7 @@ async function runInfrastructureCommand(classNameArg, flags = {}) {
2444
2444
  const infraFile = generateInfrastructure(options);
2445
2445
  files.push(infraFile);
2446
2446
  if (registerInProvider && scope === "feature" && feature && implementsInterface) {
2447
- const featureDir = path6.join(resolveFeaturesDir(projectRoot), feature);
2447
+ const featureDir = path15.join(resolveFeaturesDir(projectRoot), feature);
2448
2448
  const providerFile = updateOrGenerateDiProvider(
2449
2449
  featureDir,
2450
2450
  feature,
@@ -2606,7 +2606,7 @@ async function runInterfaceCommand(interfaceNameArg, flags = {}) {
2606
2606
  files.push(stubFile);
2607
2607
  }
2608
2608
  if (registerInProvider && generateStub) {
2609
- const featureDir = path6.join(resolveFeaturesDir(projectRoot), feature);
2609
+ const featureDir = path15.join(resolveFeaturesDir(projectRoot), feature);
2610
2610
  const providerFile = updateOrGenerateDiProvider(
2611
2611
  featureDir,
2612
2612
  feature,
@@ -2794,21 +2794,21 @@ function generateInjectable(options) {
2794
2794
  const className = toPascalCase(options.name);
2795
2795
  let targetDir = "";
2796
2796
  if (options.customPath) {
2797
- targetDir = path6.isAbsolute(options.customPath) ? options.customPath : path6.join(projectRoot, options.customPath);
2797
+ targetDir = path15.isAbsolute(options.customPath) ? options.customPath : path15.join(projectRoot, options.customPath);
2798
2798
  } else if (options.location === "feature-service" && options.feature) {
2799
- targetDir = path6.join(resolveFeaturesDir(projectRoot), options.feature, "services");
2799
+ targetDir = path15.join(resolveFeaturesDir(projectRoot), options.feature, "services");
2800
2800
  } else if (options.location === "feature-transformer" && options.feature) {
2801
- targetDir = path6.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
2801
+ targetDir = path15.join(resolveFeaturesDir(projectRoot), options.feature, "pxExpress", "transformers");
2802
2802
  } else if (options.location === "module") {
2803
- targetDir = path6.join(projectRoot, "modules", className);
2803
+ targetDir = path15.join(projectRoot, "modules", className);
2804
2804
  } else {
2805
- targetDir = options.feature ? path6.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path6.join(projectRoot, "shared", className.toLowerCase());
2805
+ targetDir = options.feature ? path15.join(resolveFeaturesDir(projectRoot), options.feature, "services") : path15.join(projectRoot, "shared", className.toLowerCase());
2806
2806
  }
2807
- const filePath = path6.join(targetDir, `${className}.ts`);
2807
+ const filePath = path15.join(targetDir, `${className}.ts`);
2808
2808
  let ifaceName;
2809
2809
  if (options.generateInterface) {
2810
2810
  ifaceName = `I${className}`;
2811
- const ifacePath = path6.join(targetDir, `${ifaceName}.ts`);
2811
+ const ifacePath = path15.join(targetDir, `${ifaceName}.ts`);
2812
2812
  const ifaceContent = renderTemplate(injectableInterfaceTemplate, { ifaceName });
2813
2813
  files.push(
2814
2814
  writeFileSafely(ifacePath, ifaceContent, {
@@ -3042,40 +3042,105 @@ async function handleCreate(type, name, flags = {}) {
3042
3042
  }
3043
3043
  }
3044
3044
  var DEFAULT_REGISTRY_BASE = "https://raw.githubusercontent.com/solid-stack-digital/mason-registry/main";
3045
+ function normalizeRegistryUrl(urlOrPath, baseDir) {
3046
+ let trimmed = urlOrPath.trim();
3047
+ const gitSshRegex = /^git@github\.com:([^/]+)\/([^/]+?)(?:\.git)?$/;
3048
+ const sshMatch = trimmed.match(gitSshRegex);
3049
+ if (sshMatch && sshMatch[1] && sshMatch[2]) {
3050
+ return `https://raw.githubusercontent.com/${sshMatch[1]}/${sshMatch[2]}/main`;
3051
+ }
3052
+ const githubWebRegex = /^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?(?:\/tree\/([^/]+))?\/?$/;
3053
+ const webMatch = trimmed.match(githubWebRegex);
3054
+ if (webMatch && webMatch[1] && webMatch[2]) {
3055
+ const owner = webMatch[1];
3056
+ const repo = webMatch[2];
3057
+ const branch = webMatch[3] || "main";
3058
+ return `https://raw.githubusercontent.com/${owner}/${repo}/${branch}`;
3059
+ }
3060
+ if (baseDir && (trimmed.startsWith("./") || trimmed.startsWith("../") || trimmed === "." || trimmed === "..")) {
3061
+ return path15.resolve(baseDir, trimmed);
3062
+ }
3063
+ return trimmed;
3064
+ }
3065
+ function findLocalRegistry(startDir = process.cwd()) {
3066
+ let current = path15.resolve(startDir);
3067
+ while (true) {
3068
+ const candidate = path15.join(current, "mason-registry");
3069
+ if (fs7.existsSync(candidate) && fs7.existsSync(path15.join(candidate, "index.json"))) {
3070
+ return candidate;
3071
+ }
3072
+ const parent = path15.dirname(current);
3073
+ if (parent === current) break;
3074
+ current = parent;
3075
+ }
3076
+ return null;
3077
+ }
3045
3078
  function getRegistryBase(projectRoot, override) {
3079
+ const rootDir = projectRoot ? path15.resolve(projectRoot) : process.cwd();
3046
3080
  if (override && override.trim()) {
3047
- return override.trim();
3081
+ return normalizeRegistryUrl(override.trim(), rootDir);
3048
3082
  }
3049
3083
  if (process.env.MASON_REGISTRY_URL && process.env.MASON_REGISTRY_URL.trim()) {
3050
- return process.env.MASON_REGISTRY_URL.trim();
3084
+ return normalizeRegistryUrl(process.env.MASON_REGISTRY_URL.trim(), rootDir);
3051
3085
  }
3052
- if (projectRoot) {
3053
- const configPath = path6.join(projectRoot, "mason.config.json");
3086
+ const configLocations = [
3087
+ path15.join(rootDir, "mason.config.json"),
3088
+ path15.join(process.cwd(), "mason.config.json")
3089
+ ];
3090
+ for (const configPath of configLocations) {
3054
3091
  if (fs7.existsSync(configPath)) {
3055
3092
  try {
3056
3093
  const config = JSON.parse(fs7.readFileSync(configPath, "utf-8"));
3057
3094
  if (config.registry && typeof config.registry === "string") {
3058
- return config.registry.trim();
3095
+ return normalizeRegistryUrl(config.registry.trim(), path15.dirname(configPath));
3059
3096
  }
3060
3097
  } catch {
3061
3098
  }
3062
3099
  }
3063
3100
  }
3064
- if (projectRoot) {
3065
- const siblingRegistry = path6.resolve(projectRoot, "../mason-registry");
3066
- if (fs7.existsSync(siblingRegistry) && fs7.existsSync(path6.join(siblingRegistry, "index.json"))) {
3067
- return siblingRegistry;
3101
+ const pkgLocations = [
3102
+ path15.join(rootDir, "package.json"),
3103
+ path15.join(process.cwd(), "package.json")
3104
+ ];
3105
+ for (const pkgPath of pkgLocations) {
3106
+ if (fs7.existsSync(pkgPath)) {
3107
+ try {
3108
+ const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
3109
+ if (pkg.mason?.registry && typeof pkg.mason.registry === "string") {
3110
+ return normalizeRegistryUrl(pkg.mason.registry.trim(), path15.dirname(pkgPath));
3111
+ }
3112
+ } catch {
3113
+ }
3068
3114
  }
3069
3115
  }
3116
+ const localRegistry = findLocalRegistry(rootDir);
3117
+ if (localRegistry) {
3118
+ return localRegistry;
3119
+ }
3070
3120
  return DEFAULT_REGISTRY_BASE;
3071
3121
  }
3072
3122
  async function fetchRegistryFile(registryBase, relPath) {
3073
3123
  const cleanRelPath = relPath.replace(/^\/+/, "");
3074
3124
  if (registryBase.startsWith("http://") || registryBase.startsWith("https://")) {
3075
3125
  const url = `${registryBase.replace(/\/+$/, "")}/${cleanRelPath}`;
3076
- const res = await fetch(url);
3126
+ const headers = {};
3127
+ const token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
3128
+ if (token) {
3129
+ headers["Authorization"] = `Bearer ${token}`;
3130
+ }
3131
+ const res = await fetch(url, { headers });
3077
3132
  if (!res.ok) {
3078
- throw new Error(`Failed to fetch ${cleanRelPath} from ${url} (HTTP ${res.status}: ${res.statusText})`);
3133
+ let extraHint = "";
3134
+ if (res.status === 404) {
3135
+ extraHint = `
3136
+ Possible reasons:
3137
+ \u2022 If using a private GitHub repository, ensure GITHUB_TOKEN is set in your environment.
3138
+ \u2022 If your registry has not yet been pushed to GitHub, point to your local directory:
3139
+ - CLI flag: mason import ... --registry /path/to/mason-registry
3140
+ - Environment: export MASON_REGISTRY_URL=/path/to/mason-registry
3141
+ - Config file: Create mason.config.json with { "registry": "/path/to/mason-registry" }`;
3142
+ }
3143
+ throw new Error(`Failed to fetch ${cleanRelPath} from ${url} (HTTP ${res.status}: ${res.statusText})${extraHint}`);
3079
3144
  }
3080
3145
  return await res.text();
3081
3146
  }
@@ -3083,7 +3148,7 @@ async function fetchRegistryFile(registryBase, relPath) {
3083
3148
  if (baseDir.startsWith("file://")) {
3084
3149
  baseDir = fileURLToPath(baseDir);
3085
3150
  }
3086
- const filePath = path6.resolve(baseDir, cleanRelPath);
3151
+ const filePath = path15.resolve(baseDir, cleanRelPath);
3087
3152
  if (!fs7.existsSync(filePath)) {
3088
3153
  throw new Error(`Registry file not found at ${filePath}`);
3089
3154
  }
@@ -3149,14 +3214,14 @@ function findClosestMatch(target, candidates) {
3149
3214
  return null;
3150
3215
  }
3151
3216
  function detectPackageManager(projectRoot) {
3152
- if (fs7.existsSync(path6.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3153
- if (fs7.existsSync(path6.join(projectRoot, "yarn.lock"))) return "yarn";
3154
- if (fs7.existsSync(path6.join(projectRoot, "bun.lockb")) || fs7.existsSync(path6.join(projectRoot, "bun.lock"))) return "bun";
3217
+ if (fs7.existsSync(path15.join(projectRoot, "pnpm-lock.yaml"))) return "pnpm";
3218
+ if (fs7.existsSync(path15.join(projectRoot, "yarn.lock"))) return "yarn";
3219
+ if (fs7.existsSync(path15.join(projectRoot, "bun.lockb")) || fs7.existsSync(path15.join(projectRoot, "bun.lock"))) return "bun";
3155
3220
  return "npm";
3156
3221
  }
3157
3222
  function getMissingNpmPackages(projectRoot, requiredPackages) {
3158
3223
  if (!requiredPackages || requiredPackages.length === 0) return [];
3159
- const pkgJsonPath = path6.join(projectRoot, "package.json");
3224
+ const pkgJsonPath = path15.join(projectRoot, "package.json");
3160
3225
  if (!fs7.existsSync(pkgJsonPath)) {
3161
3226
  return requiredPackages;
3162
3227
  }
@@ -3283,7 +3348,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3283
3348
  }
3284
3349
  const sharedDeps = manifest.dependencies?.shared || [];
3285
3350
  for (const dep of sharedDeps) {
3286
- const depPath = path6.join(projectRoot, "src", "shared", dep);
3351
+ const depPath = path15.join(projectRoot, "src", "shared", dep);
3287
3352
  if (!fs7.existsSync(depPath) && !importedModules.has(`shared:${dep}`)) {
3288
3353
  p3.log.info(` \u21B3 Resolving required shared dependency: ${pc13.cyan(dep)}`);
3289
3354
  await importModuleRecursive("shared", dep);
@@ -3291,7 +3356,7 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3291
3356
  }
3292
3357
  const featureDeps = manifest.dependencies?.features || [];
3293
3358
  for (const dep of featureDeps) {
3294
- const depPath = path6.join(projectRoot, "src", "features", dep);
3359
+ const depPath = path15.join(projectRoot, "src", "features", dep);
3295
3360
  if (!fs7.existsSync(depPath) && !importedModules.has(`feature:${dep}`)) {
3296
3361
  p3.log.info(` \u21B3 Resolving required feature dependency: ${pc13.cyan(dep)}`);
3297
3362
  await importModuleRecursive("feature", dep);
@@ -3301,21 +3366,21 @@ async function handleImport(typeArg, nameArg, flags = {}) {
3301
3366
  requiredNpmPackages.add(pkg);
3302
3367
  }
3303
3368
  const folder = modType === "shared" ? "shared" : "features";
3304
- const targetDir = path6.join(projectRoot, "src", folder, modName);
3369
+ const targetDir = path15.join(projectRoot, "src", folder, modName);
3305
3370
  for (const file of manifest.files || []) {
3306
3371
  if (file === "registry.json") continue;
3307
3372
  const remoteFilePath = `${folder}/${modName}/${file}`;
3308
- const localFilePath = path6.join(targetDir, file);
3373
+ const localFilePath = path15.join(targetDir, file);
3309
3374
  if (fs7.existsSync(localFilePath) && !flags.overwrite) {
3310
- p3.log.warn(` \u21B3 Skipping existing file: ${pc13.dim(path6.relative(projectRoot, localFilePath))} (use --overwrite to replace)`);
3375
+ p3.log.warn(` \u21B3 Skipping existing file: ${pc13.dim(path15.relative(projectRoot, localFilePath))} (use --overwrite to replace)`);
3311
3376
  continue;
3312
3377
  }
3313
3378
  const content = await fetchRegistryFile(registryBase, remoteFilePath);
3314
3379
  if (!flags.dryRun) {
3315
- fs7.mkdirSync(path6.dirname(localFilePath), { recursive: true });
3380
+ fs7.mkdirSync(path15.dirname(localFilePath), { recursive: true });
3316
3381
  fs7.writeFileSync(localFilePath, content, "utf-8");
3317
3382
  }
3318
- const displayPath = path6.relative(projectRoot, localFilePath);
3383
+ const displayPath = path15.relative(projectRoot, localFilePath);
3319
3384
  totalWrittenFiles.push(displayPath);
3320
3385
  p3.log.message(` ${pc13.green("+")} ${displayPath}`);
3321
3386
  }