@azure-net/cli 5.0.0-1 → 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 (71) hide show
  1. package/bin/azure-net.js +12 -45
  2. package/bin/azure-net.ts +13 -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 +2 -1
  9. package/src/checks/checkInternal.ts +2 -0
  10. package/src/checks/checkLayerBoundaries.js +50 -54
  11. package/src/checks/checkLayerBoundaries.ts +55 -58
  12. package/src/checks/checkLayerImports.js +136 -0
  13. package/src/checks/checkLayerImports.ts +185 -0
  14. package/src/checks/checkPresenterNames.js +1 -37
  15. package/src/checks/checkPresenterNames.ts +1 -6
  16. package/src/checks/checkProject.js +0 -6
  17. package/src/checks/checkProviderGraph.js +1 -38
  18. package/src/checks/checkProviderNames.js +0 -28
  19. package/src/checks/index.js +1 -1
  20. package/src/checks/index.ts +1 -0
  21. package/src/generate/layers/generateRepository.js +4 -19
  22. package/src/generate/layers/generateRepositoryMethod.js +0 -12
  23. package/src/generate/layers/generateUseCases.js +0 -6
  24. package/src/generate/layers/repositoryShared.js +41 -104
  25. package/src/generate/layers/repositoryShared.ts +17 -12
  26. package/src/generate/module/generateDomain.js +0 -25
  27. package/src/generate/module/generateModule.js +0 -10
  28. package/src/generate/module/generateModulePreset.js +10 -51
  29. package/src/generate/module/generateModulePreset.ts +9 -8
  30. package/src/generate/module/generatePresenter.js +29 -76
  31. package/src/generate/module/generatePresenter.ts +15 -10
  32. package/src/generate/module/generateRepository.js +0 -17
  33. package/src/generate/module/generateSchemaFromType.js +9 -58
  34. package/src/generate/module/generateSchemaFromType.ts +1 -2
  35. package/src/generate/module/generateTypesJson.js +4 -45
  36. package/src/generate/module/generateUseCases.js +0 -9
  37. package/src/generate/module/repositoryModuleShared.js +21 -98
  38. package/src/generate/module/repositoryModuleShared.ts +14 -10
  39. package/src/generate/ui/generateComponent.js +0 -16
  40. package/src/generate/ui/generateDesignComponent.js +0 -12
  41. package/src/generate/ui/generateWidget.js +0 -12
  42. package/src/init/initAliases.js +42 -64
  43. package/src/init/initAliases.ts +47 -42
  44. package/src/init/initEdges.js +0 -15
  45. package/src/init/initStructure.js +48 -21
  46. package/src/init/initStructure.ts +51 -3
  47. package/src/install/installFresh.js +0 -16
  48. package/src/plugins/addLint.js +11 -46
  49. package/src/scaffolds/createCoreSchema.js +6 -40
  50. package/src/scaffolds/createCoreSchema.ts +6 -18
  51. package/src/scaffolds/createDatasource.js +26 -61
  52. package/src/scaffolds/createDatasource.ts +26 -40
  53. package/src/scaffolds/createDatasourceProvider.js +19 -56
  54. package/src/scaffolds/createDatasourceProvider.ts +20 -34
  55. package/src/scaffolds/createPresenterFactory.js +5 -34
  56. package/src/scaffolds/createPresenterFactory.ts +5 -17
  57. package/src/scaffolds/createProgram.js +1 -5
  58. package/src/scaffolds/createResponse.js +3 -11
  59. package/src/scaffolds/createResponse.ts +3 -1
  60. package/src/scaffolds/createSchemaRule.js +3 -8
  61. package/src/scaffolds/createSchemaRule.ts +3 -1
  62. package/src/scaffolds/createTranslationManager.js +3 -14
  63. package/src/scaffolds/createTranslationManager.ts +3 -1
  64. package/src/utils/contextUtils.js +5 -20
  65. package/src/utils/contextUtils.ts +2 -3
  66. package/src/utils/fileUtils.js +3 -38
  67. package/src/utils/fileUtils.ts +0 -26
  68. package/src/utils/loadConfig.js +1 -14
  69. package/src/utils/loadConfig.ts +1 -3
  70. package/src/utils/sharedFoundation.js +48 -0
  71. package/src/utils/sharedFoundation.ts +70 -0
@@ -5,63 +5,61 @@ import { toKebabCase, toPascalCase } from '../../utils/contextUtils.js';
5
5
  import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
