@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
@@ -3,11 +3,12 @@ import path from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
5
5
  import { toPascalCase } from '../utils/contextUtils.js';
6
- import { loadUserConfig, resolveConfigFile } from '../utils/loadConfig.js';
6
+ import { loadUserConfig } from '../utils/loadConfig.js';
7
7
  import { normalizeContexts } from '../init/initAliases.js';
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
8
9
 
9
10
  type NormalizedContext = { name: string; alias: string };
10
- type DatasourceChoiceValue = null | { name: string; source: 'core' | 'context' };
11
+ type DatasourceChoiceValue = null | { name: string; source: 'shared' | 'context' };
11
12
 
12
13
  const createProviderTemplateWithoutDatasource = (
13
14
  providerName: string,
@@ -45,26 +46,6 @@ export const ${providerName} = createBoundaryProvider('${contextPrefix}${provide
45
46
  `;
46
47
  };
47
48
 
48
- const getCoreAliasOrThrow = async (): Promise<string> => {
49
- const configRef = resolveConfigFile();
50
- if (!configRef.exists) {
51
- throw new Error(
52
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
53
- );
54
- }
55
-
56
- const content = await fs.readFile(configRef.filepath, 'utf-8');
57
- const aliasMatch = content.match(/coreAlias\s*:\s*['"`]([^'"`]+)['"`]/);
58
- const alias = aliasMatch?.[1]?.trim();
59
- if (!alias) {
60
- throw new Error(
61
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
62
- );
63
- }
64
-
65
- return alias.startsWith('$') ? alias : `$${alias}`;
66
- };
67
-
68
49
  const getDatasourcesFromPath = async (targetPath: string): Promise<string[]> => {
69
50
  try {
70
51
  const files = await fs.readdir(targetPath);
@@ -74,11 +55,11 @@ const getDatasourcesFromPath = async (targetPath: string): Promise<string[]> =>
74
55
  }
75
56
  };
76
57
 
77
- const resolveContextPrefix = (target: string): string => (target === 'core' ? 'Core' : toPascalCase(target));
58
+ const resolveContextPrefix = (target: string): string => (target === '__shared__' ? 'Shared' : toPascalCase(target));
78
59
 
79
60
  const resolveProviderPath = (target: string): string => {
80
- if (target === 'core') {
81
- return path.join(process.cwd(), 'src', 'core', 'provider');
61
+ if (target === '__shared__') {
62
+ throw new Error('Shared provider path must be resolved async.');
82
63
  }
83
64
 
84
65
  return path.join(process.cwd(), 'src', 'app', target, 'layers', 'infrastructure', 'providers');
@@ -109,7 +90,10 @@ export default async function createDatasourceProvider(): Promise<void> {
109
90
  const providerName = ensureProviderName(String(providerNameRaw ?? 'DatasourceProvider'));
110
91
  const config = await loadUserConfig();
111
92
  const contexts = normalizeContexts(config.contexts) as NormalizedContext[];
112
- const contextChoices = [{ title: 'core', value: 'core' }, ...contexts.map((context) => ({ title: context.name, value: context.name }))];
93
+ const contextChoices = [
94
+ { title: 'shared foundation', value: '__shared__' },
95
+ ...contexts.map((context) => ({ title: context.name, value: context.name }))
96
+ ];
113
97
 
114
98
  const { target } = await prompts({
115
99
  type: 'select',
@@ -119,18 +103,19 @@ export default async function createDatasourceProvider(): Promise<void> {
119
103
  initial: 0
120
104
  });
121
105
 
122
- const selectedTarget = String(target ?? 'core');
123
- const coreDatasources = await getDatasourcesFromPath(path.join(process.cwd(), 'src', 'core', 'datasource'));
106
+ const selectedTarget = String(target ?? '__shared__');
107
+ const { sharedAlias, sharedContext } = await getSharedState();
108
+ const sharedDatasources = await getDatasourcesFromPath(getFoundationConstructPath(sharedContext.name, 'datasource'));
124
109
  const contextDatasources =
125
- selectedTarget === 'core'
110
+ selectedTarget === '__shared__'
126
111
  ? []
127
112
  : await getDatasourcesFromPath(
128
113
  path.join(process.cwd(), 'src', 'app', selectedTarget, 'layers', 'infrastructure', 'http', 'datasources')
129
114
  );
130
115
 
131
116
  const datasourceChoices: Array<{ title: string; value: DatasourceChoiceValue }> = [{ title: 'No datasource', value: null }];
132
- for (const datasource of coreDatasources) {
133
- datasourceChoices.push({ title: `${datasource} (core)`, value: { name: datasource, source: 'core' } });
117
+ for (const datasource of sharedDatasources) {
118
+ datasourceChoices.push({ title: `${datasource} (shared foundation)`, value: { name: datasource, source: 'shared' } });
134
119
  }
135
120
  for (const datasource of contextDatasources) {
136
121
  datasourceChoices.push({ title: `${datasource} (${selectedTarget})`, value: { name: datasource, source: 'context' } });
@@ -152,14 +137,15 @@ export default async function createDatasourceProvider(): Promise<void> {
152
137
  content = createProviderTemplateWithoutDatasource(providerName, contextPrefix);
153
138
  } else {
154
139
  const importPath =
155
- selectedDatasource.source === 'core'
156
- ? `${await getCoreAliasOrThrow()}/datasource`
140
+ selectedDatasource.source === 'shared'
141
+ ? getFoundationConstructImportPath(sharedAlias, 'datasource')
157
142
  : `${resolveContextAlias(contexts, selectedTarget)}/layers/infrastructure/http/datasources`;
158
143
 
159
144
  content = createProviderTemplateWithDatasource(providerName, contextPrefix, selectedDatasource.name, importPath);
160
145
  }
161
146
 
162
- const providerPath = resolveProviderPath(selectedTarget);
147
+ const providerPath =
148
+ selectedTarget === '__shared__' ? getFoundationConstructPath(sharedContext.name, 'provider') : resolveProviderPath(selectedTarget);
163
149
  await fs.mkdir(providerPath, { recursive: true });
164
150
 
165
151
  const filePath = path.join(providerPath, `${providerName}.ts`);
@@ -4,7 +4,7 @@ import path from 'node:path';
4
4
  import prompts from 'prompts';
5
5
  import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
6
6
  import { toPascalCase } from '../utils/contextUtils.js';
7
-
7
+ import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
8
8
  const createPresenterFactoryTemplate = (
9
9
  factoryName
10
10
  ) => `import { createErrorHandler, createAsyncHelpers, createPresenterFactory } from '@azure-net/kit/delivery';
@@ -13,36 +13,27 @@ export const AsyncHelpers = createAsyncHelpers({ handler: createErrorHandler() }
13
13
 
14
14
  export const ${factoryName} = createPresenterFactory({ ...AsyncHelpers });
15
15
  `;
16
-
17
16
  const resolveConfigFile = (cwd) => {
18
17
  const tsPath = path.join(cwd, 'azure-net.config.ts');
19
18
  const jsPath = path.join(cwd, 'azure-net.config.js');
20
-
21
19
  if (fsSync.existsSync(tsPath)) {
22
20
  return { filepath: tsPath, type: 'ts', exists: true, content: fsSync.readFileSync(tsPath, 'utf-8') };
23
21
  }
24
22
  if (fsSync.existsSync(jsPath)) {
25
23
  return { filepath: jsPath, type: 'js', exists: true, content: fsSync.readFileSync(jsPath, 'utf-8') };
26
24
  }
27
-
28
25
  return { filepath: tsPath, type: 'ts', exists: false, content: '' };
29
26
  };
30
-
31
27
  const buildDefaultPresenterFactoryConfigEntry = (factoryName) => {
32
28
  return `defaultPresenterFactory: '${factoryName}'`;
33
29
  };
34
-
35
30
  const upsertDefaultPresenterFactoryConfig = (content, factoryName) => {
36
31
  const hasExportDefaultObject = /export\s+default\s*\{[\s\S]*\}\s*;?/.test(content);
37
32
  const defaultFactoryEntry = buildDefaultPresenterFactoryConfigEntry(factoryName);
38
- const coreAliasEntry = `coreAlias: '$core'`;
39
-
40
33
  if (!hasExportDefaultObject) {
41
- return `export default {\n\t${coreAliasEntry},\n\t${defaultFactoryEntry}\n};\n`;
34
+ return `export default {\n\t${defaultFactoryEntry}\n};\n`;
42
35
  }
43
-
44
36
  let nextContent = content;
45
-
46
37
  if (/defaultPresenterFactory\s*:/.test(nextContent)) {
47
38
  nextContent = nextContent.replace(/defaultPresenterFactory\s*:\s*(\{[\s\S]*?\n\s*\}|['"`][^'"`]+['"`])/m, defaultFactoryEntry);
48
39
  } else if (/defaultPresenter\s*:/.test(nextContent)) {
@@ -53,27 +44,12 @@ const upsertDefaultPresenterFactoryConfig = (content, factoryName) => {
53
44
  if (!trimmedRight.length) {
54
45
  return `export default {\n\t${defaultFactoryEntry}\n};`;
55
46
  }
56
-
57
47
  const withComma = trimmedRight.endsWith(',') ? trimmedRight : `${trimmedRight},`;
58
48
  return `export default {${withComma}\n\t${defaultFactoryEntry}\n};`;
59
49
  });
60
50
  }
61
-
62
- if (/coreAlias\s*:/.test(nextContent)) {
63
- return nextContent.replace(/coreAlias\s*:\s*['"`][^'"`]+['"`]/, coreAliasEntry);
64
- }
65
-
66
- return nextContent.replace(/export\s+default\s*\{([\s\S]*)\}\s*;?/, (_full, body) => {
67
- const trimmedRight = body.replace(/\s*$/, '');
68
- if (!trimmedRight.length) {
69
- return `export default {\n\t${coreAliasEntry}\n};`;
70
- }
71
-
72
- const withComma = trimmedRight.endsWith(',') ? trimmedRight : `${trimmedRight},`;
73
- return `export default {${withComma}\n\t${coreAliasEntry}\n};`;
74
- });
51
+ return nextContent;
75
52
  };
76
-
77
53
  export default async function createPresenterFactory() {
78
54
  const { presenterFactoryNameRaw } = await prompts({
79
55
  type: 'text',
@@ -81,13 +57,11 @@ export default async function createPresenterFactory() {
81
57
  message: 'Presenter factory name:',
82
58
  initial: 'AppPresenter'
83
59
  });
84
-
85
60
  const presenterFactoryName = toPascalCase(String(presenterFactoryNameRaw ?? 'AppPresenter')) || 'AppPresenter';
86
- const presenterRootPath = path.join(process.cwd(), 'src', 'core', 'presenter');
61
+ const { sharedContext } = await getSharedState();
62
+ const presenterRootPath = getFoundationConstructPath(sharedContext.name, 'presenter');
87
63
  const filePath = path.join(presenterRootPath, `${presenterFactoryName}.ts`);
88
-
89
64
  await fs.mkdir(presenterRootPath, { recursive: true });
90
-
91
65
  const created = await writeIfNotExists(filePath, createPresenterFactoryTemplate(presenterFactoryName));
92
66
  if (!created) {
93
67
  console.log(`⚠️ Presenter factory "${presenterFactoryName}" already exists. File was not overwritten.`);
@@ -95,16 +69,13 @@ export default async function createPresenterFactory() {
95
69
  await updateIndexTs(presenterRootPath);
96
70
  console.log(`✅ Presenter factory created: ${filePath}`);
97
71
  }
98
-
99
72
  const { useAsDefault } = await prompts({
100
73
  type: 'confirm',
101
74
  name: 'useAsDefault',
102
75
  message: 'Use this presenter factory as default for the project?',
103
76
  initial: true
104
77
  });
105
-
106
78
  if (!useAsDefault) return;
107
-
108
79
  const config = resolveConfigFile(process.cwd());
109
80
  const nextContent = upsertDefaultPresenterFactoryConfig(config.content, presenterFactoryName);
110
81
  await fs.writeFile(config.filepath, nextContent, 'utf-8');
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import prompts from 'prompts';
5
5
  import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
6
6
  import { toPascalCase } from '../utils/contextUtils.js';
7
+ import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
7
8
 
8
9
  type ConfigPath = 'ts' | 'js';
9
10
 
@@ -37,10 +38,9 @@ const buildDefaultPresenterFactoryConfigEntry = (factoryName: string): string =>
37
38
  const upsertDefaultPresenterFactoryConfig = (content: string, factoryName: string): string => {
38
39
  const hasExportDefaultObject = /export\s+default\s*\{[\s\S]*\}\s*;?/.test(content);
39
40
  const defaultFactoryEntry = buildDefaultPresenterFactoryConfigEntry(factoryName);
40
- const coreAliasEntry = `coreAlias: '$core'`;
41
41
 
42
42
  if (!hasExportDefaultObject) {
43
- return `export default {\n\t${coreAliasEntry},\n\t${defaultFactoryEntry}\n};\n`;
43
+ return `export default {\n\t${defaultFactoryEntry}\n};\n`;
44
44
  }
45
45
 
46
46
  let nextContent = content;
@@ -60,20 +60,7 @@ const upsertDefaultPresenterFactoryConfig = (content: string, factoryName: strin
60
60
  return `export default {${withComma}\n\t${defaultFactoryEntry}\n};`;
61
61
  });
62
62
  }
63
-
64
- if (/coreAlias\s*:/.test(nextContent)) {
65
- return nextContent.replace(/coreAlias\s*:\s*['"`][^'"`]+['"`]/, coreAliasEntry);
66
- }
67
-
68
- return nextContent.replace(/export\s+default\s*\{([\s\S]*)\}\s*;?/, (_full, body: string) => {
69
- const trimmedRight = body.replace(/\s*$/, '');
70
- if (!trimmedRight.length) {
71
- return `export default {\n\t${coreAliasEntry}\n};`;
72
- }
73
-
74
- const withComma = trimmedRight.endsWith(',') ? trimmedRight : `${trimmedRight},`;
75
- return `export default {${withComma}\n\t${coreAliasEntry}\n};`;
76
- });
63
+ return nextContent;
77
64
  };
78
65
 
79
66
  export default async function createPresenterFactory(): Promise<void> {
@@ -85,7 +72,8 @@ export default async function createPresenterFactory(): Promise<void> {
85
72
  });
86
73
 
87
74
  const presenterFactoryName = toPascalCase(String(presenterFactoryNameRaw ?? 'AppPresenter')) || 'AppPresenter';
88
- const presenterRootPath = path.join(process.cwd(), 'src', 'core', 'presenter');
75
+ const { sharedContext } = await getSharedState();
76
+ const presenterRootPath = getFoundationConstructPath(sharedContext.name, 'presenter');
89
77
  const filePath = path.join(presenterRootPath, `${presenterFactoryName}.ts`);
90
78
 
91
79
  await fs.mkdir(presenterRootPath, { recursive: true });
@@ -1,19 +1,15 @@
1
1
  import path from 'node:path';
2
2
  import { writeIfNotExists } from '../utils/fileUtils.js';
3
-
4
3
  const createProgramTemplate = () => `import { createApp } from '@azure-net/kit';
5
4
 
6
- export const { register, App } = createApp((app) => app);
5
+ export const { register, Container } = createApp((app) => app);
7
6
  `;
8
-
9
7
  export default async function createProgram() {
10
8
  const programPath = path.join(process.cwd(), 'src', 'program.ts');
11
9
  const created = await writeIfNotExists(programPath, createProgramTemplate());
12
-
13
10
  if (!created) {
14
11
  console.log('⚠️ Program already exists. Nothing was overwritten.');
15
12
  return;
16
13
  }
17
-
18
14
  console.log(`✅ Program created: ${programPath}`);
19
15
  }
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
5
5
  import { toPascalCase } from '../utils/contextUtils.js';
6
-
6
+ import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
7
7
  const createResponseWithWrapperTemplate = (name) => {
8
8
  const interfaceName = `I${name}`;
9
9
  return `import { ResponseBuilder } from '@azure-net/kit/infra';
@@ -19,17 +19,14 @@ export class ${name}<TData = unknown, TMeta = unknown> extends ResponseBuilder<T
19
19
  }
20
20
  `;
21
21
  };
22
-
23
22
  const createResponseSimpleTemplate = (name) => `import { ResponseBuilder } from '@azure-net/kit/infra';
24
23
 
25
24
  export class ${name}<TData = unknown, TMeta = unknown> extends ResponseBuilder<TData, TMeta> {}
26
25
  `;
27
-
28
26
  const ensureResponseName = (rawName) => {
29
27
  const normalized = toPascalCase(rawName || 'ApiResponse') || 'ApiResponse';
30
28
  return normalized.endsWith('Response') ? normalized : `${normalized}Response`;
31
29
  };
32
-
33
30
  export default async function createResponse() {
34
31
  const { responseNameRaw } = await prompts({
35
32
  type: 'text',
@@ -37,28 +34,23 @@ export default async function createResponse() {
37
34
  message: 'Response class name:',
38
35
  initial: 'ApiResponse'
39
36
  });
40
-
41
37
  const responseName = ensureResponseName(String(responseNameRaw ?? 'ApiResponse'));
42
-
43
38
  const { useCommonResponseWrapper } = await prompts({
44
39
  type: 'confirm',
45
40
  name: 'useCommonResponseWrapper',
46
41
  message: 'Use common backend response wrapper interface?',
47
42
  initial: true
48
43
  });
49
-
50
- const responseRoot = path.join(process.cwd(), 'src', 'core', 'response');
44
+ const { sharedContext } = await getSharedState();
45
+ const responseRoot = getFoundationConstructPath(sharedContext.name, 'response');
51
46
  const filePath = path.join(responseRoot, `${responseName}.ts`);
52
47
  await fs.mkdir(responseRoot, { recursive: true });
53
-
54
48
  const template = useCommonResponseWrapper ? createResponseWithWrapperTemplate(responseName) : createResponseSimpleTemplate(responseName);
55
49
  const created = await writeIfNotExists(filePath, template);
56
50
  await updateIndexTs(responseRoot);
57
-
58
51
  if (!created) {
59
52
  console.log(`⚠️ Response "${responseName}" already exists. File was not overwritten.`);
60
53
  return;
61
54
  }
62
-
63
55
  console.log(`✅ Response created: ${filePath}`);
64
56
  }
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
5
5
  import { toPascalCase } from '../utils/contextUtils.js';
6
+ import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
6
7
 
7
8
  const createResponseWithWrapperTemplate = (name: string): string => {
8
9
  const interfaceName = `I${name}`;
@@ -47,7 +48,8 @@ export default async function createResponse(): Promise<void> {
47
48
  initial: true
48
49
  });
49
50
 
50
- const responseRoot = path.join(process.cwd(), 'src', 'core', 'response');
51
+ const { sharedContext } = await getSharedState();
52
+ const responseRoot = getFoundationConstructPath(sharedContext.name, 'response');
51
53
  const filePath = path.join(responseRoot, `${responseName}.ts`);
52
54
  await fs.mkdir(responseRoot, { recursive: true });
53
55
 
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
5
5
  import { toPascalCase, toCamelCase } from '../utils/contextUtils.js';
6
-
6
+ import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
7
7
  const createSchemaRuleTemplate = (ruleName) => `import type { ValidationParams, ValidationRuleResult } from '@azure-net/kit/delivery';
8
8
 
9
9
  export const ${ruleName} = <T = unknown, D = unknown>(): ValidationRuleResult<T, D> => {
@@ -12,7 +12,6 @@ export const ${ruleName} = <T = unknown, D = unknown>(): ValidationRuleResult<T,
12
12
  \t};
13
13
  };
14
14
  `;
15
-
16
15
  export default async function createSchemaRule() {
17
16
  const { ruleNameRaw } = await prompts({
18
17
  type: 'text',
@@ -20,21 +19,17 @@ export default async function createSchemaRule() {
20
19
  message: 'Schema rule name:',
21
20
  initial: 'File'
22
21
  });
23
-
24
22
  const pascalName = toPascalCase(String(ruleNameRaw ?? 'File')) || 'File';
25
23
  const camelName = toCamelCase(pascalName) || 'file';
26
- const rulesPath = path.join(process.cwd(), 'src', 'core', 'schema', 'custom-rules');
24
+ const { sharedContext } = await getSharedState();
25
+ const rulesPath = path.join(getFoundationConstructPath(sharedContext.name, 'schema'), 'custom-rules');
27
26
  const filePath = path.join(rulesPath, `${pascalName}.ts`);
28
-
29
27
  await fs.mkdir(rulesPath, { recursive: true });
30
-
31
28
  const created = await writeIfNotExists(filePath, createSchemaRuleTemplate(camelName));
32
29
  await updateIndexTs(rulesPath);
33
-
34
30
  if (!created) {
35
31
  console.log(`⚠️ Schema rule "${pascalName}" already exists. File was not overwritten.`);
36
32
  return;
37
33
  }
38
-
39
34
  console.log(`✅ Schema rule created: ${filePath}`);
40
35
  }
@@ -3,6 +3,7 @@ import path from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
5
5
  import { toPascalCase, toCamelCase } from '../utils/contextUtils.js';
6
+ import { getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
6
7
 
7
8
  const createSchemaRuleTemplate = (
8
9
  ruleName: string
@@ -25,7 +26,8 @@ export default async function createSchemaRule(): Promise<void> {
25
26
 
26
27
  const pascalName = toPascalCase(String(ruleNameRaw ?? 'File')) || 'File';
27
28
  const camelName = toCamelCase(pascalName) || 'file';
28
- const rulesPath = path.join(process.cwd(), 'src', 'core', 'schema', 'custom-rules');
29
+ const { sharedContext } = await getSharedState();
30
+ const rulesPath = path.join(getFoundationConstructPath(sharedContext.name, 'schema'), 'custom-rules');
29
31
  const filePath = path.join(rulesPath, `${pascalName}.ts`);
30
32
 
31
33
  await fs.mkdir(rulesPath, { recursive: true });
@@ -2,17 +2,15 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { writeIfNotExists } from '../utils/fileUtils.js';
5
-
5
+ import { getLocalizationPath, getSharedState } from '../utils/sharedFoundation.js';
6
6
  const parseLocales = (rawLocales) => {
7
7
  const parsed = String(rawLocales ?? '')
8
8
  .split(',')
9
9
  .map((item) => item.trim().toLowerCase())
10
10
  .filter(Boolean);
11
-
12
11
  const unique = Array.from(new Set(parsed));
13
12
  return unique.length ? unique : ['en', 'ru'];
14
13
  };
15
-
16
14
  const createTranslationIndexTemplate = (defaultLocale) => `import { messages } from './locales/index.js';
17
15
  import { createTranslations, type Path } from '@azure-net/kit/i18n';
18
16
 
@@ -30,17 +28,14 @@ export type MessageKeys = Path<MessagesList>;
30
28
  export type TranslationParam = MessageKeys | string | TranslationExtended;
31
29
  export type AvailableLocales = keyof typeof messages;
32
30
  `;
33
-
34
31
  const createLocalesIndexTemplate = (locales) => {
35
32
  const lines = locales.map((locale) => `\t${locale}: () => import('./${locale}/index.js').then((res) => res.default)`);
36
33
  return `export const messages = {\n${lines.join(',\n')}\n};\n`;
37
34
  };
38
-
39
35
  const createLocaleTemplate = () => `export default {
40
36
  \ttest: 'test'
41
37
  };
42
38
  `;
43
-
44
39
  export default async function createTranslationManager() {
45
40
  const { localesRaw } = await prompts({
46
41
  type: 'text',
@@ -48,9 +43,7 @@ export default async function createTranslationManager() {
48
43
  message: 'Enter locales (comma separated):',
49
44
  initial: 'en, ru'
50
45
  });
51
-
52
46
  const locales = parseLocales(String(localesRaw ?? 'en, ru'));
53
-
54
47
  const { defaultLocale } = await prompts({
55
48
  type: 'select',
56
49
  name: 'defaultLocale',
@@ -58,21 +51,17 @@ export default async function createTranslationManager() {
58
51
  choices: locales.map((locale) => ({ title: locale, value: locale })),
59
52
  initial: 0
60
53
  });
61
-
62
54
  const selectedDefaultLocale = String(defaultLocale ?? locales[0] ?? 'en');
63
-
64
- const translationRoot = path.join(process.cwd(), 'src', 'core', 'translation');
55
+ const { sharedContext } = await getSharedState();
56
+ const translationRoot = getLocalizationPath(sharedContext.name);
65
57
  const localesRoot = path.join(translationRoot, 'locales');
66
58
  await fs.mkdir(localesRoot, { recursive: true });
67
-
68
59
  await writeIfNotExists(path.join(translationRoot, 'index.ts'), createTranslationIndexTemplate(selectedDefaultLocale));
69
60
  await writeIfNotExists(path.join(localesRoot, 'index.ts'), createLocalesIndexTemplate(locales));
70
-
71
61
  for (const locale of locales) {
72
62
  const localeDir = path.join(localesRoot, locale);
73
63
  await fs.mkdir(localeDir, { recursive: true });
74
64
  await writeIfNotExists(path.join(localeDir, 'index.ts'), createLocaleTemplate());
75
65
  }
76
-
77
66
  console.log(`✅ Translation manager created: ${translationRoot}`);
78
67
  }
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import prompts from 'prompts';
4
4
  import { writeIfNotExists } from '../utils/fileUtils.js';
5
+ import { getLocalizationPath, getSharedState } from '../utils/sharedFoundation.js';
5
6
 
6
7
  const parseLocales = (rawLocales: string): string[] => {
7
8
  const parsed = String(rawLocales ?? '')
@@ -61,7 +62,8 @@ export default async function createTranslationManager(): Promise<void> {
61
62
 
62
63
  const selectedDefaultLocale = String(defaultLocale ?? locales[0] ?? 'en');
63
64
 
64
- const translationRoot = path.join(process.cwd(), 'src', 'core', 'translation');
65
+ const { sharedContext } = await getSharedState();
66
+ const translationRoot = getLocalizationPath(sharedContext.name);
65
67
  const localesRoot = path.join(translationRoot, 'locales');
66
68
  await fs.mkdir(localesRoot, { recursive: true });
67
69
 
@@ -2,13 +2,9 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import prompts from 'prompts';
4
4
  import { loadUserConfig } from './loadConfig.js';
5
-
6
5
  const APP_PATH = path.join(process.cwd(), 'src/app');
7
- const CORE_PATH = path.join(process.cwd(), 'src/core');
8
-
9
6
  function normalizeContextNames(rawContexts) {
10
7
  if (!Array.isArray(rawContexts)) return [];
11
-
12
8
  return rawContexts
13
9
  .map((item) => {
14
10
  if (typeof item === 'string') {
@@ -19,25 +15,20 @@ function normalizeContextNames(rawContexts) {
19
15
  })
20
16
  .filter(Boolean);
21
17
  }
22
-
23
18
  export async function selectContext(message = 'Select context:') {
24
19
  const config = await loadUserConfig();
25
20
  const contexts = normalizeContextNames(config.contexts);
26
-
27
21
  const { context } = await prompts({
28
22
  type: 'select',
29
23
  name: 'context',
30
24
  message,
31
- choices: [...contexts.map((c) => ({ title: c, value: c })), { title: 'core', value: 'core' }]
25
+ choices: contexts.map((c) => ({ title: c, value: c }))
32
26
  });
33
-
34
- return context;
27
+ return context ? String(context) : null;
35
28
  }
36
-
37
29
  export function getContextPath(context) {
38
- return context === 'core' ? CORE_PATH : path.join(APP_PATH, context, 'layers');
30
+ return path.join(APP_PATH, context, 'layers');
39
31
  }
40
-
41
32
  export async function getAvailableFiles(dir, pattern = '.ts') {
42
33
  try {
43
34
  const files = await fs.readdir(dir);
@@ -46,40 +37,34 @@ export async function getAvailableFiles(dir, pattern = '.ts') {
46
37
  return [];
47
38
  }
48
39
  }
49
-
50
40
  export async function getApplicationProviderServices(context) {
51
41
  const contextPath = getContextPath(context);
52
42
  const providerPath = path.join(contextPath, 'application/providers/ApplicationProvider.ts');
53
-
54
43
  try {
55
44
  const content = await fs.readFile(providerPath, 'utf-8');
56
45
  // Extract service names from ApplicationProvider
57
46
  const serviceRegex = /(\w+Service):\s*\(/g;
58
47
  const services = [];
59
48
  let match;
60
-
61
49
  while ((match = serviceRegex.exec(content)) !== null) {
62
- services.push(match[1]);
50
+ const serviceName = match[1];
51
+ if (serviceName) services.push(serviceName);
63
52
  }
64
-
65
53
  return services;
66
54
  } catch {
67
55
  return [];
68
56
  }
69
57
  }
70
-
71
58
  export function toPascalCase(str) {
72
59
  return str
73
60
  .split(/[-_\s]+/)
74
61
  .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
75
62
  .join('');
76
63
  }
77
-
78
64
  export function toCamelCase(str) {
79
65
  const pascal = toPascalCase(str);
80
66
  return pascal.charAt(0).toLowerCase() + pascal.slice(1);
81
67
  }
82
-
83
68
  export function toKebabCase(str) {
84
69
  return str
85
70
  .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
@@ -4,7 +4,6 @@ import prompts from 'prompts';
4
4
  import { loadUserConfig } from './loadConfig.js';
5
5
 
6
6
  const APP_PATH = path.join(process.cwd(), 'src/app');
7
- const CORE_PATH = path.join(process.cwd(), 'src/core');
8
7
 
9
8
  function normalizeContextNames(rawContexts: unknown): string[] {
10
9
  if (!Array.isArray(rawContexts)) return [];
@@ -28,14 +27,14 @@ export async function selectContext(message = 'Select context:'): Promise<string
28
27
  type: 'select',
29
28
  name: 'context',
30
29
  message,
31
- choices: [...contexts.map((c) => ({ title: c, value: c })), { title: 'core', value: 'core' }]
30
+ choices: contexts.map((c) => ({ title: c, value: c }))
32
31
  });
33
32
 
34
33
  return context ? String(context) : null;
35
34
  }
36
35
 
37
36
  export function getContextPath(context: string): string {
38
- return context === 'core' ? CORE_PATH : path.join(APP_PATH, context, 'layers');
37
+ return path.join(APP_PATH, context, 'layers');
39
38
  }
40
39
 
41
40
  export async function getAvailableFiles(dir: string, pattern = '.ts'): Promise<string[]> {