@azure-net/cli 5.0.0-3 → 5.0.1

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 +366 -371
  27. package/src/generate/module/generatePresenter.ts +76 -20
  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 +67 -94
  52. package/src/scaffolds/createPresenterFactory.ts +7 -19
  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,7 +5,7 @@ import { loadUserConfig } from '../../utils/loadConfig.js';
5
5
  import { normalizeContexts } from '../../init/initAliases.js';
6
6
  import { toCamelCase, toPascalCase } from '../../utils/contextUtils.js';
7
7
  import { updateIndexTs } from '../../utils/fileUtils.js';
8
-
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../../utils/sharedFoundation.js';
9
9
  const HTTP_METHODS = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'];
10
10
  const MUTATION_METHODS = new Set(['put', 'patch', 'delete']);
11
11
  const RESERVED_IDENTIFIERS = new Set([
@@ -56,20 +56,16 @@ const RESERVED_IDENTIFIERS = new Set([
56
56
  'static',
57
57
  'yield'
58
58
  ]);
59
-
60
59
  const normalizeAlias = (value, fallback) => {
61
60
  const raw = String(value ?? '').trim();
62
61
  if (!raw.length) return fallback;
63
62
  return raw.startsWith('$') ? raw : `$${raw}`;
64
63
  };
65
-
66
64
  const toTypeFileName = (typeName) => String(typeName ?? '').replace(/^I/, '') || 'Type';
67
-
68
65
  export const ensureRepositoryName = (rawName) => {
69
66
  const normalized = toPascalCase(String(rawName ?? '').trim() || 'Repository');
70
67
  return normalized.endsWith('Repository') ? normalized : `${normalized}Repository`;
71
68
  };
72
-
73
69
  export const getRepositoryMeta = (repositoryName) => {
74
70
  const className = ensureRepositoryName(repositoryName);
75
71
  const baseName = className.replace(/Repository$/, '') || 'Repository';
@@ -79,7 +75,6 @@ export const getRepositoryMeta = (repositoryName) => {
79
75
  const repositoryDependencyName = RESERVED_IDENTIFIERS.has(repositoryDependencyNameRaw)
80
76
  ? `${repositoryDependencyNameRaw}Repository`
81
77
  : repositoryDependencyNameRaw;
82
-
83
78
  return {
84
79
  className,
85
80
  baseName,
@@ -88,21 +83,18 @@ export const getRepositoryMeta = (repositoryName) => {
88
83
  repositoryDependencyName
89
84
  };
90
85
  };
91
-
92
86
  export const getConfigState = async () => {
93
87
  const config = await loadUserConfig();
94
88
  const contexts = normalizeContexts(config.contexts);
95
- const coreAlias = normalizeAlias(config.coreAlias, '$core');
96
- return { contexts, coreAlias };
89
+ const { sharedAlias, sharedContext } = await getSharedState();
90
+ return { contexts, sharedAlias, sharedContextName: sharedContext.name };
97
91
  };
98
-
99
92
  export const selectContext = async (message = 'Select context:') => {
100
93
  const { contexts } = await getConfigState();
101
94
  if (!contexts.length) {
102
95
  console.error('❌ Cannot continue: `contexts` is missing or empty in azure-net.config.ts/js');
103
96
  return null;
104
97
  }
105
-
106
98
  const { context } = await prompts({
107
99
  type: 'select',
108
100
  name: 'context',
@@ -110,30 +102,25 @@ export const selectContext = async (message = 'Select context:') => {
110
102
  choices: contexts.map((item) => ({ title: item.name, value: item.name })),
111
103
  initial: 0
112
104
  });
113
-
114
105
  return context ? String(context) : null;
115
106
  };
116
-
117
107
  export const resolveContextAlias = (contexts, contextName) => {
118
108
  const found = contexts.find((item) => item.name === contextName);
119
109
  if (!found) return `$${contextName}`;
120
110
  return normalizeAlias(found.alias, `$${contextName}`);
121
111
  };
122
-
123
112
  export const resolveRepositoriesPath = (contextName) =>
124
113
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'repositories');
125
-
126
114
  export const resolveDatasourcesPath = (contextName) =>
127
115
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'datasources');
128
-
129
- export const resolveCoreDatasourcesPath = () => path.join(process.cwd(), 'src', 'core', 'datasource');
130
-
116
+ export const resolveSharedDatasourcesPath = async () => {
117
+ const { sharedContext } = await getSharedState();
118
+ return getFoundationConstructPath(sharedContext.name, 'datasource');
119
+ };
131
120
  export const resolveDomainRootPath = (contextName, domainName) =>
132
121
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain', domainName);
133
-
134
122
  export const resolveUseCasesPath = (contextName) =>
135
123
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'application', 'use-cases');
136
-
137
124
  export const getAvailableTsNames = async (targetPath) => {
138
125
  try {
139
126
  const files = await fs.readdir(targetPath);
@@ -142,33 +129,28 @@ export const getAvailableTsNames = async (targetPath) => {
142
129
  return [];
143
130
  }
144
131
  };
145
-
146
132
  export const selectDatasource = async (contextName) => {
147
- const { contexts, coreAlias } = await getConfigState();
133
+ const { contexts, sharedAlias } = await getConfigState();
148
134
  const contextAlias = resolveContextAlias(contexts, contextName);
149
- const coreDatasources = await getAvailableTsNames(resolveCoreDatasourcesPath());
135
+ const sharedDatasources = await getAvailableTsNames(await resolveSharedDatasourcesPath());
150
136
  const contextDatasources = await getAvailableTsNames(resolveDatasourcesPath(contextName));
151
-
152
137
  const choices = [];
153
- for (const datasource of coreDatasources) {
138
+ for (const datasource of sharedDatasources) {
154
139
  choices.push({
155
- title: `${datasource} (core)`,
156
- value: { name: datasource, importPath: `${coreAlias}/datasource` }
140
+ title: `${datasource} (shared foundation)`,
141
+ value: { name: datasource, importPath: getFoundationConstructImportPath(sharedAlias, 'datasource') }
157
142
  });
158
143
  }
159
-
160
144
  for (const datasource of contextDatasources) {
161
145
  choices.push({
162
146
  title: `${datasource} (${contextName})`,
163
147
  value: { name: datasource, importPath: `${contextAlias}/layers/infrastructure/http/datasources` }
164
148
  });
165
149
  }
166
-
167
150
  if (!choices.length) {
168
- console.error(`❌ No datasources found in core or context "${contextName}".`);
151
+ console.error(`❌ No datasources found in shared foundation or context "${contextName}".`);
169
152
  return null;
170
153
  }
171
-
172
154
  const { datasource } = await prompts({
173
155
  type: 'select',
174
156
  name: 'datasource',
@@ -176,10 +158,8 @@ export const selectDatasource = async (contextName) => {
176
158
  choices,
177
159
  initial: 0
178
160
  });
179
-
180
161
  return datasource ?? null;
181
162
  };
182
-
183
163
  export const selectDomain = async (contextName) => {
184
164
  const domainPath = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain');
185
165
  try {
@@ -189,7 +169,6 @@ export const selectDomain = async (contextName) => {
189
169
  console.error(`❌ No domain folders found in ${domainPath}`);
190
170
  return null;
191
171
  }
192
-
193
172
  const { domain } = await prompts({
194
173
  type: 'select',
195
174
  name: 'domain',
@@ -197,19 +176,16 @@ export const selectDomain = async (contextName) => {
197
176
  choices: domains.map((item) => ({ title: item, value: item })),
198
177
  initial: 0
199
178
  });
200
-
201
179
  return domain ? String(domain) : null;
202
180
  } catch {
203
181
  console.error(`❌ Domain path not found: ${domainPath}`);
204
182
  return null;
205
183
  }
206
184
  };
207
-
208
185
  const extractInterfaceName = (content, fallback) => {
209
186
  const match = content.match(/export\s+interface\s+([A-Za-z0-9_]+)/);
210
187
  return match?.[1] ?? fallback;
211
188
  };
212
-
213
189
  export const getDomainTypes = async (contextName, domainName) => {
214
190
  const root = resolveDomainRootPath(contextName, domainName);
215
191
  const result = [];
@@ -220,7 +196,6 @@ export const getDomainTypes = async (contextName, domainName) => {
220
196
  for (const fileName of files) {
221
197
  if (!fileName.endsWith('.ts') || fileName === 'index.ts') continue;
222
198
  const absolute = path.join(layerPath, fileName);
223
- // eslint-disable-next-line no-await-in-loop
224
199
  const content = await fs.readFile(absolute, 'utf-8');
225
200
  const fallback = fileName.replace(/\.ts$/, '');
226
201
  const interfaceName = extractInterfaceName(content, fallback);
@@ -230,15 +205,12 @@ export const getDomainTypes = async (contextName, domainName) => {
230
205
  // ignore
231
206
  }
232
207
  };
233
-
234
208
  await readLayer('ports');
235
209
  await readLayer('model');
236
210
  return result;
237
211
  };
238
-
239
212
  const isPrimitiveType = (value) =>
240
213
  ['string', 'number', 'boolean', 'unknown', 'void', 'null', 'undefined', 'bigint', 'symbol', 'never'].includes(String(value ?? '').trim());
241
-
242
214
  const buildResponseSelection = async (domainTypes) => {
243
215
  const { isPrimitive } = await prompts({
244
216
  type: 'confirm',
@@ -246,7 +218,6 @@ const buildResponseSelection = async (domainTypes) => {
246
218
  message: 'Is response type primitive?',
247
219
  initial: false
248
220
  });
249
-
250
221
  if (isPrimitive) {
251
222
  const { primitiveType } = await prompts({
252
223
  type: 'text',
@@ -254,15 +225,12 @@ const buildResponseSelection = async (domainTypes) => {
254
225
  message: 'Primitive response type:',
255
226
  validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Type is required')
256
227
  });
257
-
258
228
  return { kind: 'primitive', type: String(primitiveType).trim(), layer: null };
259
229
  }
260
-
261
230
  if (!domainTypes.length) {
262
231
  console.error('❌ Domain has no model/ports types to choose from.');
263
232
  return null;
264
233
  }
265
-
266
234
  const { selectedType } = await prompts({
267
235
  type: 'select',
268
236
  name: 'selectedType',
@@ -273,26 +241,21 @@ const buildResponseSelection = async (domainTypes) => {
273
241
  })),
274
242
  initial: 0
275
243
  });
276
-
277
244
  if (!selectedType) return null;
278
245
  return { kind: 'domain', type: selectedType.name, layer: selectedType.layer };
279
246
  };
280
-
281
247
  const buildDomainRequestTypeSelection = async (question, domainTypes) => {
282
248
  if (!domainTypes.length) {
283
249
  console.error('❌ Domain has no model/ports types to choose from.');
284
250
  return null;
285
251
  }
286
-
287
252
  const { enabled } = await prompts({
288
253
  type: 'confirm',
289
254
  name: 'enabled',
290
255
  message: question,
291
256
  initial: false
292
257
  });
293
-
294
258
  if (!enabled) return null;
295
-
296
259
  const { selectedType } = await prompts({
297
260
  type: 'select',
298
261
  name: 'selectedType',
@@ -303,10 +266,8 @@ const buildDomainRequestTypeSelection = async (question, domainTypes) => {
303
266
  })),
304
267
  initial: 0
305
268
  });
306
-
307
269
  return selectedType ?? null;
308
270
  };
309
-
310
271
  const buildPathParamTypeSelection = async () => {
311
272
  const { enabled } = await prompts({
312
273
  type: 'confirm',
@@ -314,9 +275,7 @@ const buildPathParamTypeSelection = async () => {
314
275
  message: 'Has path param?',
315
276
  initial: false
316
277
  });
317
-
318
278
  if (!enabled) return null;
319
-
320
279
  const { type } = await prompts({
321
280
  type: 'select',
322
281
  name: 'type',
@@ -327,10 +286,8 @@ const buildPathParamTypeSelection = async () => {
327
286
  ],
328
287
  initial: 0
329
288
  });
330
-
331
289
  return type ?? 'string';
332
290
  };
333
-
334
291
  export const promptMethodDefinition = async (domainTypes) => {
335
292
  const { methodNameRaw } = await prompts({
336
293
  type: 'text',
@@ -338,7 +295,6 @@ export const promptMethodDefinition = async (domainTypes) => {
338
295
  message: 'Method name:',
339
296
  validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Method name is required')
340
297
  });
341
-
342
298
  const { httpMethod } = await prompts({
343
299
  type: 'select',
344
300
  name: 'httpMethod',
@@ -346,7 +302,6 @@ export const promptMethodDefinition = async (domainTypes) => {
346
302
  choices: HTTP_METHODS.map((item) => ({ title: item, value: item })),
347
303
  initial: 0
348
304
  });
349
-
350
305
  const { routeRaw } = await prompts({
351
306
  type: 'text',
352
307
  name: 'routeRaw',
@@ -358,15 +313,12 @@ export const promptMethodDefinition = async (domainTypes) => {
358
313
  return raw.startsWith('/') ? true : 'Path must start with "/"';
359
314
  }
360
315
  });
361
-
362
316
  const response = await buildResponseSelection(domainTypes);
363
317
  if (!response) return null;
364
-
365
318
  const normalizedMethod = String(httpMethod ?? 'get').toLowerCase();
366
319
  let queryType = null;
367
320
  let bodyType = null;
368
321
  let pathParamType = null;
369
-
370
322
  if (normalizedMethod === 'get') {
371
323
  queryType = await buildDomainRequestTypeSelection('Has query params?', domainTypes);
372
324
  } else if (normalizedMethod === 'post') {
@@ -375,7 +327,6 @@ export const promptMethodDefinition = async (domainTypes) => {
375
327
  pathParamType = await buildPathParamTypeSelection();
376
328
  bodyType = await buildDomainRequestTypeSelection('Has request body?', domainTypes);
377
329
  }
378
-
379
330
  return {
380
331
  name: toCamelCase(String(methodNameRaw ?? '').trim() || 'method'),
381
332
  httpMethod: normalizedMethod,
@@ -386,9 +337,7 @@ export const promptMethodDefinition = async (domainTypes) => {
386
337
  pathParamType
387
338
  };
388
339
  };
389
-
390
340
  const getResponseTypeName = (method) => method.response.type;
391
-
392
341
  const getMethodParams = (method) => {
393
342
  const params = [];
394
343
  if (method.pathParamType) params.push(`pathParam: ${method.pathParamType}`);
@@ -396,10 +345,8 @@ const getMethodParams = (method) => {
396
345
  if (method.bodyType) params.push(`request: ${method.bodyType.name}`);
397
346
  return params.join(', ');
398
347
  };
399
-
400
348
  const getRequestMapping = (method) => {
401
349
  const routeWithPath = method.pathParamType ? `${method.route}/\${pathParam}` : method.route;
402
-
403
350
  if (method.httpMethod === 'get') {
404
351
  if (method.queryType) {
405
352
  return {
@@ -407,13 +354,11 @@ const getRequestMapping = (method) => {
407
354
  call: `http.get(\`${routeWithPath}\`, { searchParams: query.toSearchParams(request) })`
408
355
  };
409
356
  }
410
-
411
357
  return {
412
358
  callbackParams: '{ http }',
413
359
  call: `http.get(\`${routeWithPath}\`)`
414
360
  };
415
361
  }
416
-
417
362
  if (method.httpMethod === 'post') {
418
363
  if (method.bodyType) {
419
364
  return {
@@ -421,13 +366,11 @@ const getRequestMapping = (method) => {
421
366
  call: `http.post(\`${routeWithPath}\`, { json: request })`
422
367
  };
423
368
  }
424
-
425
369
  return {
426
370
  callbackParams: '{ http }',
427
371
  call: `http.post(\`${routeWithPath}\`)`
428
372
  };
429
373
  }
430
-
431
374
  if (method.httpMethod === 'put' || method.httpMethod === 'patch' || method.httpMethod === 'delete') {
432
375
  const callMethod = method.httpMethod;
433
376
  if (method.bodyType) {
@@ -436,27 +379,22 @@ const getRequestMapping = (method) => {
436
379
  call: `http.${callMethod}(\`${routeWithPath}\`, { json: request })`
437
380
  };
438
381
  }
439
-
440
382
  return {
441
383
  callbackParams: '{ http }',
442
384
  call: `http.${callMethod}(\`${routeWithPath}\`)`
443
385
  };
444
386
  }
445
-
446
387
  return {
447
388
  callbackParams: '{ http }',
448
389
  call: `http.${method.httpMethod}(\`${routeWithPath}\`)`
449
390
  };
450
391
  };
451
-
452
392
  export const buildRepositoryMethodCode = (method) => {
453
393
  const params = getMethodParams(method);
454
394
  const responseType = getResponseTypeName(method);
455
395
  const mapping = getRequestMapping(method);
456
-
457
396
  return `\tpublic async ${method.name}(${params}) {\n\t\treturn this.datasource\n\t\t\t.createRequest<${responseType}>((${mapping.callbackParams}) => ${mapping.call})\n\t\t\t.then((res) => res.getData());\n\t}`;
458
397
  };
459
-
460
398
  export const buildRepositoryContent = ({ meta, contextAlias, domainName, datasource, methods }) => {
461
399
  const typeImports = new Set([meta.repositoryInterfaceName]);
462
400
  for (const method of methods) {
@@ -464,12 +402,10 @@ export const buildRepositoryContent = ({ meta, contextAlias, domainName, datasou
464
402
  if (method.queryType) typeImports.add(method.queryType.name);
465
403
  if (method.bodyType) typeImports.add(method.bodyType.name);
466
404
  }
467
-
468
405
  const methodsCode = methods.map((method) => buildRepositoryMethodCode(method)).join('\n\n');
469
406
  const typeImportLine = typeImports.size
470
407
  ? `import type { ${Array.from(typeImports).join(', ')} } from '${contextAlias}/layers/domain/${domainName}';\n`
471
408
  : '';
472
-
473
409
  return `import { ${datasource.name} } from '${datasource.importPath}';
474
410
  ${typeImportLine}
475
411
  export class ${meta.className} implements ${meta.repositoryInterfaceName} {
@@ -479,15 +415,12 @@ ${methodsCode ? `${methodsCode}\n` : ''}
479
415
  }
480
416
  `;
481
417
  };
482
-
483
418
  export const writeRepositoryInterface = async ({ contextName, domainName, meta, methods }) => {
484
419
  const domainPath = resolveDomainRootPath(contextName, domainName);
485
420
  const portsPath = path.join(domainPath, 'ports');
486
421
  await fs.mkdir(portsPath, { recursive: true });
487
-
488
422
  const portsImports = new Set();
489
423
  const modelImports = new Set();
490
-
491
424
  for (const method of methods) {
492
425
  const allTypes = [];
493
426
  if (!isPrimitiveType(method.response.type) && method.response.layer) {
@@ -495,13 +428,11 @@ export const writeRepositoryInterface = async ({ contextName, domainName, meta,
495
428
  }
496
429
  if (method.queryType) allTypes.push(method.queryType);
497
430
  if (method.bodyType) allTypes.push(method.bodyType);
498
-
499
431
  for (const typeEntry of allTypes) {
500
432
  if (typeEntry.layer === 'ports') portsImports.add(typeEntry.name);
501
433
  if (typeEntry.layer === 'model') modelImports.add(typeEntry.name);
502
434
  }
503
435
  }
504
-
505
436
  const imports = [];
506
437
  for (const name of Array.from(portsImports)) {
507
438
  imports.push(`import type { ${name} } from './${toTypeFileName(name)}';`);
@@ -509,7 +440,6 @@ export const writeRepositoryInterface = async ({ contextName, domainName, meta,
509
440
  for (const name of Array.from(modelImports)) {
510
441
  imports.push(`import type { ${name} } from '../model/${toTypeFileName(name)}';`);
511
442
  }
512
-
513
443
  const signatures = methods
514
444
  .map((method) => {
515
445
  const responseType = getResponseTypeName(method);
@@ -517,33 +447,33 @@ export const writeRepositoryInterface = async ({ contextName, domainName, meta,
517
447
  return `\t${method.name}(${params}): Promise<${responseType}>;`;
518
448
  })
519
449
  .join('\n');
520
-
521
450
  const content = `${imports.join('\n')}${imports.length ? '\n\n' : ''}export interface ${meta.repositoryInterfaceName} {\n${signatures ? `${signatures}\n` : ''}}\n`;
522
- await fs.writeFile(path.join(portsPath, `${meta.repositoryInterfaceName}.ts`), content, 'utf-8');
451
+ await fs.writeFile(path.join(portsPath, `${toTypeFileName(meta.repositoryInterfaceName)}.ts`), content, 'utf-8');
523
452
  await updateIndexTs(portsPath);
524
453
  };
525
-
526
454
  const parseRepositoryFileForDomain = (content, contextAlias) => {
527
455
  const escapedAlias = contextAlias.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
528
456
  const regex = new RegExp(`from\\s+['"]${escapedAlias}/layers/domain/([^'"]+)['"]`);
529
457
  const match = content.match(regex);
530
458
  return match?.[1] ?? null;
531
459
  };
532
-
533
460
  const parseRepositoryMethodsFromInterface = (content) => {
534
461
  const methods = [];
535
462
  const regex = /(\w+)\(([^)]*)\)\s*:\s*Promise<([^>]+)>;/g;
536
463
  let match;
537
464
  while ((match = regex.exec(content)) !== null) {
538
- methods.push(match[1]);
465
+ const methodName = match[1];
466
+ if (methodName) methods.push(methodName);
539
467
  }
540
468
  return methods;
541
469
  };
542
-
543
470
  export const createUseCasesForRepository = async ({ contextName, contextAlias, domainName, repositoryName }) => {
544
471
  const meta = getRepositoryMeta(repositoryName);
545
- const interfacePath = path.join(resolveDomainRootPath(contextName, domainName), 'ports', `${meta.repositoryInterfaceName}.ts`);
546
-
472
+ const interfacePath = path.join(
473
+ resolveDomainRootPath(contextName, domainName),
474
+ 'ports',
475
+ `${toTypeFileName(meta.repositoryInterfaceName)}.ts`
476
+ );
547
477
  let interfaceContent = '';
548
478
  try {
549
479
  interfaceContent = await fs.readFile(interfacePath, 'utf-8');
@@ -551,13 +481,10 @@ export const createUseCasesForRepository = async ({ contextName, contextAlias, d
551
481
  console.error(`❌ Repository interface not found: ${interfacePath}`);
552
482
  return false;
553
483
  }
554
-
555
484
  const methods = parseRepositoryMethodsFromInterface(interfaceContent);
556
485
  const useCasesPath = resolveUseCasesPath(contextName);
557
486
  await fs.mkdir(useCasesPath, { recursive: true });
558
-
559
487
  const declares = methods.map((methodName) => `\tdeclare ${methodName}: ${meta.repositoryInterfaceName}['${methodName}'];`).join('\n');
560
-
561
488
  const content = `import { ClassMirror } from '@azure-net/kit';
562
489
  import type { ${meta.repositoryInterfaceName} } from '${contextAlias}/layers/domain/${domainName}';
563
490
 
@@ -569,12 +496,10 @@ export class ${meta.useCasesClassName} extends ClassMirror {
569
496
  ${declares ? `${declares}\n` : ''}
570
497
  }
571
498
  `;
572
-
573
499
  await fs.writeFile(path.join(useCasesPath, `${meta.useCasesClassName}.ts`), content, 'utf-8');
574
500
  await updateIndexTs(useCasesPath);
575
501
  return true;
576
502
  };
577
-
578
503
  export const resolveDomainForRepository = async ({ contextName, contextAlias, repositoryName }) => {
579
504
  const repositoryPath = path.join(resolveRepositoriesPath(contextName), `${ensureRepositoryName(repositoryName)}.ts`);
580
505
  try {
@@ -584,12 +509,10 @@ export const resolveDomainForRepository = async ({ contextName, contextAlias, re
584
509
  return null;
585
510
  }
586
511
  };
587
-
588
512
  export const promptMethodsBatch = async (domainTypes, count) => {
589
513
  const methods = [];
590
514
  for (let index = 0; index < count; index += 1) {
591
515
  console.log(`\n🧩 Method ${index + 1} of ${count}`);
592
- // eslint-disable-next-line no-await-in-loop
593
516
  const method = await promptMethodDefinition(domainTypes);
594
517
  if (!method) return null;
595
518
  methods.push(method);
@@ -5,6 +5,7 @@ import { loadUserConfig } from '../../utils/loadConfig.js';
5
5
  import { normalizeContexts } from '../../init/initAliases.js';
6
6
  import { toCamelCase, toPascalCase } from '../../utils/contextUtils.js';
7
7
  import { updateIndexTs } 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 TypeLayer = 'ports' | 'model';
@@ -113,11 +114,11 @@ export const getRepositoryMeta = (repositoryName: unknown): RepositoryMeta => {
113
114
  };
114
115
  };
115
116
 
116
- export const getConfigState = async (): Promise<{ contexts: ContextConfig[]; coreAlias: string }> => {
117
+ export const getConfigState = async (): Promise<{ contexts: ContextConfig[]; sharedAlias: string; sharedContextName: string }> => {
117
118
  const config = await loadUserConfig();
118
119
  const contexts = normalizeContexts(config.contexts);
119
- const coreAlias = normalizeAlias(config.coreAlias, '$core');
120
- return { contexts, coreAlias };
120
+ const { sharedAlias, sharedContext } = await getSharedState();
121
+ return { contexts, sharedAlias, sharedContextName: sharedContext.name };
121
122
  };
122
123
 
123
124
  export const selectContext = async (message = 'Select context:'): Promise<string | null> => {
@@ -150,7 +151,10 @@ export const resolveRepositoriesPath = (contextName: string): string =>
150
151
  export const resolveDatasourcesPath = (contextName: string): string =>
151
152
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'datasources');
152
153
 
153
- export const resolveCoreDatasourcesPath = (): string => path.join(process.cwd(), 'src', 'core', 'datasource');
154
+ export const resolveSharedDatasourcesPath = async (): Promise<string> => {
155
+ const { sharedContext } = await getSharedState();
156
+ return getFoundationConstructPath(sharedContext.name, 'datasource');
157
+ };
154
158
 
155
159
  export const resolveDomainRootPath = (contextName: string, domainName: string): string =>
156
160
  path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'domain', domainName);
@@ -168,16 +172,16 @@ export const getAvailableTsNames = async (targetPath: string): Promise<string[]>
168
172
  };
169
173
 
170
174
  export const selectDatasource = async (contextName: string): Promise<DatasourceInfo | null> => {
171
- const { contexts, coreAlias } = await getConfigState();
175
+ const { contexts, sharedAlias } = await getConfigState();
172
176
  const contextAlias = resolveContextAlias(contexts, contextName);
173
- const coreDatasources = await getAvailableTsNames(resolveCoreDatasourcesPath());
177
+ const sharedDatasources = await getAvailableTsNames(await resolveSharedDatasourcesPath());
174
178
  const contextDatasources = await getAvailableTsNames(resolveDatasourcesPath(contextName));
175
179
 
176
180
  const choices: Array<{ title: string; value: DatasourceInfo }> = [];
177
- for (const datasource of coreDatasources) {
181
+ for (const datasource of sharedDatasources) {
178
182
  choices.push({
179
- title: `${datasource} (core)`,
180
- value: { name: datasource, importPath: `${coreAlias}/datasource` }
183
+ title: `${datasource} (shared foundation)`,
184
+ value: { name: datasource, importPath: getFoundationConstructImportPath(sharedAlias, 'datasource') }
181
185
  });
182
186
  }
183
187
 
@@ -189,7 +193,7 @@ export const selectDatasource = async (contextName: string): Promise<DatasourceI
189
193
  }
190
194
 
191
195
  if (!choices.length) {
192
- console.error(`❌ No datasources found in core or context "${contextName}".`);
196
+ console.error(`❌ No datasources found in shared foundation or context "${contextName}".`);
193
197
  return null;
194
198
  }
195
199
 
@@ -5,7 +5,6 @@ import { toPascalCase } from '../../utils/contextUtils.js';
5
5
  import { normalizeContexts } from '../../init/initAliases.js';
6
6
  import { loadUserConfig } from '../../utils/loadConfig.js';
7
7
  import { writeIfNotExists } from '../../utils/fileUtils.js';
8
-
9
8
  const createComponentContent = (propsName) => `<script lang="ts">
10
9
  import type { ${propsName} } from './types';
11
10
  const {}: ${propsName} = $props();
@@ -15,22 +14,17 @@ const {}: ${propsName} = $props();
15
14
  \t@use './style';
16
15
  </style>
17
16
  `;
18
-
19
17
  const createTypesContent = (propsName) => `export interface ${propsName} {}
20
18
  `;
21
-
22
19
  const createIndexContent = (name) => `export { default as ${name} } from './Component.svelte';
23
20
  `;
24
-
25
21
  const printContextsError = () => {
26
22
  console.error('❌ Cannot generate UI artifact: `contexts` is missing or empty in azure-net.config.ts/js');
27
23
  };
28
-
29
24
  const selectContext = async () => {
30
25
  const config = await loadUserConfig();
31
26
  const contexts = normalizeContexts(config.contexts);
32
27
  if (!contexts.length) return null;
33
-
34
28
  const { context } = await prompts({
35
29
  type: 'select',
36
30
  name: 'context',
@@ -38,10 +32,8 @@ const selectContext = async () => {
38
32
  choices: contexts.map((item) => ({ title: item.name, value: item.name })),
39
33
  initial: 0
40
34
  });
41
-
42
35
  return context ? String(context) : null;
43
36
  };
44
-
45
37
  export default async function generateComponent() {
46
38
  const context = await selectContext();
47
39
  if (!context) {
@@ -49,14 +41,12 @@ export default async function generateComponent() {
49
41
  process.exitCode = 1;
50
42
  return;
51
43
  }
52
-
53
44
  const { rawName } = await prompts({
54
45
  type: 'text',
55
46
  name: 'rawName',
56
47
  message: 'Component name:',
57
48
  validate: (value) => (String(value).trim().length > 0 ? true : 'Name is required')
58
49
  });
59
-
60
50
  const { componentKind } = await prompts({
61
51
  type: 'select',
62
52
  name: 'componentKind',
@@ -68,12 +58,10 @@ export default async function generateComponent() {
68
58
  ],
69
59
  initial: 0
70
60
  });
71
-
72
61
  const name = toPascalCase(String(rawName ?? 'Component')) || 'Component';
73
62
  const propsName = `IComponent${name}Props`;
74
63
  const kind = componentKind ?? 'shared';
75
64
  let targetDir = path.join(process.cwd(), 'src', 'app', context, 'ui', 'components', kind);
76
-
77
65
  if (kind === 'page') {
78
66
  const { pageFolderRaw } = await prompts({
79
67
  type: 'text',
@@ -81,18 +69,14 @@ export default async function generateComponent() {
81
69
  message: 'Page folder name:',
82
70
  validate: (value) => (String(value).trim().length > 0 ? true : 'Page folder name is required')
83
71
  });
84
-
85
72
  const pageFolder = String(pageFolderRaw ?? '').trim();
86
73
  targetDir = path.join(targetDir, pageFolder);
87
74
  }
88
-
89
75
  targetDir = path.join(targetDir, name);
90
-
91
76
  await fs.mkdir(targetDir, { recursive: true });
92
77
  await writeIfNotExists(path.join(targetDir, 'Component.svelte'), createComponentContent(propsName));
93
78
  await writeIfNotExists(path.join(targetDir, 'style.scss'), '');
94
79
  await writeIfNotExists(path.join(targetDir, 'types.ts'), createTypesContent(propsName));
95
80
  await writeIfNotExists(path.join(targetDir, 'index.ts'), createIndexContent(name));
96
-
97
81
  console.log(`✅ Component generated: ${targetDir}`);
98
82
  }