6
6
  import { getConfigState, resolveContextAlias, selectContext } from './repositoryModuleShared.js';
7
7
  import { loadUserConfig } from '../../utils/loadConfig.js';
8
-
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../../utils/sharedFoundation.js';
9
9
  const PRIMITIVE_TYPES = new Set(['string', 'number', 'boolean', 'unknown', 'void', 'null', 'undefined', 'bigint', 'symbol', 'never']);
10
-
11
10
  const parseProviderInfo = (content) => {
12
11
  const providerNameMatch = content.match(/export\s+const\s+(\w+)\s*=\s*createBoundaryProvider/);
13
12
  const providerName = providerNameMatch?.[1] ?? 'ApplicationProvider';
14
-
15
13
  const registerMatch = content.match(/register\s*:\s*\([^)]*\)\s*=>\s*\(\{([\s\S]*?)\}\)/m);
16
14
  const registerBody = registerMatch?.[1] ?? '';
17
15
  const keys = [];
18
16
  const keyRegex = /([A-Za-z_]\w*)\s*:/g;
19
17
  let keyMatch;
20
18
  while ((keyMatch = keyRegex.exec(registerBody)) !== null) {
21
- keys.push(keyMatch[1]);
19
+ const key = keyMatch[1];
20
+ if (key) keys.push(key);
22
21
  }
23
-
24
22
  return { providerName, registeredKeys: keys };
25
23
  };
