@azure-net/cli 5.0.0-3 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/bin/azure-net.js +5 -46
  2. package/bin/azure-net.ts +5 -5
  3. package/package.json +1 -1
  4. package/src/checks/checkDomain.js +8 -38
  5. package/src/checks/checkFoldersStructure.js +0 -14
  6. package/src/checks/checkHooks.js +0 -5
  7. package/src/checks/checkImports.js +0 -1
  8. package/src/checks/checkInternal.js +0 -1
  9. package/src/checks/checkLayerBoundaries.js +50 -54
  10. package/src/checks/checkLayerBoundaries.ts +55 -58
  11. package/src/checks/checkPresenterNames.js +1 -37
  12. package/src/checks/checkPresenterNames.ts +1 -6
  13. package/src/checks/checkProject.js +0 -6
  14. package/src/checks/checkProviderGraph.js +1 -38
  15. package/src/checks/checkProviderNames.js +0 -28
  16. package/src/checks/index.js +0 -1
  17. package/src/generate/layers/generateRepository.js +4 -19
  18. package/src/generate/layers/generateRepositoryMethod.js +0 -12
  19. package/src/generate/layers/generateUseCases.js +0 -6
  20. package/src/generate/layers/repositoryShared.js +41 -104
  21. package/src/generate/layers/repositoryShared.ts +17 -12
  22. package/src/generate/module/generateDomain.js +0 -25
  23. package/src/generate/module/generateModule.js +0 -10
  24. package/src/generate/module/generateModulePreset.js +10 -51
  25. package/src/generate/module/generateModulePreset.ts +9 -8
  26. package/src/generate/module/generatePresenter.js +29 -76
  27. package/src/generate/module/generatePresenter.ts +15 -10
  28. package/src/generate/module/generateRepository.js +0 -17
  29. package/src/generate/module/generateSchemaFromType.js +9 -58
  30. package/src/generate/module/generateSchemaFromType.ts +1 -2
  31. package/src/generate/module/generateTypesJson.js +4 -45
  32. package/src/generate/module/generateUseCases.js +0 -9
  33. package/src/generate/module/repositoryModuleShared.js +21 -98
  34. package/src/generate/module/repositoryModuleShared.ts +14 -10
  35. package/src/generate/ui/generateComponent.js +0 -16
  36. package/src/generate/ui/generateDesignComponent.js +0 -12
  37. package/src/generate/ui/generateWidget.js +0 -12
  38. package/src/init/initAliases.js +42 -64
  39. package/src/init/initAliases.ts +47 -42
  40. package/src/init/initEdges.js +0 -15
  41. package/src/init/initStructure.js +48 -21
  42. package/src/init/initStructure.ts +51 -3
  43. package/src/install/installFresh.js +0 -16
  44. package/src/plugins/addLint.js +11 -46
  45. package/src/scaffolds/createCoreSchema.js +6 -40
  46. package/src/scaffolds/createCoreSchema.ts +6 -18
  47. package/src/scaffolds/createDatasource.js +26 -61
  48. package/src/scaffolds/createDatasource.ts +26 -40
  49. package/src/scaffolds/createDatasourceProvider.js +19 -56
  50. package/src/scaffolds/createDatasourceProvider.ts +20 -34
  51. package/src/scaffolds/createPresenterFactory.js +5 -34
  52. package/src/scaffolds/createPresenterFactory.ts +5 -17
  53. package/src/scaffolds/createProgram.js +1 -5
  54. package/src/scaffolds/createResponse.js +3 -11
  55. package/src/scaffolds/createResponse.ts +3 -1
  56. package/src/scaffolds/createSchemaRule.js +3 -8
  57. package/src/scaffolds/createSchemaRule.ts +3 -1
  58. package/src/scaffolds/createTranslationManager.js +3 -14
  59. package/src/scaffolds/createTranslationManager.ts +3 -1
  60. package/src/utils/contextUtils.js +5 -20
  61. package/src/utils/contextUtils.ts +2 -3
  62. package/src/utils/fileUtils.js +3 -38
  63. package/src/utils/fileUtils.ts +0 -26
  64. package/src/utils/loadConfig.js +1 -14
  65. package/src/utils/loadConfig.ts +1 -3
  66. package/src/utils/sharedFoundation.js +48 -0
  67. package/src/utils/sharedFoundation.ts +70 -0
