@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
@@ -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 MessagePreset = 'ru' | 'en' | 'i18n';
9
10
  type ConfigPath = 'ts' | 'js';
@@ -48,10 +49,9 @@ const buildDefaultSchemaFactoryConfigEntry = (factoryName: string): string => {
48
49
  const upsertDefaultSchemaFactoryConfig = (content: string, factoryName: string): string => {
49
50
  const hasExportDefaultObject = /export\s+default\s*\{[\s\S]*\}\s*;?/.test(content);
50
51
  const defaultFactoryEntry = buildDefaultSchemaFactoryConfigEntry(factoryName);
51
- const coreAliasEntry = `coreAlias: '$core'`;
52
52
 
53
53
  if (!hasExportDefaultObject) {
54
- return `export default {\n\t${coreAliasEntry},\n\t${defaultFactoryEntry}\n};\n`;
54
+ return `export default {\n\t${defaultFactoryEntry}\n};\n`;
55
55
  }
56
56
 
57
57
  let nextContent = content;
@@ -71,20 +71,7 @@ const upsertDefaultSchemaFactoryConfig = (content: string, factoryName: string):
71
71
  return `export default {${withComma}\n\t${defaultFactoryEntry}\n};`;
72
72
  });
73
73
  }
74
-
75
- if (/coreAlias\s*:/.test(nextContent)) {
76
- return nextContent.replace(/coreAlias\s*:\s*['"`][^'"`]+['"`]/, coreAliasEntry);
77
- }
78
-
79
- return nextContent.replace(/export\s+default\s*\{([\s\S]*)\}\s*;?/, (_full, body: string) => {
80
- const trimmedRight = body.replace(/\s*$/, '');
81
- if (!trimmedRight.length) {
82
- return `export default {\n\t${coreAliasEntry}\n};`;
83
- }
84
-
85
- const withComma = trimmedRight.endsWith(',') ? trimmedRight : `${trimmedRight},`;
86
- return `export default {${withComma}\n\t${coreAliasEntry}\n};`;
87
- });
74
+ return nextContent;
88
75
  };
89
76
 
90
77
  export default async function createCoreSchema(): Promise<void> {
@@ -111,7 +98,8 @@ export default async function createCoreSchema(): Promise<void> {
111
98
 
112
99
  const selectedPreset = (preset ?? 'i18n') as MessagePreset;
113
100
  const messagesImportName = resolvePresetImportName(selectedPreset);
114
- const coreSchemaPath = path.join(process.cwd(), 'src', 'core', 'schema');
101
+ const { sharedContext } = await getSharedState();
102
+ const coreSchemaPath = getFoundationConstructPath(sharedContext.name, 'schema');
115
103
  const customRulesPath = path.join(coreSchemaPath, 'custom-rules');
116
104
  const filePath = path.join(coreSchemaPath, `${schemaFactoryName}.ts`);
117
105
 
@@ -123,7 +111,7 @@ export default async function createCoreSchema(): Promise<void> {
123
111
  console.log(`⚠️ Schema factory "${schemaFactoryName}" already exists. File was not overwritten.`);
124
112
  } else {
125
113
  await updateIndexTs(coreSchemaPath);
126
- console.log(`✅ Core schema factory created: ${filePath}`);
114
+ console.log(`✅ Shared schema factory created: ${filePath}`);
127
115
  }
128
116
 
129
117
  const { useAsDefault } = await prompts({
@@ -3,9 +3,9 @@ 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
-
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
9
9
  const createDatasourceTemplateWithoutResponse = (
10
10
  className
11
11
  ) => `import { BaseHttpDatasource, type CreateRequestCallbackType } from '@azure-net/kit/infra';
@@ -16,13 +16,12 @@ export class ${className} extends BaseHttpDatasource {
16
16
  \t}
17
17
  }
18
18
  `;
19
-
20
19
  const createDatasourceTemplateWithResponseAndInterface = (
21
20
  className,
22
21
  responseName,
23
- coreAlias
22
+ responseImportPath
24
23
  ) => `import { BaseHttpDatasource, type CreateRequestCallbackType } from '@azure-net/kit/infra';
25
- import { ${responseName}, type I${responseName} } from '${coreAlias}/response';
24
+ import { ${responseName}, type I${responseName} } from '${responseImportPath}';
26
25
 
27
26
  export class ${className} extends BaseHttpDatasource {
28
27
  \tasync createRequest<T>(callback: CreateRequestCallbackType<I${responseName}<T>>) {
@@ -30,13 +29,12 @@ export class ${className} extends BaseHttpDatasource {
30
29
  \t}
31
30
  }
32
31
  `;
33
-
34
32
  const createDatasourceTemplateWithResponse = (
35
33
  className,
36
34
  responseName,
37
- coreAlias
35
+ responseImportPath
38
36
  ) => `import { BaseHttpDatasource, type CreateRequestCallbackType } from '@azure-net/kit/infra';
39
- import { ${responseName} } from '${coreAlias}/response';
37
+ import { ${responseName} } from '${responseImportPath}';
40
38
 
41
39
  export class ${className} extends BaseHttpDatasource {
42
40
  \tasync createRequest<T>(callback: CreateRequestCallbackType<T>) {
@@ -44,26 +42,22 @@ export class ${className} extends BaseHttpDatasource {
44
42
  \t}
45
43
  }
46
44
  `;
47
-
48
45
  const ensureDatasourceClassName = (rawName) => {
49
46
  const normalized = toPascalCase(rawName || 'MockApi');
50
47
  return normalized.endsWith('Datasource') ? normalized : `${normalized}Datasource`;
51
48
  };
52
-
53
49
  const getContextChoices = async () => {
54
50
  const config = await loadUserConfig();
55
51
  const contexts = normalizeContexts(config.contexts);
56
-
57
- const choices = [{ title: 'core', value: 'core' }];
52
+ const choices = [{ title: 'shared foundation', value: '__shared__' }];
58
53
  for (const context of contexts) {
59
54
  choices.push({ title: context.name, value: context.name });
60
55
  }
61
-
62
56
  return choices;
63
57
  };
64
-
65
- const getCoreResponses = async () => {
66
- const responsesPath = path.join(process.cwd(), 'src', 'core', 'response');
58
+ const getSharedResponses = async () => {
59
+ const { sharedContext } = await getSharedState();
60
+ const responsesPath = getFoundationConstructPath(sharedContext.name, 'response');
67
61
  try {
68
62
  const files = await fs.readdir(responsesPath);
69
63
  return files.filter((file) => file.endsWith('.ts') && file !== 'index.ts').map((file) => file.replace(/\.ts$/, ''));
@@ -71,9 +65,9 @@ const getCoreResponses = async () => {
71
65
  return [];
72
66
  }
73
67
  };
74
-
75
68
  const hasResponseInterface = async (responseName) => {
76
- const responsePath = path.join(process.cwd(), 'src', 'core', 'response', `${responseName}.ts`);
69
+ const { sharedContext } = await getSharedState();
70
+ const responsePath = path.join(getFoundationConstructPath(sharedContext.name, 'response'), `${responseName}.ts`);
77
71
  try {
78
72
  const content = await fs.readFile(responsePath, 'utf-8');
79
73
  const interfaceRegex = new RegExp(`export\\s+interface\\s+I${responseName}\\b`);
@@ -82,36 +76,12 @@ const hasResponseInterface = async (responseName) => {
82
76
  return false;
83
77
  }
84
78
  };
85
-
86
- const getConfiguredCoreAliasOrThrow = async () => {
87
- const configRef = resolveConfigFile();
88
- if (!configRef.exists) {
89
- throw new Error(
90
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
91
- );
92
- }
93
-
94
- const content = await fs.readFile(configRef.filepath, 'utf-8');
95
- const aliasMatch = content.match(/coreAlias\s*:\s*['"`]([^'"`]+)['"`]/);
96
- const alias = aliasMatch?.[1]?.trim();
97
-
98
- if (!alias) {
99
- throw new Error(
100
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
101
- );
102
- }
103
-
104
- return alias.startsWith('$') ? alias : `$${alias}`;
105
- };
106
-
107
79
  const resolveTargetPath = (target) => {
108
- if (target === 'core') {
109
- return path.join(process.cwd(), 'src', 'core', 'datasource');
80
+ if (target === '__shared__') {
81
+ throw new Error('Shared target path must be resolved async.');
110
82
  }
111
-
112
83
  return path.join(process.cwd(), 'src', 'app', target, 'layers', 'infrastructure', 'http', 'datasources');
113
84
  };
114
-
115
85
  export default async function createDatasource() {
116
86
  const { datasourceNameRaw } = await prompts({
117
87
  type: 'text',
@@ -119,9 +89,7 @@ export default async function createDatasource() {
119
89
  message: 'Datasource name:',
120
90
  initial: 'MockApi'
121
91
  });
122
-
123
92
  const className = ensureDatasourceClassName(String(datasourceNameRaw ?? 'MockApi'));
124
-
125
93
  const contextChoices = await getContextChoices();
126
94
  const { target } = await prompts({
127
95
  type: 'select',
@@ -130,44 +98,41 @@ export default async function createDatasource() {
130
98
  choices: contextChoices,
131
99
  initial: 0
132
100
  });
133
-
134
- const selectedTarget = String(target ?? 'core');
135
- const coreResponses = await getCoreResponses();
136
-
101
+ const selectedTarget = String(target ?? '__shared__');
102
+ const sharedResponses = await getSharedResponses();
137
103
  let selectedResponse = null;
138
- if (coreResponses.length > 0) {
104
+ if (sharedResponses.length > 0) {
139
105
  const { responseName } = await prompts({
140
106
  type: 'select',
141
107
  name: 'responseName',
142
108
  message: 'Select response for datasource:',
143
- choices: [{ title: 'No response', value: null }, ...coreResponses.map((response) => ({ title: response, value: response }))],
109
+ choices: [{ title: 'No response', value: null }, ...sharedResponses.map((response) => ({ title: response, value: response }))],
144
110
  initial: 0
145
111
  });
146
112
  selectedResponse = responseName ? String(responseName) : null;
147
113
  }
148
-
149
114
  let content;
150
115
  if (!selectedResponse) {
151
116
  content = createDatasourceTemplateWithoutResponse(className);
152
117
  } else {
153
- const coreAlias = await getConfiguredCoreAliasOrThrow();
118
+ const { sharedAlias } = await getSharedState();
119
+ const responseImportPath = getFoundationConstructImportPath(sharedAlias, 'response');
154
120
  const withInterface = await hasResponseInterface(selectedResponse);
155
121
  content = withInterface
156
- ? createDatasourceTemplateWithResponseAndInterface(className, selectedResponse, coreAlias)
157
- : createDatasourceTemplateWithResponse(className, selectedResponse, coreAlias);
122
+ ? createDatasourceTemplateWithResponseAndInterface(className, selectedResponse, responseImportPath)
123
+ : createDatasourceTemplateWithResponse(className, selectedResponse, responseImportPath);
158
124
  }
159
-
160
- const targetPath = resolveTargetPath(selectedTarget);
125
+ const targetPath =
126
+ selectedTarget === '__shared__'
127
+ ? getFoundationConstructPath((await getSharedState()).sharedContext.name, 'datasource')
128
+ : resolveTargetPath(selectedTarget);
161
129
  await fs.mkdir(targetPath, { recursive: true });
162
-
163
130
  const filePath = path.join(targetPath, `${className}.ts`);
164
131
  const created = await writeIfNotExists(filePath, content);
165
132
  await updateIndexTs(targetPath);
166
-
167
133
  if (!created) {
168
134
  console.log(`⚠️ Datasource "${className}" already exists. File was not overwritten.`);
169
135
  return;
170
136
  }
171
-
172
137
  console.log(`✅ Datasource created: ${filePath}`);
173
138
  }
@@ -3,8 +3,9 @@ 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 ContextChoice = { title: string; value: string };
10
11
 
@@ -22,9 +23,9 @@ export class ${className} extends BaseHttpDatasource {
22
23
  const createDatasourceTemplateWithResponseAndInterface = (
23
24
  className: string,
24
25
  responseName: string,
25
- coreAlias: string
26
+ responseImportPath: string
26
27
  ): string => `import { BaseHttpDatasource, type CreateRequestCallbackType } from '@azure-net/kit/infra';
27
- import { ${responseName}, type I${responseName} } from '${coreAlias}/response';
28
+ import { ${responseName}, type I${responseName} } from '${responseImportPath}';
28
29
 
29
30
  export class ${className} extends BaseHttpDatasource {
30
31
  \tasync createRequest<T>(callback: CreateRequestCallbackType<I${responseName}<T>>) {
@@ -36,9 +37,9 @@ export class ${className} extends BaseHttpDatasource {
36
37
  const createDatasourceTemplateWithResponse = (
37
38
  className: string,
38
39
  responseName: string,
39
- coreAlias: string
40
+ responseImportPath: string
40
41
  ): string => `import { BaseHttpDatasource, type CreateRequestCallbackType } from '@azure-net/kit/infra';
41
- import { ${responseName} } from '${coreAlias}/response';
42
+ import { ${responseName} } from '${responseImportPath}';
42
43
 
43
44
  export class ${className} extends BaseHttpDatasource {
44
45
  \tasync createRequest<T>(callback: CreateRequestCallbackType<T>) {
@@ -56,7 +57,7 @@ const getContextChoices = async (): Promise<ContextChoice[]> => {
56
57
  const config = await loadUserConfig();
57
58
  const contexts = normalizeContexts(config.contexts);
58
59
 
59
- const choices: ContextChoice[] = [{ title: 'core', value: 'core' }];
60
+ const choices: ContextChoice[] = [{ title: 'shared foundation', value: '__shared__' }];
60
61
  for (const context of contexts) {
61
62
  choices.push({ title: context.name, value: context.name });
62
63
  }
@@ -64,8 +65,9 @@ const getContextChoices = async (): Promise<ContextChoice[]> => {
64
65
  return choices;
65
66
  };
66
67
 
67
- const getCoreResponses = async (): Promise<string[]> => {
68
- const responsesPath = path.join(process.cwd(), 'src', 'core', 'response');
68
+ const getSharedResponses = async (): Promise<string[]> => {
69
+ const { sharedContext } = await getSharedState();
70
+ const responsesPath = getFoundationConstructPath(sharedContext.name, 'response');
69
71
  try {
70
72
  const files = await fs.readdir(responsesPath);
71
73
  return files.filter((file) => file.endsWith('.ts') && file !== 'index.ts').map((file) => file.replace(/\.ts$/, ''));
@@ -75,7 +77,8 @@ const getCoreResponses = async (): Promise<string[]> => {
75
77
  };
76
78
 
77
79
  const hasResponseInterface = async (responseName: string): Promise<boolean> => {
78
- const responsePath = path.join(process.cwd(), 'src', 'core', 'response', `${responseName}.ts`);
80
+ const { sharedContext } = await getSharedState();
81
+ const responsePath = path.join(getFoundationConstructPath(sharedContext.name, 'response'), `${responseName}.ts`);
79
82
  try {
80
83
  const content = await fs.readFile(responsePath, 'utf-8');
81
84
  const interfaceRegex = new RegExp(`export\\s+interface\\s+I${responseName}\\b`);
@@ -85,30 +88,9 @@ const hasResponseInterface = async (responseName: string): Promise<boolean> => {
85
88
  }
86
89
  };
87
90
 
88
- const getConfiguredCoreAliasOrThrow = async (): Promise<string> => {
89
- const configRef = resolveConfigFile();
90
- if (!configRef.exists) {
91
- throw new Error(
92
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
93
- );
94
- }
95
-
96
- const content = await fs.readFile(configRef.filepath, 'utf-8');
97
- const aliasMatch = content.match(/coreAlias\s*:\s*['"`]([^'"`]+)['"`]/);
98
- const alias = aliasMatch?.[1]?.trim();
99
-
100
- if (!alias) {
101
- throw new Error(
102
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
103
- );
104
- }
105
-
106
- return alias.startsWith('$') ? alias : `$${alias}`;
107
- };
108
-
109
91
  const resolveTargetPath = (target: string): string => {
110
- if (target === 'core') {
111
- return path.join(process.cwd(), 'src', 'core', 'datasource');
92
+ if (target === '__shared__') {
93
+ throw new Error('Shared target path must be resolved async.');
112
94
  }
113
95
 
114
96
  return path.join(process.cwd(), 'src', 'app', target, 'layers', 'infrastructure', 'http', 'datasources');
@@ -133,16 +115,16 @@ export default async function createDatasource(): Promise<void> {
133
115
  initial: 0
134
116
  });
135
117
 
136
- const selectedTarget = String(target ?? 'core');
137
- const coreResponses = await getCoreResponses();
118
+ const selectedTarget = String(target ?? '__shared__');
119
+ const sharedResponses = await getSharedResponses();
138
120
 
139
121
  let selectedResponse: string | null = null;
140
- if (coreResponses.length > 0) {
122
+ if (sharedResponses.length > 0) {
141
123
  const { responseName } = await prompts({
142
124
  type: 'select',
143
125
  name: 'responseName',
144
126
  message: 'Select response for datasource:',
145
- choices: [{ title: 'No response', value: null }, ...coreResponses.map((response) => ({ title: response, value: response }))],
127
+ choices: [{ title: 'No response', value: null }, ...sharedResponses.map((response) => ({ title: response, value: response }))],
146
128
  initial: 0
147
129
  });
148
130
  selectedResponse = responseName ? String(responseName) : null;
@@ -152,14 +134,18 @@ export default async function createDatasource(): Promise<void> {
152
134
  if (!selectedResponse) {
153
135
  content = createDatasourceTemplateWithoutResponse(className);
154
136
  } else {
155
- const coreAlias = await getConfiguredCoreAliasOrThrow();
137
+ const { sharedAlias } = await getSharedState();
138
+ const responseImportPath = getFoundationConstructImportPath(sharedAlias, 'response');
156
139
  const withInterface = await hasResponseInterface(selectedResponse);
157
140
  content = withInterface
158
- ? createDatasourceTemplateWithResponseAndInterface(className, selectedResponse, coreAlias)
159
- : createDatasourceTemplateWithResponse(className, selectedResponse, coreAlias);
141
+ ? createDatasourceTemplateWithResponseAndInterface(className, selectedResponse, responseImportPath)
142
+ : createDatasourceTemplateWithResponse(className, selectedResponse, responseImportPath);
160
143
  }
161
144
 
162
- const targetPath = resolveTargetPath(selectedTarget);
145
+ const targetPath =
146
+ selectedTarget === '__shared__'
147
+ ? getFoundationConstructPath((await getSharedState()).sharedContext.name, 'datasource')
148
+ : resolveTargetPath(selectedTarget);
163
149
  await fs.mkdir(targetPath, { recursive: true });
164
150
 
165
151
  const filePath = path.join(targetPath, `${className}.ts`);
@@ -3,20 +3,18 @@ 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
-
8
+ import { getFoundationConstructImportPath, getFoundationConstructPath, getSharedState } from '../utils/sharedFoundation.js';
9
9
  const createProviderTemplateWithoutDatasource = (providerName, contextPrefix) => `import { createBoundaryProvider } from '@azure-net/kit';
10
10
 
11
11
  export const ${providerName} = createBoundaryProvider('${contextPrefix}${providerName}', {
12
12
  \tregister: () => ({})
13
13
  });
14
14
  `;
15
-
16
15
  const createProviderTemplateWithDatasource = (providerName, contextPrefix, datasourceName, datasourceImportPath) => {
17
16
  const factoryNameBase = datasourceName.endsWith('Datasource') ? datasourceName.slice(0, -'Datasource'.length) : datasourceName;
18
17
  const datasourceFactoryName = `${factoryNameBase || datasourceName}Rest`;
19
-
20
18
  return `import { ${datasourceName} } from '${datasourceImportPath}';
21
19
  import { createHttpServiceInstance } from '@azure-net/kit/infra';
22
20
  import { createBoundaryProvider } from '@azure-net/kit';
@@ -33,27 +31,6 @@ export const ${providerName} = createBoundaryProvider('${contextPrefix}${provide
33
31
  });
34
32
  `;
35
33
  };
36
-
37
- const getCoreAliasOrThrow = async () => {
38
- const configRef = resolveConfigFile();
39
- if (!configRef.exists) {
40
- throw new Error(
41
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
42
- );
43
- }
44
-
45
- const content = await fs.readFile(configRef.filepath, 'utf-8');
46
- const aliasMatch = content.match(/coreAlias\s*:\s*['"`]([^'"`]+)['"`]/);
47
- const alias = aliasMatch?.[1]?.trim();
48
- if (!alias) {
49
- throw new Error(
50
- 'azure-net.config is not configured. Run "azure-net init folders-structure" and do not forget to fill contexts if you need them.'
51
- );
52
- }
53
-
54
- return alias.startsWith('$') ? alias : `$${alias}`;
55
- };
56
-
57
34
  const getDatasourcesFromPath = async (targetPath) => {
58
35
  try {
59
36
  const files = await fs.readdir(targetPath);
@@ -62,31 +39,24 @@ const getDatasourcesFromPath = async (targetPath) => {
62
39
  return [];
63
40
  }
64
41
  };
65
-
66
- const resolveContextPrefix = (target) => (target === 'core' ? 'Core' : toPascalCase(target));
67
-
42
+ const resolveContextPrefix = (target) => (target === '__shared__' ? 'Shared' : toPascalCase(target));
68
43
  const resolveProviderPath = (target) => {
69
- if (target === 'core') {
70
- return path.join(process.cwd(), 'src', 'core', 'provider');
44
+ if (target === '__shared__') {
45
+ throw new Error('Shared provider path must be resolved async.');
71
46
  }
72
-
73
47
  return path.join(process.cwd(), 'src', 'app', target, 'layers', 'infrastructure', 'providers');
74
48
  };
75
-
76
49
  const resolveContextAlias = (contexts, targetContext) => {
77
50
  const found = contexts.find((context) => context.name === targetContext);
78
51
  if (!found) {
79
52
  return `$${targetContext}`;
80
53
  }
81
-
82
54
  return found.alias.startsWith('$') ? found.alias : `$${found.alias}`;
83
55
  };
84
-
85
56
  const ensureProviderName = (rawName) => {
86
57
  const normalized = toPascalCase(rawName || 'DatasourceProvider') || 'DatasourceProvider';
87
58
  return normalized.endsWith('Provider') ? normalized : `${normalized}Provider`;
88
59
  };
89
-
90
60
  export default async function createDatasourceProvider() {
91
61
  const { providerNameRaw } = await prompts({
92
62
  type: 'text',
@@ -94,12 +64,13 @@ export default async function createDatasourceProvider() {
94
64
  message: 'Datasource provider name:',
95
65
  initial: 'DatasourceProvider'
96
66
  });
97
-
98
67
  const providerName = ensureProviderName(String(providerNameRaw ?? 'DatasourceProvider'));
99
68
  const config = await loadUserConfig();
100
69
  const contexts = normalizeContexts(config.contexts);
101
- const contextChoices = [{ title: 'core', value: 'core' }, ...contexts.map((context) => ({ title: context.name, value: context.name }))];
102
-
70
+ const contextChoices = [
71
+ { title: 'shared foundation', value: '__shared__' },
72
+ ...contexts.map((context) => ({ title: context.name, value: context.name }))
73
+ ];
103
74
  const { target } = await prompts({
104
75
  type: 'select',
105
76
  name: 'target',
@@ -107,24 +78,22 @@ export default async function createDatasourceProvider() {
107
78
  choices: contextChoices,
108
79
  initial: 0
109
80
  });
110
-
111
- const selectedTarget = String(target ?? 'core');
112
- const coreDatasources = await getDatasourcesFromPath(path.join(process.cwd(), 'src', 'core', 'datasource'));
81
+ const selectedTarget = String(target ?? '__shared__');
82
+ const { sharedAlias, sharedContext } = await getSharedState();
83
+ const sharedDatasources = await getDatasourcesFromPath(getFoundationConstructPath(sharedContext.name, 'datasource'));
113
84
  const contextDatasources =
114
- selectedTarget === 'core'
85
+ selectedTarget === '__shared__'
115
86
  ? []
116
87
  : await getDatasourcesFromPath(
117
88
  path.join(process.cwd(), 'src', 'app', selectedTarget, 'layers', 'infrastructure', 'http', 'datasources')
118
89
  );
119
-
120
90
  const datasourceChoices = [{ title: 'No datasource', value: null }];
121
- for (const datasource of coreDatasources) {
122
- datasourceChoices.push({ title: `${datasource} (core)`, value: { name: datasource, source: 'core' } });
91
+ for (const datasource of sharedDatasources) {
92
+ datasourceChoices.push({ title: `${datasource} (shared foundation)`, value: { name: datasource, source: 'shared' } });
123
93
  }
124
94
  for (const datasource of contextDatasources) {
125
95
  datasourceChoices.push({ title: `${datasource} (${selectedTarget})`, value: { name: datasource, source: 'context' } });
126
96
  }
127
-
128
97
  const { datasourceSelection } = await prompts({
129
98
  type: 'select',
130
99
  name: 'datasourceSelection',
@@ -132,33 +101,27 @@ export default async function createDatasourceProvider() {
132
101
  choices: datasourceChoices,
133
102
  initial: 0
134
103
  });
135
-
136
104
  const selectedDatasource = datasourceSelection ?? null;
137
105
  const contextPrefix = resolveContextPrefix(selectedTarget);
138
106
  let content;
139
-
140
107
  if (!selectedDatasource) {
141
108
  content = createProviderTemplateWithoutDatasource(providerName, contextPrefix);
142
109
  } else {
143
110
  const importPath =
144
- selectedDatasource.source === 'core'
145
- ? `${await getCoreAliasOrThrow()}/datasource`
111
+ selectedDatasource.source === 'shared'
112
+ ? getFoundationConstructImportPath(sharedAlias, 'datasource')
146
113
  : `${resolveContextAlias(contexts, selectedTarget)}/layers/infrastructure/http/datasources`;
147
-
148
114
  content = createProviderTemplateWithDatasource(providerName, contextPrefix, selectedDatasource.name, importPath);
149
115
  }
150
-
151
- const providerPath = resolveProviderPath(selectedTarget);
116
+ const providerPath =
117
+ selectedTarget === '__shared__' ? getFoundationConstructPath(sharedContext.name, 'provider') : resolveProviderPath(selectedTarget);
152
118
  await fs.mkdir(providerPath, { recursive: true });
153
-
154
119
  const filePath = path.join(providerPath, `${providerName}.ts`);
155
120
  const created = await writeIfNotExists(filePath, content);
156
121
  await updateIndexTs(providerPath);
157
-
158
122
  if (!created) {
159
123
  console.log(`⚠️ Datasource provider "${providerName}" already exists. File was not overwritten.`);
160
124
  return;
161
125
  }
162
-
163
126
  console.log(`✅ Datasource provider created: ${filePath}`);
164
127
  }