26
-
27
24
  const parseUseCasesInfo = (content) => {
28
25
  const interfaceImport = content.match(/import\s+type\s+\{\s*(\w+)\s*\}\s+from\s+['"]([^'"]+)['"]/);
29
26
  const repositoryInterfaceName = interfaceImport?.[1] ?? null;
30
27
  const domainImportPath = interfaceImport?.[2] ?? null;
31
28
  const domainNameMatch = domainImportPath?.match(/\/layers\/domain\/([^'"]+)$/);
32
29
  const domainName = domainNameMatch?.[1] ?? null;
33
-
34
30
  const methods = [];
35
31
  const declareRegex = /declare\s+(\w+)\s*:\s*\w+\['\w+'\];/g;
36
32
  let declareMatch;
37
33
  while ((declareMatch = declareRegex.exec(content)) !== null) {
38
- methods.push(declareMatch[1]);
34
+ const methodName = declareMatch[1];
35
+ if (methodName) methods.push(methodName);
39
36
  }
40
-
41
37
  return { repositoryInterfaceName, domainName, methods };
42
38
  };
43
-
44
39
  const parseRepositoryInterfaceMethods = (content) => {
45
40
  const methods = [];
46
41
  const regex = /(\w+)\(([^)]*)\)\s*:\s*Promise<([^>]+)>;/g;
47
42
  let match;
48
43
  while ((match = regex.exec(content)) !== null) {
49
- const paramsRaw = match[2].trim();
44
+ const methodName = match[1];
45
+ const paramsChunk = match[2];
46
+ const responseTypeChunk = match[3];
47
+ if (!methodName || paramsChunk === undefined || !responseTypeChunk) continue;
48
+ const paramsRaw = paramsChunk.trim();
50
49
  const params = paramsRaw
51
50
  ? paramsRaw.split(',').map((part) => {
52
51
  const [name, ...typeParts] = part.trim().split(':');
53
- return { name: name.trim(), type: typeParts.join(':').trim() };
52
+ return { name: String(name ?? '').trim(), type: typeParts.join(':').trim() };
54
53
  })
55
54
  : [];
56
55
  methods.push({
57
- name: match[1],
56
+ name: methodName,
58
57
  params,
59
- responseType: match[3].trim()
58
+ responseType: responseTypeChunk.trim()
60
59
  });
61
60
  }
62
61
  return methods;
63
62
  };
64
-
65
63
  const findRepositoryByInterface = async (contextName, repositoryInterfaceName) => {
66
64
  const repositoriesPath = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'infrastructure', 'http', 'repositories');
67
65
  try {
@@ -69,7 +67,6 @@ const findRepositoryByInterface = async (contextName, repositoryInterfaceName) =
69
67
  for (const fileName of files) {
70
68
  if (!fileName.endsWith('.ts') || fileName === 'index.ts') continue;
71
69
  const absolute = path.join(repositoriesPath, fileName);
72
- // eslint-disable-next-line no-await-in-loop
73
70
  const content = await fs.readFile(absolute, 'utf-8');
74
71
  if (content.includes(`implements ${repositoryInterfaceName}`)) {
75
72
  return { filePath: absolute, content };
@@ -80,22 +77,22 @@ const findRepositoryByInterface = async (contextName, repositoryInterfaceName) =
80
77
  return null;
81
78
  }
82
79
  };
83
-
84
80
  const parseRepositoryHttpMethods = (content) => {
85
81
  const methods = new Map();
86
82
  const regex = /public\s+async\s+(\w+)\([^)]*\)\s*\{([\s\S]*?)\n\t\}/g;
87
83
  let match;
88
84
  while ((match = regex.exec(content)) !== null) {
89
85
  const methodName = match[1];
86
+ if (!methodName) continue;
90
87
  const body = match[2] ?? '';
91
88
  const httpMatch = body.match(/http\.(get|post|put|delete|head|options|patch)\(/);
92
- if (httpMatch) methods.set(methodName, httpMatch[1]);
89
+ if (httpMatch?.[1]) methods.set(methodName, httpMatch[1]);
93
90
  }
94
91
  return methods;
95
92
  };
96
-
97
- const getCorePresenterFactories = async () => {
98
- const presenterPath = path.join(process.cwd(), 'src', 'core', 'presenter');
93
+ const getSharedPresenterFactories = async () => {
94
+ const { sharedContext } = await getSharedState();
95
+ const presenterPath = getFoundationConstructPath(sharedContext.name, 'presenter');
99
96
  try {
100
97
  const files = await fs.readdir(presenterPath);
101
98
  return files.filter((item) => item.endsWith('.ts') && item !== 'index.ts').map((item) => item.replace(/\.ts$/, ''));
@@ -103,9 +100,9 @@ const getCorePresenterFactories = async () => {
103
100
  return [];
104
101
  }
105
102
  };
106
-
107
- const getCoreSchemaFactories = async () => {
108
- const schemaPath = path.join(process.cwd(), 'src', 'core', 'schema');
103
+ const getSharedSchemaFactories = async () => {
104
+ const { sharedContext } = await getSharedState();
105
+ const schemaPath = getFoundationConstructPath(sharedContext.name, 'schema');
109
106
  try {
110
107
  const files = await fs.readdir(schemaPath);
111
108
  return files.filter((item) => item.endsWith('.ts') && item !== 'index.ts').map((item) => item.replace(/\.ts$/, ''));
@@ -113,19 +110,15 @@ const getCoreSchemaFactories = async () => {
113
110
  return [];
114
111
  }
115
112
  };
116
-
117
113
  const normalizePresenterName = (rawName, fallbackUseCasesName) => {
118
114
  const fallback = fallbackUseCasesName.replace(/UseCases$/, '') || 'Module';
119
115
  const source = String(rawName ?? '').trim() || fallback;
120
116
  const normalized = toPascalCase(source);
121
117
  return normalized.endsWith('Presenter') ? normalized : `${normalized}Presenter`;
122
118
  };
123
-
124
119
  const isPrimitive = (typeName) => PRIMITIVE_TYPES.has(String(typeName ?? '').trim());
125
-
126
120
  const normalizeTypeImports = (types) => Array.from(new Set(types.filter((typeName) => !isPrimitive(typeName))));
127
121
  const isPathParamName = (name) => name === 'pathParam' || name === 'path';
128
-
129
122
  const buildMethodInvocation = (method, useCasesName, schemaByMethod) => {
130
123
  const paramsSignature = method.params.map((item) => `${item.name}: ${item.type}`).join(', ');
131
124
  const argNames = method.params.map((item) => item.name);
@@ -137,21 +130,17 @@ const buildMethodInvocation = (method, useCasesName, schemaByMethod) => {
137
130
  }
138
131
  return argName;
139
132
  });
140
-
141
133
  const httpMethod = method.httpMethod;
142
134
  if (httpMethod === 'get' || httpMethod === 'head' || httpMethod === 'options') {
143
135
  return `\tconst ${method.name} = (${paramsSignature}) => createAsyncResource(() => ${useCasesName}.${method.name}(${args.join(', ')}));`;
144
136
  }
145
-
146
137
  const responseType = method.responseType;
147
138
  const requestType = requestParam?.type ?? null;
148
139
  let generic = `<${responseType}`;
149
140
  if (requestType) generic += `, ${requestType}`;
150
141
  generic += '>';
151
-
152
142
  return `\tconst ${method.name} = async (${paramsSignature}) =>\n\t\tawait createAsyncAction${generic}(() => ${useCasesName}.${method.name}(${args.join(', ')}));`;
153
143
  };
154
-
155
144
  const getApplicationProviderFile = async (contextName) => {
156
145
  const providersPath = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'application', 'providers');
157
146
  const targetPath = path.join(providersPath, 'ApplicationProvider.ts');
@@ -164,7 +153,6 @@ const getApplicationProviderFile = async (contextName) => {
164
153
  return candidate ? path.join(providersPath, candidate) : null;
165
154
  }
166
155
  };
167
-
168
156
  export default async function generatePresenter(options = {}) {
169
157
  let contextName = options.contextName ? String(options.contextName) : null;
170
158
  if (!contextName) {
@@ -174,17 +162,14 @@ export default async function generatePresenter(options = {}) {
174
162
  process.exitCode = 1;
175
163
  return;
176
164
  }
177
-
178
165
  const appProviderFile = await getApplicationProviderFile(contextName);
179
166
  if (!appProviderFile) {
180
167
  console.error('❌ ApplicationProvider file was not found.');
181
168
  process.exitCode = 1;
182
169
  return;
183
170
  }
184
-
185
171
  const appProviderContent = await fs.readFile(appProviderFile, 'utf-8');
186
172
  const providerInfo = parseProviderInfo(appProviderContent);
187
-
188
173
  const useCasesPath = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'application', 'use-cases');
189
174
  let useCasesFiles = [];
190
175
  try {
@@ -194,14 +179,12 @@ export default async function generatePresenter(options = {}) {
194
179
  } catch {
195
180
  // ignore
196
181
  }
197
-
198
182
  const registeredUseCases = useCasesFiles.filter((item) => providerInfo.registeredKeys.includes(item));
199
183
  if (!registeredUseCases.length) {
200
184
  console.error('❌ No registered UseCases were found in ApplicationProvider register().');
201
185
  process.exitCode = 1;
202
186
  return;
203
187
  }
204
-
205
188
  let useCasesName = options.useCasesName ? String(options.useCasesName) : '';
206
189
  if (!useCasesName) {
207
190
  const { selectedUseCases } = await prompts({
@@ -213,13 +196,11 @@ export default async function generatePresenter(options = {}) {
213
196
  });
214
197
  useCasesName = String(selectedUseCases ?? '');
215
198
  }
216
-
217
199
  if (!registeredUseCases.includes(useCasesName)) {
218
200
  console.error(`❌ UseCases "${useCasesName}" is not registered in ApplicationProvider.`);
219
201
  process.exitCode = 1;
220
202
  return;
221
203
  }
222
-
223
204
  const useCasesFile = path.join(useCasesPath, `${useCasesName}.ts`);
224
205
  const useCasesContent = await fs.readFile(useCasesFile, 'utf-8');
225
206
  const useCasesInfo = parseUseCasesInfo(useCasesContent);
@@ -228,14 +209,12 @@ export default async function generatePresenter(options = {}) {
228
209
  process.exitCode = 1;
229
210
  return;
230
211
  }
231
-
232
212
  const repositoryInfo = await findRepositoryByInterface(contextName, useCasesInfo.repositoryInterfaceName);
233
213
  if (!repositoryInfo) {
234
214
  console.error(`❌ Repository implementing ${useCasesInfo.repositoryInterfaceName} was not found.`);
235
215
  process.exitCode = 1;
236
216
  return;
237
217
  }
238
-
239
218
  const repositoryHttpMethods = parseRepositoryHttpMethods(repositoryInfo.content);
240
219
  const domainPortsInterfacePath = path.join(
241
220
  process.cwd(),
@@ -252,25 +231,23 @@ export default async function generatePresenter(options = {}) {
252
231
  const repositoryMethods = parseRepositoryInterfaceMethods(repositoryInterfaceContent).filter((method) =>
253
232
  useCasesInfo.methods.includes(method.name)
254
233
  );
255
-
256
234
  const { presenterNameRaw } = await prompts({
257
235
  type: 'text',
258
236
  name: 'presenterNameRaw',
259
237
  message: 'Presenter name (optional):'
260
238
  });
261
-
262
239
  const presenterName = normalizePresenterName(presenterNameRaw, useCasesName);
263
240
  const presenterFolder = toKebabCase(presenterName.replace(/Presenter$/, ''));
264
241
  const presentationPath = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'presentation', presenterFolder);
265
242
  await fs.mkdir(presentationPath, { recursive: true });
266
-
267
- const { contexts, coreAlias } = await getConfigState();
243
+ const { contexts, sharedAlias } = await getConfigState();
268
244
  const contextAlias = resolveContextAlias(contexts, contextName);
269
-
245
+ const presenterImportPath = getFoundationConstructImportPath(sharedAlias, 'presenter');
246
+ const schemaFactoryImportPath = getFoundationConstructImportPath(sharedAlias, 'schema');
270
247
  let presenterFactory = String((await loadUserConfig()).defaultPresenterFactory ?? '').trim();
271
248
  let useNativePresenter = false;
272
249
  if (!presenterFactory) {
273
- const availableFactories = await getCorePresenterFactories();
250
+ const availableFactories = await getSharedPresenterFactories();
274
251
  const { selectedFactory } = await prompts({
275
252
  type: 'select',
276
253
  name: 'selectedFactory',
@@ -281,7 +258,6 @@ export default async function generatePresenter(options = {}) {
281
258
  ],
282
259
  initial: 0
283
260
  });
284
-
285
261
  if (selectedFactory === '__native__') {
286
262
  useNativePresenter = true;
287
263
  presenterFactory = 'createPresenter';
@@ -289,46 +265,38 @@ export default async function generatePresenter(options = {}) {
289
265
  presenterFactory = String(selectedFactory ?? '');
290
266
  }
291
267
  }
292
-
293
268
  if (!presenterFactory) {
294
269
  useNativePresenter = true;
295
270
  presenterFactory = 'createPresenter';
296
271
  }
297
-
298
272
  const mutationMethods = repositoryMethods.filter((method) => {
299
273
  const httpMethod = repositoryHttpMethods.get(method.name) ?? 'get';
300
274
  return httpMethod !== 'get' && httpMethod !== 'head' && httpMethod !== 'options';
301
275
  });
302
-
303
276
  let schemaFactory = String((await loadUserConfig()).defaultSchemaFactory ?? '').trim();
304
277
  let schemaFactoryResolved = false;
305
278
  const schemaByMethod = new Map();
306
279
  const schemaImports = [];
307
280
  let schemaIndexNeeded = false;
308
-
309
281
  if (mutationMethods.length) {
310
282
  for (const method of mutationMethods) {
311
283
  const requestParam = method.params.find((item) => !isPathParamName(item.name));
312
284
  if (!requestParam) continue;
313
-
314
285
  const { shouldCreateSchema } = await prompts({
315
286
  type: 'confirm',
316
287
  name: 'shouldCreateSchema',
317
288
  message: `Create schema for "${method.name}"?`,
318
289
  initial: false
319
290
  });
320
-
321
291
  if (!shouldCreateSchema) continue;
322
-
323
292
  if (!schemaFactoryResolved) {
324
293
  if (!schemaFactory) {
325
- const availableSchemas = await getCoreSchemaFactories();
294
+ const availableSchemas = await getSharedSchemaFactories();
326
295
  if (!availableSchemas.length) {
327
- console.error('❌ No schema factories found in src/core/schema.');
296
+ console.error('❌ No schema factories found in shared foundation constructs/schema.');
328
297
  process.exitCode = 1;
329
298
  return;
330
299
  }
331
-
332
300
  const { selectedSchemaFactory } = await prompts({
333
301
  type: 'select',
334
302
  name: 'selectedSchemaFactory',
@@ -336,37 +304,30 @@ export default async function generatePresenter(options = {}) {
336
304
  choices: availableSchemas.map((item) => ({ title: item, value: item })),
337
305
  initial: 0
338
306
  });
339
-
340
307
  schemaFactory = String(selectedSchemaFactory ?? '');
341
308
  }
342
-
343
309
  schemaFactoryResolved = true;
344
310
  }
345
-
346
311
  const schemaConstName = `${toPascalCase(method.name)}${presenterName}Schema`;
347
312
  const schemaFileName = `${schemaConstName}.ts`;
348
313
  const schemaPath = path.join(presentationPath, 'schema');
349
314
  await fs.mkdir(schemaPath, { recursive: true });
350
315
  schemaIndexNeeded = true;
351
-
352
- const schemaContent = `import { ${schemaFactory} } from '${coreAlias}/schema';
316
+ const schemaContent = `import { ${schemaFactory} } from '${schemaFactoryImportPath}';
353
317
  import type { ${requestParam.type} } from '${contextAlias}/layers/domain/${useCasesInfo.domainName}';
354
318
 
355
319
  export const ${schemaConstName} = ${schemaFactory}<${requestParam.type}>()
356
320
  \t.rules((rules) => ({}))
357
321
  \t.create();
358
322
  `;
359
-
360
323
  await writeIfNotExists(path.join(schemaPath, schemaFileName), schemaContent);
361
324
  schemaByMethod.set(method.name, { constName: schemaConstName });
362
325
  schemaImports.push(schemaConstName);
363
326
  }
364
327
  }
365
-
366
328
  if (schemaIndexNeeded) {
367
329
  await updateIndexTs(path.join(presentationPath, 'schema'));
368
330
  }
369
-
370
331
  const hasGetLike = repositoryMethods.some((method) => {
371
332
  const httpMethod = repositoryHttpMethods.get(method.name) ?? 'get';
372
333
  return httpMethod === 'get' || httpMethod === 'head' || httpMethod === 'options';
@@ -375,29 +336,23 @@ export const ${schemaConstName} = ${schemaFactory}<${requestParam.type}>()
375
336
  const httpMethod = repositoryHttpMethods.get(method.name) ?? 'get';
376
337
  return httpMethod !== 'get' && httpMethod !== 'head' && httpMethod !== 'options';
377
338
  });
378
-
379
339
  const helperParams = [hasGetLike ? 'createAsyncResource' : null, hasMutation ? 'createAsyncAction' : null].filter(Boolean).join(', ');
380
-
381
340
  const typeImports = normalizeTypeImports(
382
341
  repositoryMethods.flatMap((method) => [method.responseType, ...method.params.map((item) => item.type)])
383
342
  );
384
-
385
343
  const methodsCode = repositoryMethods
386
344
  .map((method) => {
387
345
  const httpMethod = repositoryHttpMethods.get(method.name) ?? 'get';
388
346
  return buildMethodInvocation({ ...method, httpMethod }, useCasesName, schemaByMethod);
389
347
  })
390
348
  .join('\n\n');
391
-
392
349
  const factoryImport = useNativePresenter
393
350
  ? `import { createPresenter } from '@azure-net/kit/delivery';`
394
- : `import { ${presenterFactory} } from '${coreAlias}/presenter';`;
395
-
351
+ : `import { ${presenterFactory} } from '${presenterImportPath}';`;
396
352
  const schemaImportLine = schemaImports.length ? `import { ${Array.from(new Set(schemaImports)).join(', ')} } from './schema';` : '';
397
353
  const typeImportLine = typeImports.length
398
354
  ? `import type { ${typeImports.join(', ')} } from '${contextAlias}/layers/domain/${useCasesInfo.domainName}';`
399
355
  : '';
400
-
401
356
  const presenterId = `${toPascalCase(contextName)}${presenterName}`;
402
357
  const presenterContent = `${factoryImport}
403
358
  import { ${providerInfo.providerName} } from '${contextAlias}/layers/application/providers';
@@ -411,9 +366,7 @@ ${methodsCode ? `${methodsCode}\n` : ''}
411
366
  \treturn { ${repositoryMethods.map((method) => method.name).join(', ')} };
412
367
  });
413
368
  `;
414
-
415
369
  await writeIfNotExists(path.join(presentationPath, `${presenterName}.ts`), presenterContent);
416
370
  await fs.writeFile(path.join(presentationPath, 'index.ts'), `export * from './${presenterName}';\n`, 'utf-8');
417
-
418
371
  console.log(`✅ Presenter generated: ${path.join(presentationPath, `${presenterName}.ts`)}`);
419
372
  }
@@ -5,6 +5,7 @@ import { toKebabCase, toPascalCase } from '../../utils/contextUtils.js';
5
5
  import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
6
6
  import { getConfigState, resolveContextAlias, selectContext } from './repositoryModuleShared.js';
7
7
  import { loadUserConfig } from '../../utils/loadConfig.js';
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../../utils/sharedFoundation.js';
8
9
 
9
10
  type MethodParam = { name: string; type: string };
10
11
  type RepositoryMethod = { name: string; params: MethodParam[]; responseType: string };
@@ -108,8 +109,9 @@ const parseRepositoryHttpMethods = (content: string): Map<string, string> => {
108
109
  return methods;
109
110
  };
110
111
 
111
- const getCorePresenterFactories = async (): Promise<string[]> => {
112
- const presenterPath = path.join(process.cwd(), 'src', 'core', 'presenter');
112
+ const getSharedPresenterFactories = async (): Promise<string[]> => {
113
+ const { sharedContext } = await getSharedState();
114
+ const presenterPath = getFoundationConstructPath(sharedContext.name, 'presenter');
113
115
  try {
114
116
  const files = await fs.readdir(presenterPath);
115
117
  return files.filter((item) => item.endsWith('.ts') && item !== 'index.ts').map((item) => item.replace(/\.ts$/, ''));
@@ -118,8 +120,9 @@ const getCorePresenterFactories = async (): Promise<string[]> => {
118
120
  }
119
121
  };
120
122
 
121
- const getCoreSchemaFactories = async (): Promise<string[]> => {
122
- const schemaPath = path.join(process.cwd(), 'src', 'core', 'schema');
123
+ const getSharedSchemaFactories = async (): Promise<string[]> => {
124
+ const { sharedContext } = await getSharedState();
125
+ const schemaPath = getFoundationConstructPath(sharedContext.name, 'schema');
123
126
  try {
124
127
  const files = await fs.readdir(schemaPath);
125
128
  return files.filter((item) => item.endsWith('.ts') && item !== 'index.ts').map((item) => item.replace(/\.ts$/, ''));
@@ -287,13 +290,15 @@ export default async function generatePresenter(options: GeneratePresenterOption
287
290
  const presentationPath = path.join(process.cwd(), 'src', 'app', contextName, 'layers', 'presentation', presenterFolder);
288
291
  await fs.mkdir(presentationPath, { recursive: true });
289
292
 
290
- const { contexts, coreAlias } = await getConfigState();
293
+ const { contexts, sharedAlias } = await getConfigState();
291
294
  const contextAlias = resolveContextAlias(contexts, contextName);
295
+ const presenterImportPath = getFoundationConstructImportPath(sharedAlias, 'presenter');
296
+ const schemaFactoryImportPath = getFoundationConstructImportPath(sharedAlias, 'schema');
292
297
 
293
298
  let presenterFactory = String((await loadUserConfig()).defaultPresenterFactory ?? '').trim();
294
299
  let useNativePresenter = false;
295
300
  if (!presenterFactory) {
296
- const availableFactories = await getCorePresenterFactories();
301
+ const availableFactories = await getSharedPresenterFactories();
297
302
  const { selectedFactory } = await prompts({
298
303
  type: 'select',
299
304
  name: 'selectedFactory',
@@ -345,9 +350,9 @@ export default async function generatePresenter(options: GeneratePresenterOption
345
350
 
346
351
  if (!schemaFactoryResolved) {
347
352
  if (!schemaFactory) {
348
- const availableSchemas = await getCoreSchemaFactories();
353
+ const availableSchemas = await getSharedSchemaFactories();
349
354
  if (!availableSchemas.length) {
350
- console.error('❌ No schema factories found in src/core/schema.');
355
+ console.error('❌ No schema factories found in shared foundation constructs/schema.');
351
356
  process.exitCode = 1;
352
357
  return;
353
358
  }
@@ -372,7 +377,7 @@ export default async function generatePresenter(options: GeneratePresenterOption
372
377
  await fs.mkdir(schemaPath, { recursive: true });
373
378
  schemaIndexNeeded = true;
374
379
 
375
- const schemaContent = `import { ${schemaFactory} } from '${coreAlias}/schema';
380
+ const schemaContent = `import { ${schemaFactory} } from '${schemaFactoryImportPath}';
376
381
  import type { ${requestParam.type} } from '${contextAlias}/layers/domain/${useCasesInfo.domainName}';
377
382
 
378
383
  export const ${schemaConstName} = ${schemaFactory}<${requestParam.type}>()
@@ -414,7 +419,7 @@ export const ${schemaConstName} = ${schemaFactory}<${requestParam.type}>()
414
419
 
415
420
  const factoryImport = useNativePresenter
416
421
  ? `import { createPresenter } from '@azure-net/kit/delivery';`
417
- : `import { ${presenterFactory} } from '${coreAlias}/presenter';`;
422
+ : `import { ${presenterFactory} } from '${presenterImportPath}';`;
418
423
 
419
424
  const schemaImportLine = schemaImports.length ? `import { ${Array.from(new Set(schemaImports)).join(', ')} } from './schema';` : '';
420
425
  const typeImportLine = typeImports.length
@@ -17,7 +17,6 @@ import {
17
17
  writeRepositoryInterface
18
18
  } from './repositoryModuleShared.js';
19
19
  import { updateIndexTs, writeIfNotExists } from '../../utils/fileUtils.js';
20
-
21
20
  export default async function generateRepository(options = {}) {
22
21
  let contextName = options.contextName ? String(options.contextName) : null;
23
22
  if (!contextName) {
@@ -27,7 +26,6 @@ export default async function generateRepository(options = {}) {
27
26
  process.exitCode = 1;
28
27
  return null;
29
28
  }
30
-
31
29
  let domainName = options.domainName ? String(options.domainName) : null;
32
30
  if (!domainName) {
33
31
  domainName = await selectDomain(contextName);
@@ -36,23 +34,19 @@ export default async function generateRepository(options = {}) {
36
34
  process.exitCode = 1;
37
35
  return null;
38
36
  }
39
-
40
37
  const { repositoryNameRaw } = await prompts({
41
38
  type: 'text',
42
39
  name: 'repositoryNameRaw',
43
40
  message: 'Repository name:',
44
41
  validate: (value) => (String(value ?? '').trim().length > 0 ? true : 'Repository name is required')
45
42
  });
46
-
47
43
  const className = ensureRepositoryName(repositoryNameRaw);
48
44
  const meta = getRepositoryMeta(className);
49
-
50
45
  const datasource = await selectDatasource(contextName);
51
46
  if (!datasource) {
52
47
  process.exitCode = 1;
53
48
  return null;
54
49
  }
55
-
56
50
  const domainTypes = await getDomainTypes(contextName, domainName);
57
51
  const { methodsCountRaw } = await prompts({
58
52
  type: 'number',
@@ -61,33 +55,28 @@ export default async function generateRepository(options = {}) {
61
55
  initial: 1,
62
56
  min: 0
63
57
  });
64
-
65
58
  const methodsCount = Number(methodsCountRaw ?? 0);
66
59
  if (!Number.isFinite(methodsCount) || methodsCount < 0) {
67
60
  console.error('❌ Invalid methods count.');
68
61
  process.exitCode = 1;
69
62
  return null;
70
63
  }
71
-
72
64
  const methods = await promptMethodsBatch(domainTypes, methodsCount);
73
65
  if (!methods) {
74
66
  process.exitCode = 1;
75
67
  return null;
76
68
  }
77
-
78
69
  const { contexts } = await getConfigState();
79
70
  const contextAlias = resolveContextAlias(contexts, contextName);
80
71
  const repositoriesPath = resolveRepositoriesPath(contextName);
81
72
  await fs.mkdir(repositoriesPath, { recursive: true });
82
73
  const repositoryPath = path.join(repositoriesPath, `${meta.className}.ts`);
83
-
84
74
  await writeRepositoryInterface({
85
75
  contextName,
86
76
  domainName,
87
77
  meta,
88
78
  methods
89
79
  });
90
-
91
80
  const repositoryContent = buildRepositoryContent({
92
81
  meta,
93
82
  contextAlias,
@@ -95,23 +84,19 @@ export default async function generateRepository(options = {}) {
95
84
  datasource,
96
85
  methods
97
86
  });
98
-
99
87
  const created = await writeIfNotExists(repositoryPath, repositoryContent);
100
88
  await updateIndexTs(repositoriesPath);
101
-
102
89
  if (!created) {
103
90
  console.warn(`⚠️ Repository "${meta.className}" already exists. File was not overwritten.`);
104
91
  process.exitCode = 1;
105
92
  return null;
106
93
  }
107
-
108
94
  const { shouldCreateUseCases } = await prompts({
109
95
  type: 'confirm',
110
96
  name: 'shouldCreateUseCases',
111
97
  message: 'Create UseCases for this repository?',
112
98
  initial: true
113
99
  });
114
-
115
100
  let useCasesCreated = false;
116
101
  if (shouldCreateUseCases) {
117
102
  await createUseCasesForRepository({
@@ -122,7 +107,6 @@ export default async function generateRepository(options = {}) {
122
107
  });
123
108
  useCasesCreated = true;
124
109
  }
125
-
126
110
  if (!shouldCreateUseCases && options.forceCreateUseCases) {
127
111
  await createUseCasesForRepository({
128
112
  contextName,
@@ -132,7 +116,6 @@ export default async function generateRepository(options = {}) {
132
116
  });
133
117
  useCasesCreated = true;
134
118
  }
135
-
136
119
  console.log(`✅ Repository generated: ${repositoryPath}`);
137
120
  return {
138
121
  contextName,