@@ -5,20 +5,32 @@ import { loadUserConfig } from '../../utils/loadConfig.js';
5
5
  import { normalizeContexts } from '../../init/initAliases.js';
6
6
  import { toCamelCase, toKebabCase, toPascalCase } from '../../utils/contextUtils.js';
7
7
  import { updateIndexTs, writeIfNotExists } from '../../utils/fileUtils.js';
8
-
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../../utils/sharedFoundation.js';
9
+ const toHttpMethod = (value) => {
10
+ const normalized = String(value ?? '').toLowerCase();
11
+ if (
12
+ normalized === 'get' ||
13
+ normalized === 'post' ||
14
+ normalized === 'put' ||
15
+ normalized === 'delete' ||
16
+ normalized === 'head' ||
17
+ normalized === 'options' ||
18
+ normalized === 'patch'
19
+ ) {
20
+ return normalized;
21
+ }
22
+ return 'get';
23
+ };
9
24
  const HTTP_METHODS = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'];
10
-
11
25
  const normalizeAlias = (value, fallback) => {
12
26
  const raw = String(value ?? '').trim();
13
27
  if (!raw.length) return fallback;
14
28
  return raw.startsWith('$') ? raw : `$${raw}`;
15
29
  };
16
-
17
30
  export const ensureRepositoryName = (rawName) => {
18
31
  const normalized = toPascalCase(String(rawName ?? '').trim() || 'Repository');
19
32
  return normalized.endsWith('Repository') ? normalized : `${normalized}Repository`;
20
33
  };
21
-
22
34
  const RESERVED_IDENTIFIERS = new Set([
23
35
  'break',
24
36
  'case',
@@ -67,14 +79,12 @@ const RESERVED_IDENTIFIERS = new Set([
67
79
  'static',
68
80
  'yield'
69
81
  ]);
70
-
71
82
  const toSafeIdentifier = (value) => {
72
83
  const normalized = toCamelCase(value || 'repository');
73
84
  if (!normalized.length) return 'repository';
74
85
  if (RESERVED_IDENTIFIERS.has(normalized)) return `${normalized}Repository`;
75
86
  return normalized;
76
87
  };
77
-
78
88
  export const getRepositoryMeta = (repositoryClassName) => {
79
89
  const className = ensureRepositoryName(repositoryClassName);
80
90
  const baseName = className.replace(/Repository$/, '') || 'Repository';
@@ -82,7 +92,6 @@ export const getRepositoryMeta = (repositoryClassName) => {
82
92
  const repositoryInterfaceName = `I${baseName}Repository`;
83
93
  const useCasesClassName = `${baseName}UseCases`;
84
94
  const repositoryDependencyName = toSafeIdentifier(baseName || 'repository');
85
-
86
95
  return {
87
96
  className,
88
97
  baseName,
@@ -92,25 +101,22 @@ export const getRepositoryMeta = (repositoryClassName) => {
92
101
  repositoryDependencyName
93
102
  };
94
103
  };
95
-
96
104
  export const getConfigState = async () => {
97
105
  const config = await loadUserConfig();
98
106
  const contexts = normalizeContexts(config.contexts);
99
- const coreAlias = normalizeAlias(config.coreAlias, '$core');
100
-
107
+ const { sharedAlias, sharedContext } = await getSharedState();
101
108
  return {
102
109
  contexts,
103
- coreAlias
110
+ sharedAlias,
111
+ sharedContextName: sharedContext.name
104
112
  };
105
113
  };
106
-
107
114
  export const selectContext = async (message = 'Select context:') => {
108
115
  const { contexts } = await getConfigState();
109
116
  if (!contexts.length) {
110
117
  console.error('❌ Cannot continue: `contexts` is missing or empty in azure-net.config.ts/js');
111
118
  return null;
112
119
  }
113
-
114
120
  const { context } = await prompts({
115
121
  type: 'select',
116
122
  name: 'context',
@@ -118,30 +124,25 @@ export const selectContext = async (message = 'Select context:') => {
118
124
  choices: contexts.map((item) => ({ title: item.name, value: item.name })),
119
125
  initial: 0
120
126
  });
121
-
122
127
  return context ? String(context) : null;
123
128
  };
124
-
125
129
  export const resolveContextAlias = (contexts, contextName) => {
126
130
  const found = contexts.find((item) => item.name === contextName);
127
131
  if (!found) return `$${contextName}`;
128
132
  return normalizeAlias(found.alias, `$${contextName}`);
129
133
  };
130
-
131
134
  export const resolveRepositoriesPath = (contextName) =>
132
135
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'repositories');
133
-
134
136
  export const resolveDatasourcesPath = (contextName) =>
135
137
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'datasources');
136
-
137
- export const resolveCoreDatasourcesPath = () => path.join(process.cwd(), 'src', 'core', 'datasource');
138
-
138
+ export const resolveSharedDatasourcesPath = async () => {
139
+ const { sharedContext } = await getSharedState();
140
+ return getFoundationConstructPath(sharedContext.name, 'datasource');
141
+ };
139
142
  export const resolveDomainRootPath = (contextName, domainName) =>
140
143
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain', domainName);
141
-
142
144
  export const resolveUseCasesPath = (contextName) =>
143
145
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'application', 'use-cases');
144
-
145
146
  export const getAvailableTsNames = async (targetPath) => {
146
147
  try {
147
148
  const files = await fs.readdir(targetPath);
@@ -150,25 +151,22 @@ export const getAvailableTsNames = async (targetPath) => {
150
151
  return [];
151
152
  }
152
153
  };
153
-
154
154
  export const selectDatasource = async (contextName) => {
155
- const { contexts, coreAlias } = await getConfigState();
155
+ const { contexts, sharedAlias } = await getConfigState();
156
156
  const contextAlias = resolveContextAlias(contexts, contextName);
157
- const coreDatasources = await getAvailableTsNames(resolveCoreDatasourcesPath());
157
+ const sharedDatasources = await getAvailableTsNames(await resolveSharedDatasourcesPath());
158
158
  const contextDatasources = await getAvailableTsNames(resolveDatasourcesPath(contextName));
159
-
160
159
  const choices = [];
161
- for (const datasource of coreDatasources) {
160
+ for (const datasource of sharedDatasources) {
162
161
  choices.push({
163
- title: `${datasource} (core)`,
162
+ title: `${datasource} (shared foundation)`,
164
163
  value: {
165
164
  name: datasource,
166
- source: 'core',
167
- importPath: `${coreAlias}/datasource`
165
+ source: 'shared',
166
+ importPath: getFoundationConstructImportPath(sharedAlias, 'datasource')
168
167
  }
169
168
  });
170
169
  }
171
-
172
170
  for (const datasource of contextDatasources) {
173
171
  choices.push({
174
172
  title: `${datasource} (${contextName})`,
@@ -179,12 +177,10 @@ export const selectDatasource = async (contextName) => {
179
177
  }
180
178
  });
181
179
  }
182
-
183
180
  if (!choices.length) {
184
- console.error(`❌ No datasources found in core or context "${contextName}".`);
181
+ console.error(`❌ No datasources found in shared foundation or context "${contextName}".`);
185
182
  return null;
186
183
  }
187
-
188
184
  const { datasource } = await prompts({
189
185
  type: 'select',
190
186
  name: 'datasource',
@@ -192,106 +188,86 @@ export const selectDatasource = async (contextName) => {
192
188
  choices,
193
189
  initial: 0
194
190
  });
195
-
196
191
  return datasource ?? null;
197
192
  };
198
-
199
193
  const updateDomainIndex = async (domainRootPath) => {
200
194
  const exports = [];
201
-
202
195
  try {
203
196
  await fs.access(path.join(domainRootPath, 'ports'));
204
197
  exports.push(`export * from './ports';`);
205
198
  } catch {
206
199
  // ignore
207
200
  }
208
-
209
201
  try {
210
202
  await fs.access(path.join(domainRootPath, 'model'));
211
203
  exports.push(`export * from './model';`);
212
204
  } catch {
213
205
  // ignore
214
206
  }
215
-
216
207
  const content = exports.length ? `${exports.join('\n')}\n` : '';
217
208
  await fs.mkdir(domainRootPath, { recursive: true });
218
209
  await fs.writeFile(path.join(domainRootPath, 'index.ts'), content, 'utf-8');
219
210
  };
220
-
221
211
  const normalizeTypeName = (rawTypeName) => {
222
212
  const normalized = toPascalCase(String(rawTypeName ?? '').trim());
223
213
  return normalized || 'IType';
224
214
  };
225
-
226
215
  export const ensureDomainType = async ({ contextName, domainName, typeName, layer }) => {
227
216
  const interfaceName = normalizeTypeName(typeName);
228
217
  const domainRootPath = resolveDomainRootPath(contextName, domainName);
229
218
  const preferredLayer = layer === 'model' ? 'model' : 'ports';
230
219
  const portsTypePath = path.join(domainRootPath, 'ports', `${interfaceName}.ts`);
231
220
  const modelTypePath = path.join(domainRootPath, 'model', `${interfaceName}.ts`);
232
-
233
221
  try {
234
222
  await fs.access(portsTypePath);
235
223
  return { interfaceName, layer: 'ports' };
236
224
  } catch {
237
225
  // ignore
238
226
  }
239
-
240
227
  try {
241
228
  await fs.access(modelTypePath);
242
229
  return { interfaceName, layer: 'model' };
243
230
  } catch {
244
231
  // ignore
245
232
  }
246
-
247
233
  const layerPath = path.join(domainRootPath, preferredLayer);
248
234
  const filePath = path.join(layerPath, `${interfaceName}.ts`);
249
-
250
235
  await fs.mkdir(layerPath, { recursive: true });
251
236
  await writeIfNotExists(filePath, `export interface ${interfaceName} {\n\t[key: string]: unknown;\n}\n`);
252
237
  await updateIndexTs(layerPath);
253
238
  await updateDomainIndex(domainRootPath);
254
-
255
239
  return { interfaceName, layer: preferredLayer };
256
240
  };
257
-
258
241
  export const ensureRepositoryInterfaceFile = async ({ contextName, meta, methods }) => {
259
242
  const domainRootPath = resolveDomainRootPath(contextName, meta.domainName);
260
243
  const portsPath = path.join(domainRootPath, 'ports');
261
244
  await fs.mkdir(portsPath, { recursive: true });
262
-
263
245
  const resolveTypeLayer = async (typeName) => {
264
246
  const portsTypePath = path.join(domainRootPath, 'ports', `${typeName}.ts`);
265
247
  const modelTypePath = path.join(domainRootPath, 'model', `${typeName}.ts`);
266
-
267
248
  try {
268
249
  await fs.access(portsTypePath);
269
250
  return 'ports';
270
251
  } catch {
271
252
  // ignore
272
253
  }
273
-
274
254
  try {
275
255
  await fs.access(modelTypePath);
276
256
  return 'model';
277
257
  } catch {
278
258
  // ignore
279
259
  }
280
-
281
260
  return null;
282
261
  };
283
-
284
262
  const importsByLayer = { ports: new Set(), model: new Set() };
285
263
  for (const method of methods) {
286
- const relatedTypes = [method.responseType, method.requestType].filter(Boolean);
264
+ const relatedTypes = [method.responseType, method.requestType].filter((item) => Boolean(item));
287
265
  for (const typeName of relatedTypes) {
288
- // eslint-disable-next-line no-await-in-loop
289
266
  const typeLayer = await resolveTypeLayer(typeName);
290
267
  if (typeLayer === 'ports') importsByLayer.ports.add(typeName);
291
268
  if (typeLayer === 'model') importsByLayer.model.add(typeName);
292
269
  }
293
270
  }
294
-
295
271
  const importLines = [];
296
272
  for (const typeName of Array.from(importsByLayer.ports)) {
297
273
  importLines.push(`import type { ${typeName} } from './${typeName}.js';`);
@@ -299,56 +275,45 @@ export const ensureRepositoryInterfaceFile = async ({ contextName, meta, methods
299
275
  for (const typeName of Array.from(importsByLayer.model)) {
300
276
  importLines.push(`import type { ${typeName} } from '../model/${typeName}.js';`);
301
277
  }
302
-
303
278
  const signatures = methods
304
279
  .map((method) => {
305
280
  const arg = method.requestType ? `data: ${method.requestType}` : '';
306
281
  return `\t${method.name}(${arg}): Promise<${method.responseType}>;`;
307
282
  })
308
283
  .join('\n');
309
-
310
284
  const content = `${importLines.join('\n')}${importLines.length ? '\n\n' : ''}export interface ${meta.repositoryInterfaceName} {\n${signatures ? `${signatures}\n` : ''}}\n`;
311
285
  await fs.writeFile(path.join(portsPath, `${meta.repositoryInterfaceName}.ts`), content, 'utf-8');
312
286
  await updateIndexTs(portsPath);
313
287
  await updateDomainIndex(domainRootPath);
314
288
  };
315
-
316
289
  const buildHttpCall = (method) => {
317
290
  const httpMethod = method.httpMethod.toLowerCase();
318
291
  const route = method.route || '/public-route';
319
292
  if (!method.requestType) {
320
293
  return `http.${httpMethod}('${route}')`;
321
294
  }
322
-
323
295
  if (httpMethod === 'get' || httpMethod === 'head' || httpMethod === 'options') {
324
296
  return `http.${httpMethod}('${route}', { searchParams: query.toSearchParams(data) })`;
325
297
  }
326
-
327
298
  return `http.${httpMethod}('${route}', { json: data })`;
328
299
  };
329
-
330
300
  export const buildRepositoryMethodCode = (method) => {
331
301
  const arg = method.requestType ? `data: ${method.requestType}` : '';
332
302
  const call = buildHttpCall(method);
333
-
334
303
  let getInCallback = `http`;
335
304
  const httpMethod = String(method.httpMethod ?? '').toLowerCase();
336
305
  if (httpMethod === 'get' || httpMethod === 'head' || httpMethod === 'options') {
337
306
  getInCallback = `http, query`;
338
307
  }
339
-
340
308
  return `\tpublic async ${method.name}(${arg}) {\n\t\treturn this.datasource\n\t\t\t.createRequest<${method.responseType}>(({ ${getInCallback} }) => ${call})\n\t\t\t.then((res) => res.get());\n\t}`;
341
309
  };
342
-
343
310
  export const buildRepositoryContent = ({ meta, contextAlias, datasourceImportPath, datasourceName, methods }) => {
344
311
  const importedTypes = new Set([meta.repositoryInterfaceName]);
345
312
  for (const method of methods) {
346
313
  importedTypes.add(method.responseType);
347
314
  if (method.requestType) importedTypes.add(method.requestType);
348
315
  }
349
-
350
316
  const methodsCode = methods.map((method) => buildRepositoryMethodCode(method)).join('\n\n');
351
-
352
317
  return `import { ${datasourceName} } from '${datasourceImportPath}';
353
318
  import type { ${Array.from(importedTypes).join(', ')} } from '${contextAlias}/layers/domain/${meta.domainName}';
354
319
 
@@ -359,35 +324,28 @@ ${methodsCode ? `${methodsCode}\n` : ''}
359
324
  }
360
325
  `;
361
326
  };
362
-
363
327
  const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
364
-
365
328
  const upsertImport = (content, source, names) => {
366
- const uniqueNames = Array.from(new Set(names)).filter(Boolean);
329
+ const uniqueNames = Array.from(new Set(names)).filter((item) => Boolean(item));
367
330
  if (!uniqueNames.length) return content;
368
-
369
331
  const importRegex = new RegExp(`import\\s+(type\\s+)?\\{([^}]+)\\}\\s*from\\s*['"]${escapeRegExp(source)}['"];?`);
370
332
  const match = content.match(importRegex);
371
-
372
333
  if (match) {
373
- const existing = match[2]
334
+ const existing = (match[2] ?? '')
374
335
  .split(',')
375
336
  .map((part) => part.trim())
376
337
  .filter(Boolean);
377
338
  const merged = Array.from(new Set([...existing, ...uniqueNames]));
378
339
  return content.replace(importRegex, `import type { ${merged.join(', ')} } from '${source}';`);
379
340
  }
380
-
381
341
  const lines = content.split('\n');
382
342
  let insertIndex = 0;
383
- while (insertIndex < lines.length && lines[insertIndex].startsWith('import ')) {
343
+ while (insertIndex < lines.length && (lines[insertIndex] ?? '').startsWith('import ')) {
384
344
  insertIndex += 1;
385
345
  }
386
-
387
346
  lines.splice(insertIndex, 0, `import type { ${uniqueNames.join(', ')} } from '${source}';`);
388
347
  return lines.join('\n');
389
348
  };
390
-
391
349
  export const appendRepositoryMethod = async ({ repositoryPath, method, contextAlias, domainName }) => {
392
350
  let content = await fs.readFile(repositoryPath, 'utf-8');
393
351
  const methodExistsRegex = new RegExp(`\\bpublic\\s+async\\s+${escapeRegExp(method.name)}\\s*\\(`);
@@ -395,53 +353,46 @@ export const appendRepositoryMethod = async ({ repositoryPath, method, contextAl
395
353
  console.warn(`⚠️ Method "${method.name}" already exists in repository, skipped.`);
396
354
  return false;
397
355
  }
398
-
399
356
  content = upsertImport(content, `${contextAlias}/layers/domain/${domainName}`, [method.responseType, method.requestType]);
400
-
401
357
  const methodCode = buildRepositoryMethodCode(method);
402
358
  const classEnd = content.lastIndexOf('\n}');
403
359
  if (classEnd === -1) {
404
360
  throw new Error(`Repository class end was not found in ${repositoryPath}`);
405
361
  }
406
-
407
362
  content = `${content.slice(0, classEnd)}\n\n${methodCode}\n${content.slice(classEnd)}`;
408
363
  await fs.writeFile(repositoryPath, content, 'utf-8');
409
364
  return true;
410
365
  };
411
-
412
366
  export const parseRepositoryMethods = (content) => {
413
367
  const methods = [];
414
368
  const regex =
415
369
  /public\s+async\s+(\w+)\(([^)]*)\)\s*\{[\s\S]*?\.createRequest<([^>]+)>\(\(\{ http \}\) =>\s*http\.(get|post|put|delete|head|options|patch)\(/g;
416
-
417
370
  let match;
418
371
  while ((match = regex.exec(content)) !== null) {
419
372
  const name = match[1];
420
373
  const params = match[2] ?? '';
421
374
  const responseType = (match[3] ?? '').trim();
422
- const httpMethod = (match[4] ?? 'get').toLowerCase();
375
+ const httpMethod = toHttpMethod(match[4]);
423
376
  const requestTypeMatch = params.match(/:\s*([A-Za-z0-9_]+)/);
424
377
  const requestType = requestTypeMatch?.[1] ?? null;
425
-
426
378
  methods.push({
427
- name,
379
+ name: name ?? 'method',
428
380
  httpMethod,
429
381
  responseType,
382
+ responseLayer: 'ports',
430
383
  requestType,
384
+ requestLayer: requestType ? 'ports' : null,
431
385
  route: '/public-route'
432
386
  });
433
387
  }
434
-
435
388
  return methods;
436
389
  };
437
-
438
390
  export const ensureUseCasesFile = async ({ contextName, contextAlias, meta }) => {
439
391
  const repositoryInterfacePath = path.join(
440
392
  resolveDomainRootPath(contextName, meta.domainName),
441
393
  'ports',
442
394
  `${meta.repositoryInterfaceName}.ts`
443
395
  );
444
-
445
396
  let interfaceContent = '';
446
397
  try {
447
398
  interfaceContent = await fs.readFile(repositoryInterfacePath, 'utf-8');
@@ -449,18 +400,16 @@ export const ensureUseCasesFile = async ({ contextName, contextAlias, meta }) =>
449
400
  console.error(`❌ Repository interface not found: ${repositoryInterfacePath}`);
450
401
  return false;
451
402
  }
452
-
453
403
  const methods = [];
454
404
  const methodRegex = /(\w+)\(([^)]*)\)\s*:\s*Promise<[^>]+>;/g;
455
405
  let methodMatch;
456
406
  while ((methodMatch = methodRegex.exec(interfaceContent)) !== null) {
457
- methods.push(methodMatch[1]);
407
+ const methodName = methodMatch[1];
408
+ if (methodName) methods.push(methodName);
458
409
  }
459
-
460
410
  const declares = methods.map((methodName) => `\tdeclare ${methodName}: ${meta.repositoryInterfaceName}['${methodName}'];`).join('\n');
461
411
  const useCasesPath = resolveUseCasesPath(contextName);
462
412
  await fs.mkdir(useCasesPath, { recursive: true });
463
-
464
413
  const content = `import { ClassMirror } from '@azure-net/kit';
465
414
  import type { ${meta.repositoryInterfaceName} } from '${contextAlias}/layers/domain/${meta.domainName}';
466
415
 
@@ -472,12 +421,10 @@ export class ${meta.useCasesClassName} extends ClassMirror {
472
421
  ${declares ? `${declares}\n` : ''}
473
422
  }
474
423
  `;
475
-
476
424
  await fs.writeFile(path.join(useCasesPath, `${meta.useCasesClassName}.ts`), content, 'utf-8');
477
425
  await updateIndexTs(useCasesPath);
478
426
  return true;
479
427
  };
480
-
481
428
  export const promptMethodDefinition = async () => {
482
429
  const { methodNameRaw } = await prompts({
483
430
  type: 'text',
@@ -485,7 +432,6 @@ export const promptMethodDefinition = async () => {
485
432
  message: 'Method name:',
486
433
  validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Method name is required')
487
434
  });
488
-
489
435
  const { httpMethod } = await prompts({
490
436
  type: 'select',
491
437
  name: 'httpMethod',
@@ -493,14 +439,12 @@ export const promptMethodDefinition = async () => {
493
439
  choices: HTTP_METHODS.map((value) => ({ title: value, value })),
494
440
  initial: 0
495
441
  });
496
-
497
442
  const { responseTypeRaw } = await prompts({
498
443
  type: 'text',
499
444
  name: 'responseTypeRaw',
500
445
  message: 'Response type name:',
501
446
  validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Response type is required')
502
447
  });
503
-
504
448
  const { responseLayer } = await prompts({
505
449
  type: 'select',
506
450
  name: 'responseLayer',
@@ -511,7 +455,6 @@ export const promptMethodDefinition = async () => {
511
455
  ],
512
456
  initial: 0
513
457
  });
514
-
515
458
  const { routeRaw } = await prompts({
516
459
  type: 'text',
517
460
  name: 'routeRaw',
@@ -523,17 +466,14 @@ export const promptMethodDefinition = async () => {
523
466
  return raw.startsWith('/') ? true : 'Path must start with "/"';
524
467
  }
525
468
  });
526
-
527
469
  const { hasRequest } = await prompts({
528
470
  type: 'confirm',
529
471
  name: 'hasRequest',
530
472
  message: 'Has request payload type?',
531
473
  initial: false
532
474
  });
533
-
534
475
  let requestType = null;
535
476
  let requestLayer = null;
536
-
537
477
  if (hasRequest) {
538
478
  const { requestTypeRaw } = await prompts({
539
479
  type: 'text',
@@ -541,7 +481,6 @@ export const promptMethodDefinition = async () => {
541
481
  message: 'Request type name:',
542
482
  validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Request type is required')
543
483
  });
544
-
545
484
  const layerResult = await prompts({
546
485
  type: 'select',
547
486
  name: 'requestLayer',
@@ -552,14 +491,12 @@ export const promptMethodDefinition = async () => {
552
491
  ],
553
492
  initial: 0
554
493
  });
555
-
556
494
  requestType = normalizeTypeName(requestTypeRaw);
557
495
  requestLayer = layerResult.requestLayer ?? 'ports';
558
496
  }
559
-
560
497
  return {
561
498
  name: toCamelCase(String(methodNameRaw ?? '').trim() || 'method'),
562
- httpMethod: String(httpMethod ?? 'get').toLowerCase(),
499
+ httpMethod: toHttpMethod(httpMethod),
563
500
  responseType: normalizeTypeName(responseTypeRaw),
564
501
  responseLayer: responseLayer ?? 'ports',
565
502
  route: String(routeRaw ?? '/public-route').trim() || '/public-route',
@@ -5,6 +5,7 @@ import { loadUserConfig } from '../../utils/loadConfig.js';
5
5
  import { normalizeContexts } from '../../init/initAliases.js';
6
6
  import { toCamelCase, toKebabCase, toPascalCase } from '../../utils/contextUtils.js';
7
7
  import { updateIndexTs, writeIfNotExists } from '../../utils/fileUtils.js';
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../../utils/sharedFoundation.js';
8
9
 
9
10
  type ContextConfig = { name: string; alias: string };
10
11
  type DomainLayer = 'ports' | 'model';
@@ -17,7 +18,7 @@ type RepositoryMeta = {
17
18
  useCasesClassName: string;
18
19
  repositoryDependencyName: string;
19
20
  };
20
- type DatasourceInfo = { name: string; source: 'core' | 'context'; importPath: string };
21
+ type DatasourceInfo = { name: string; source: 'shared' | 'context'; importPath: string };
21
22
  type RepositoryMethod = {
22
23
  name: string;
23
24
  httpMethod: HttpMethod;
@@ -131,14 +132,15 @@ export const getRepositoryMeta = (repositoryClassName: unknown): RepositoryMeta
131
132
  };
132
133
  };
133
134
 
134
- export const getConfigState = async (): Promise<{ contexts: ContextConfig[]; coreAlias: string }> => {
135
+ export const getConfigState = async (): Promise<{ contexts: ContextConfig[]; sharedAlias: string; sharedContextName: string }> => {
135
136
  const config = await loadUserConfig();
136
137
  const contexts = normalizeContexts(config.contexts);
137
- const coreAlias = normalizeAlias(config.coreAlias, '$core');
138
+ const { sharedAlias, sharedContext } = await getSharedState();
138
139
 
139
140
  return {
140
141
  contexts,
141
- coreAlias
142
+ sharedAlias,
143
+ sharedContextName: sharedContext.name
142
144
  };
143
145
  };
144
146
 
@@ -172,7 +174,10 @@ export const resolveRepositoriesPath = (contextName: string): string =>
172
174
  export const resolveDatasourcesPath = (contextName: string): string =>
173
175
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'datasources');
174
176
 
175
- export const resolveCoreDatasourcesPath = (): string => path.join(process.cwd(), 'src', 'core', 'datasource');
177
+ export const resolveSharedDatasourcesPath = async (): Promise<string> => {
178
+ const { sharedContext } = await getSharedState();
179
+ return getFoundationConstructPath(sharedContext.name, 'datasource');
180
+ };
176
181
 
177
182
  export const resolveDomainRootPath = (contextName: string, domainName: string): string =>
178
183
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain', domainName);
@@ -190,19 +195,19 @@ export const getAvailableTsNames = async (targetPath: string): Promise<string[]>
190
195
  };
191
196
 
192
197
  export const selectDatasource = async (contextName: string): Promise<DatasourceInfo | null> => {
193
- const { contexts, coreAlias } = await getConfigState();
198
+ const { contexts, sharedAlias } = await getConfigState();
194
199
  const contextAlias = resolveContextAlias(contexts, contextName);
195
- const coreDatasources = await getAvailableTsNames(resolveCoreDatasourcesPath());
200
+ const sharedDatasources = await getAvailableTsNames(await resolveSharedDatasourcesPath());
196
201
  const contextDatasources = await getAvailableTsNames(resolveDatasourcesPath(contextName));
197
202
 
198
203
  const choices: Array<{ title: string; value: DatasourceInfo }> = [];
199
- for (const datasource of coreDatasources) {
204
+ for (const datasource of sharedDatasources) {
200
205
  choices.push({
201
- title: `${datasource} (core)`,
206
+ title: `${datasource} (shared foundation)`,
202
207
  value: {
203
208
  name: datasource,
204
- source: 'core',
205
- importPath: `${coreAlias}/datasource`
209
+ source: 'shared',
210
+ importPath: getFoundationConstructImportPath(sharedAlias, 'datasource')
206
211
  }
207
212
  });
208
213
  }
@@ -219,7 +224,7 @@ export const selectDatasource = async (contextName: string): Promise<DatasourceI
219
224
  }
220
225
 
221
226
  if (!choices.length) {
222
- console.error(`❌ No datasources found in core or context "${contextName}".`);
227
+ console.error(`❌ No datasources found in shared foundation or context "${contextName}".`);
223
228
  return null;
224
229
  }
225
230