@azure-net/cli 2.0.0 → 2.0.2

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.
package/bin/azure-net.js CHANGED
@@ -7,7 +7,8 @@ import {
7
7
  initMiddleware,
8
8
  initEdges,
9
9
  initTranslations,
10
- initSchema
10
+ initSchema,
11
+ initLint
11
12
  } from '../src/commands/init/index.js';
12
13
  import {
13
14
  generateDatasource,
@@ -28,6 +29,11 @@ program
28
29
  .version('2.0.0');
29
30
 
30
31
  // Init commands
32
+ program
33
+ .command('init:lint')
34
+ .description('Setup linting and commit tools')
35
+ .action(initLint)
36
+
31
37
  program
32
38
  .command('init:structure')
33
39
  .description('Generate base folder structure')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-net/cli",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "azure-net": "bin/azure-net.js"
@@ -2,74 +2,85 @@ import prompts from 'prompts';
2
2
  import fs from 'fs/promises';
3
3
  import path from 'path';
4
4
  import { selectContext, getContextPath, toPascalCase, toCamelCase, getAvailableFiles } from '../../utils/contextUtils.js';
5
- import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
5
+ import { writeIfNotExists, updateIndexTs, ensureIndexExports } from '../../utils/fileUtils.js';
6
6
  import { ensureProvider, addToProvider } from '../../utils/providerUtils.js';
7
7
 
8
8
  // Templates for CRUD generation
9
9
  const entityTemplate = `export interface I{{name}} {
10
10
  \tid: number;
11
11
  \t// Add entity fields here
12
- \t[key: string]: unknown;
13
- }
12
+ \tcreated_at: string;
13
+ \tupdated_at: string;
14
+ }`;
14
15
 
15
- export type I{{name}}Collection = I{{name}}[];`;
16
+ const portsIndexTemplate = `import type { I{{name}} } from '../../Entities/{{name}}';
16
17
 
17
- const requestTemplate = `import type { I{{name}} } from '../../Entities/{{name}}/index.js';
18
+ export interface I{{name}}Collection {
19
+ \tdata: I{{name}}[];
20
+ \ttotal: number;
21
+ }
18
22
 
19
- export type I{{name}}CreateRequest = Omit<I{{name}}, 'id'>;
20
- export type I{{name}}UpdateRequest = Partial<I{{name}}CreateRequest>;
21
23
  export interface I{{name}}CollectionQuery {
24
+ \tpage?: number;
25
+ \tperPage?: number;
22
26
  \t[key: string]: unknown;
27
+ }
28
+
29
+ export interface I{{name}}CreateRequest {
30
+ \t// Add request fields here
31
+ }
32
+
33
+ export interface I{{name}}UpdateRequest {
34
+ \t// Add request fields here
23
35
  }`;
24
36
 
25
37
  const repositoryTemplate = `import { {{datasource}} } from '{{datasourceImport}}';
26
- import type {
38
+ import type { I{{name}} } from '\${{context}}/Domain/Entities/{{name}}';
39
+ import type {
27
40
  \tI{{name}}Collection,
28
- \tI{{name}},
41
+ \tI{{name}}CollectionQuery,
29
42
  \tI{{name}}CreateRequest,
30
- \tI{{name}}UpdateRequest,
31
- \tI{{name}}CollectionQuery
32
- } from '../../Domain/Entities/{{name}}/index.js';
33
- import type { I{{name}}CreateRequest as CreateRequest, I{{name}}UpdateRequest as UpdateRequest } from '../../Domain/Ports/{{name}}/index.js';
43
+ \tI{{name}}UpdateRequest
44
+ } from '\${{context}}/Domain/Ports/{{name}}';
34
45
 
35
46
  export class {{name}}Repository {
36
47
  \tprivate endpoint = '{{endpoint}}';
37
48
  \t
38
49
  \tconstructor(private {{datasourceVar}}: {{datasource}}) {}
39
50
  \t
40
- \tpublic async collection(queryParams?: I{{name}}CollectionQuery) {
41
- \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}Collection>(({ http, query }) =>
42
- \t\t\thttp.get(\`\${this.endpoint}\${query.build(queryParams)}\`)
43
- \t\t);
51
+ \tpublic async collection(query?: I{{name}}CollectionQuery) {
52
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}Collection>(({ http, query: q }) =>
53
+ \t\t\thttp.get(\`\${this.endpoint}\${q.build(query || {})}\`)
54
+ \t\t).then(res => res.getData());
44
55
  \t}
45
56
  \t
46
57
  \tpublic async resource(id: number) {
47
- \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
58
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
48
59
  \t\t\thttp.get(\`\${this.endpoint}/\${id}\`)
49
- \t\t);
60
+ \t\t).then(res => res.getData());
50
61
  \t}
51
62
  \t
52
- \tpublic async create(data: CreateRequest) {
53
- \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
63
+ \tpublic async create(data: I{{name}}CreateRequest) {
64
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
54
65
  \t\t\thttp.post(this.endpoint, { json: data })
55
- \t\t);
66
+ \t\t).then(res => res.getData());
56
67
  \t}
57
68
  \t
58
- \tpublic async update(id: number, data: UpdateRequest) {
59
- \t\treturn await this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
69
+ \tpublic async update(id: number, data: I{{name}}UpdateRequest) {
70
+ \t\treturn this.{{datasourceVar}}.createRequest<I{{name}}>(({ http }) =>
60
71
  \t\t\thttp.put(\`\${this.endpoint}/\${id}\`, { json: data })
61
- \t\t);
72
+ \t\t).then(res => res.getData());
62
73
  \t}
63
74
  \t
64
75
  \tpublic async remove(id: number) {
65
- \t\treturn await this.{{datasourceVar}}.createRequest<never>(({ http }) =>
76
+ \t\treturn this.{{datasourceVar}}.createRequest<void>(({ http }) =>
66
77
  \t\t\thttp.delete(\`\${this.endpoint}/\${id}\`)
67
- \t\t);
78
+ \t\t).then(() => undefined);
68
79
  \t}
69
80
  }`;
70
81
 
71
82
  const serviceTemplate = `import { ClassMirror } from '@azure-net/kit';
72
- import { {{name}}Repository } from '../../Infrastructure/Repositories/index.js';
83
+ import { {{name}}Repository } from '../../Infrastructure/Http/Repositories';
73
84
 
74
85
  export class {{name}}Service extends ClassMirror<{{name}}Repository> {
75
86
  \tconstructor(private {{camelName}}Repository: {{name}}Repository) {
@@ -84,7 +95,7 @@ export class {{name}}Service extends ClassMirror<{{name}}Repository> {
84
95
  }`;
85
96
 
86
97
  const createSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
87
- import type { I{{name}}CreateRequest } from '../../Domain/Ports/{{name}}/index.js';
98
+ import type { I{{name}}CreateRequest } from '\${{context}}/Domain/Ports/{{name}}';
88
99
 
89
100
  export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
90
101
  \t.rules((rules) => ({
@@ -93,7 +104,7 @@ export const Create{{name}}Schema = {{schemaFactory}}<I{{name}}CreateRequest>()
93
104
  \t.create();`;
94
105
 
95
106
  const updateSchemaTemplate = `import { {{schemaFactory}} } from '{{schemaImport}}';
96
- import type { I{{name}}UpdateRequest } from '../../Domain/Ports/{{name}}/index.js';
107
+ import type { I{{name}}UpdateRequest } from '\${{context}}/Domain/Ports/{{name}}';
97
108
 
98
109
  export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
99
110
  \t.rules((rules) => ({
@@ -102,15 +113,19 @@ export const Update{{name}}Schema = {{schemaFactory}}<I{{name}}UpdateRequest>()
102
113
  \t.create();`;
103
114
 
104
115
  const presenterTemplate = `import { {{presenterFactory}} } from '{{presenterImport}}';
105
- import { ApplicationProvider } from '../../Application/index.js';
106
- import { Create{{name}}Schema, Update{{name}}Schema } from '../Schemas/index.js';
107
- import type { I{{name}}CreateRequest, I{{name}}UpdateRequest, I{{name}}CollectionQuery } from '../../Domain/Ports/{{name}}/index.js';
116
+ import { ApplicationProvider } from '\${{context}}/Application';
117
+ import { Create{{name}}Schema, Update{{name}}Schema } from './Schema';
118
+ import type {
119
+ \tI{{name}}CollectionQuery,
120
+ \tI{{name}}CreateRequest,
121
+ \tI{{name}}UpdateRequest
122
+ } from '\${{context}}/Domain/Ports/{{name}}';
108
123
 
109
- export const {{contextName}}{{name}}Presenter = {{presenterFactory}}('{{contextName}}{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
124
+ export const {{name}}Presenter = {{presenterFactory}}('{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
110
125
  \tconst { {{name}}Service } = ApplicationProvider();
111
126
  \t
112
- \tconst collection = async (queryParams?: I{{name}}CollectionQuery) =>
113
- \t\tawait createAsyncResource({{name}}Service.collection(queryParams));
127
+ \tconst collection = async (query?: I{{name}}CollectionQuery) =>
128
+ \t\tawait createAsyncResource({{name}}Service.collection(query));
114
129
  \t
115
130
  \tconst resource = async (id: number) =>
116
131
  \t\tawait createAsyncResource({{name}}Service.resource(id));
@@ -150,15 +165,15 @@ export default async function generateCrud() {
150
165
  // Get available datasources
151
166
  const contextPath = getContextPath(context);
152
167
  const coreDatasources = await getAvailableFiles(
153
- path.join(process.cwd(), 'src/app/core/Datasources')
168
+ path.join(process.cwd(), 'src/app/core/Datasource')
154
169
  );
155
170
  const contextDatasources = await getAvailableFiles(
156
- path.join(contextPath, 'Infrastructure', 'Datasources')
171
+ path.join(contextPath, 'Infrastructure/Http/Datasource')
157
172
  );
158
173
 
159
174
  const allDatasources = [
160
- ...coreDatasources.map(d => ({ title: `${d}Datasource (core)`, value: { name: `${d}Datasource`, from: 'core' } })),
161
- ...contextDatasources.map(d => ({ title: `${d}Datasource (${context})`, value: { name: `${d}Datasource`, from: context } }))
175
+ ...coreDatasources.map(d => ({ title: `${d} (core)`, value: { name: d, from: 'core' } })),
176
+ ...contextDatasources.map(d => ({ title: `${d} (${context})`, value: { name: d, from: context } }))
162
177
  ];
163
178
 
164
179
  const { datasource } = await prompts({
@@ -170,7 +185,7 @@ export default async function generateCrud() {
170
185
 
171
186
  // Get schema and presenter factories
172
187
  const coreSchemas = await getAvailableFiles(
173
- path.join(process.cwd(), 'src/app/core/Schemas')
188
+ path.join(process.cwd(), 'src/app/core/Schema')
174
189
  );
175
190
 
176
191
  const { schemaType } = await prompts({
@@ -178,13 +193,13 @@ export default async function generateCrud() {
178
193
  name: 'schemaType',
179
194
  message: 'Select schema factory:',
180
195
  choices: [
181
- { title: 'Use schema from package', value: 'package' },
196
+ { title: 'Use createSchemaFactory from package', value: 'package' },
182
197
  ...coreSchemas.map(s => ({ title: `Use ${s} from core`, value: s }))
183
198
  ]
184
199
  });
185
200
 
186
201
  const corePresenters = await getAvailableFiles(
187
- path.join(process.cwd(), 'src/app/core/Presenters')
202
+ path.join(process.cwd(), 'src/app/core/Presenter')
188
203
  );
189
204
 
190
205
  const { presenterType } = await prompts({
@@ -210,23 +225,26 @@ export default async function generateCrud() {
210
225
  entityTemplate.replace(/{{name}}/g, pascalName)
211
226
  );
212
227
 
213
- // 2. Create Ports (Requests/Responses)
228
+ // 2. Create Ports
214
229
  const portsPath = path.join(contextPath, 'Domain', 'Ports', pascalName);
215
230
  await writeIfNotExists(
216
231
  path.join(portsPath, 'index.ts'),
217
- requestTemplate.replace(/{{name}}/g, pascalName)
232
+ portsIndexTemplate
233
+ .replace(/{{name}}/g, pascalName)
234
+ .replace(/{{context}}/g, context)
218
235
  );
219
236
 
220
237
  // 3. Create Repository
221
- const repoPath = path.join(contextPath, 'Infrastructure', 'Repositories');
238
+ const repoPath = path.join(contextPath, 'Infrastructure', 'Http', 'Repositories');
222
239
  const datasourceImport = datasource.from === 'core'
223
- ? '$core/Datasources/index.js'
224
- : '../Datasources/index.js';
240
+ ? '$core/Datasource'
241
+ : `\$${context}/Infrastructure/Http/Datasource`;
225
242
 
226
243
  await writeIfNotExists(
227
244
  path.join(repoPath, `${pascalName}Repository.ts`),
228
245
  repositoryTemplate
229
246
  .replace(/{{name}}/g, pascalName)
247
+ .replace(/{{context}}/g, context)
230
248
  .replace(/{{endpoint}}/g, endpoint)
231
249
  .replace(/{{datasource}}/g, datasource.name)
232
250
  .replace(/{{datasourceImport}}/g, datasourceImport)
@@ -244,80 +262,117 @@ export default async function generateCrud() {
244
262
  );
245
263
  await updateIndexTs(servicePath);
246
264
 
247
- // 5. Create Schemas
248
- const schemaPath = path.join(contextPath, 'Delivery', 'Schemas');
249
- const schemaFactory = schemaType === 'package' ? 'schema' : schemaType;
265
+ // 5. Create Delivery module structure
266
+ const deliveryModulePath = path.join(contextPath, 'Delivery', pascalName);
267
+ const schemaPath = path.join(deliveryModulePath, 'Schema');
268
+
269
+ // Create schemas
270
+ const schemaFactory = schemaType === 'package' ? 'Schema' : schemaType;
250
271
  const schemaImport = schemaType === 'package'
251
272
  ? '@azure-net/kit'
252
- : '$core/Schemas/index.js';
253
-
254
- await writeIfNotExists(
255
- path.join(schemaPath, `Create${pascalName}Schema.ts`),
256
- createSchemaTemplate
257
- .replace(/{{name}}/g, pascalName)
258
- .replace(/{{schemaFactory}}/g, schemaFactory)
259
- .replace(/{{schemaImport}}/g, schemaImport)
260
- );
261
-
262
- await writeIfNotExists(
263
- path.join(schemaPath, `Update${pascalName}Schema.ts`),
264
- updateSchemaTemplate
265
- .replace(/{{name}}/g, pascalName)
266
- .replace(/{{schemaFactory}}/g, schemaFactory)
267
- .replace(/{{schemaImport}}/g, schemaImport)
268
- );
273
+ : '$core/Schema';
274
+
275
+ if (schemaType === 'package') {
276
+ // Add the import for createSchemaFactory and rules
277
+ const schemaImportFull = `import { createSchemaFactory } from '@azure-net/kit';
278
+ import { createRules, validationMessagesI18n } from '@azure-net/kit/schema';
279
+
280
+ const Schema = createSchemaFactory(createRules(validationMessagesI18n));`;
281
+
282
+ await writeIfNotExists(
283
+ path.join(schemaPath, `Create${pascalName}Schema.ts`),
284
+ schemaImportFull + '\n' + createSchemaTemplate
285
+ .replace('import { {{schemaFactory}} } from \'{{schemaImport}}\';\n', '')
286
+ .replace(/{{name}}/g, pascalName)
287
+ .replace(/{{context}}/g, context)
288
+ .replace(/{{schemaFactory}}/g, 'Schema')
289
+ );
290
+
291
+ await writeIfNotExists(
292
+ path.join(schemaPath, `Update${pascalName}Schema.ts`),
293
+ schemaImportFull + '\n' + updateSchemaTemplate
294
+ .replace('import { {{schemaFactory}} } from \'{{schemaImport}}\';\n', '')
295
+ .replace(/{{name}}/g, pascalName)
296
+ .replace(/{{context}}/g, context)
297
+ .replace(/{{schemaFactory}}/g, 'Schema')
298
+ );
299
+ } else {
300
+ await writeIfNotExists(
301
+ path.join(schemaPath, `Create${pascalName}Schema.ts`),
302
+ createSchemaTemplate
303
+ .replace(/{{name}}/g, pascalName)
304
+ .replace(/{{context}}/g, context)
305
+ .replace(/{{schemaFactory}}/g, schemaFactory)
306
+ .replace(/{{schemaImport}}/g, schemaImport)
307
+ );
308
+
309
+ await writeIfNotExists(
310
+ path.join(schemaPath, `Update${pascalName}Schema.ts`),
311
+ updateSchemaTemplate
312
+ .replace(/{{name}}/g, pascalName)
313
+ .replace(/{{context}}/g, context)
314
+ .replace(/{{schemaFactory}}/g, schemaFactory)
315
+ .replace(/{{schemaImport}}/g, schemaImport)
316
+ );
317
+ }
269
318
  await updateIndexTs(schemaPath);
270
319
 
271
320
  // 6. Create Presenter
272
- const presenterPath = path.join(contextPath, 'Delivery', 'Presenters');
273
321
  const presenterFactory = presenterType === 'package' ? 'createPresenter' : presenterType;
274
322
  const presenterImport = presenterType === 'package'
275
323
  ? '@azure-net/kit'
276
- : '$core/Presenters/index.js';
324
+ : '$core/Presenter';
277
325
 
278
326
  await writeIfNotExists(
279
- path.join(presenterPath, `${pascalName}Presenter.ts`),
327
+ path.join(deliveryModulePath, `${pascalName}Presenter.ts`),
280
328
  presenterTemplate
281
329
  .replace(/{{name}}/g, pascalName)
282
- .replace(/{{contextName}}/g, contextName)
330
+ .replace(/{{context}}/g, context)
283
331
  .replace(/{{presenterFactory}}/g, presenterFactory)
284
332
  .replace(/{{presenterImport}}/g, presenterImport)
285
333
  );
286
- await updateIndexTs(presenterPath);
334
+
335
+ // Create module index
336
+ await writeIfNotExists(
337
+ path.join(deliveryModulePath, 'index.ts'),
338
+ `export * from './${pascalName}Presenter';\nexport * from './Schema';`
339
+ );
287
340
 
288
341
  // 7. Setup Providers
289
342
  // Ensure DatasourceProvider
290
- await ensureProvider(context, 'Infrastructure', { datasource: datasource.name });
343
+ const datasourceProviderPath = path.join(contextPath, 'Infrastructure', 'Providers', 'DatasourceProvider.ts');
344
+ if (!(await fs.access(datasourceProviderPath).then(() => true).catch(() => false))) {
345
+ const datasourceProviderContent = `import { createBoundaryProvider } from '@azure-net/kit';
346
+ import { ${datasource.name} } from '${datasource.from === 'core' ? '$core/Datasource' : '../Http/Datasource'}';
347
+
348
+ export const DatasourceProvider = createBoundaryProvider('${contextName}DatasourceProvider', () => ({
349
+ \t${datasource.name}: () => new ${datasource.name}()
350
+ }));`;
351
+ await writeIfNotExists(datasourceProviderPath, datasourceProviderContent);
352
+ await updateIndexTs(path.dirname(datasourceProviderPath));
353
+ }
291
354
 
292
- // Ensure InfrastructureProvider and add repository
355
+ // Ensure InfrastructureProvider
293
356
  const infraProvider = await ensureProvider(context, 'Infrastructure', {});
294
357
  await addToProvider(
295
358
  infraProvider.path,
296
359
  `${pascalName}Repository`,
297
- '../Repositories/index.js'
360
+ '../Http/Repositories',
361
+ `DatasourceProvider.${datasource.name}`
298
362
  );
299
363
 
300
- // Update provider with datasource dependency
301
- let infraContent = await fs.readFile(infraProvider.path, 'utf-8');
302
- const repoFactory = `${pascalName}Repository: () => new ${pascalName}Repository()`;
303
- const repoWithDatasource = `${pascalName}Repository: () => new ${pascalName}Repository(DatasourceProvider.${datasource.name})`;
304
- infraContent = infraContent.replace(repoFactory, repoWithDatasource);
305
- await fs.writeFile(infraProvider.path, infraContent, 'utf-8');
306
-
307
- // Ensure ApplicationProvider and add service
364
+ // Ensure ApplicationProvider
308
365
  const appProvider = await ensureProvider(context, 'Application', { infrastructure: true });
309
366
  await addToProvider(
310
367
  appProvider.path,
311
368
  `${pascalName}Service`,
312
- '../Services/index.js'
369
+ '../Services',
370
+ `InfrastructureProvider.${pascalName}Repository`
313
371
  );
314
372
 
315
- // Update provider with repository dependency
316
- let appContent = await fs.readFile(appProvider.path, 'utf-8');
317
- const serviceFactory = `${pascalName}Service: () => new ${pascalName}Service()`;
318
- const serviceWithRepo = `${pascalName}Service: () => new ${pascalName}Service(InfrastructureProvider.${pascalName}Repository)`;
319
- appContent = appContent.replace(serviceFactory, serviceWithRepo);
320
- await fs.writeFile(appProvider.path, appContent, 'utf-8');
373
+ // Update all layer indexes
374
+ await ensureIndexExports(path.join(contextPath, 'Infrastructure'));
375
+ await ensureIndexExports(path.join(contextPath, 'Application'));
321
376
 
322
377
  console.log(`✅ CRUD module for ${pascalName} created successfully!`);
323
378
  }
@@ -3,11 +3,16 @@ import { selectContext, getContextPath, toPascalCase } from '../../utils/context
3
3
  import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
4
4
  import path from 'path';
5
5
 
6
- const datasourceTemplate = `import { BaseHttpDatasource, HttpService } from '@azure-net/kit';
6
+ const datasourceTemplate = `import { BaseHttpDatasource } from '@azure-net/kit/infra';
7
+ import { HttpService } from '@azure-net/kit/infra';
7
8
 
8
9
  export class {{name}}Datasource extends BaseHttpDatasource {
9
10
  \tconstructor() {
10
- \t\tsuper({ http: new HttpService({ baseUrl: '' }) });
11
+ \t\tsuper({
12
+ \t\t\thttp: new HttpService({
13
+ \t\t\t\tbaseUrl: 'https://api.example.com'
14
+ \t\t\t})
15
+ \t\t});
11
16
  \t}
12
17
  }`;
13
18
 
@@ -17,14 +22,14 @@ export default async function generateDatasource() {
17
22
  const { name } = await prompts({
18
23
  type: 'text',
19
24
  name: 'name',
20
- message: 'Datasource name (PascalCase):'
25
+ message: 'Datasource name (without "Datasource" suffix):'
21
26
  });
22
27
 
23
28
  const pascalName = toPascalCase(name);
24
29
  const contextPath = getContextPath(context);
25
30
  const datasourcePath = context === 'core'
26
- ? path.join(contextPath, 'Datasources')
27
- : path.join(contextPath, 'Infrastructure', 'Datasources');
31
+ ? path.join(contextPath, 'Datasource')
32
+ : path.join(contextPath, 'Infrastructure', 'Http', 'Datasource');
28
33
 
29
34
  const content = datasourceTemplate.replace(/{{name}}/g, pascalName);
30
35
  const filePath = path.join(datasourcePath, `${pascalName}Datasource.ts`);
@@ -3,15 +3,15 @@ import path from 'path';
3
3
  import { selectContext, getContextPath, toPascalCase, getAvailableFiles } from '../../utils/contextUtils.js';
4
4
  import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
5
5
 
6
- const presenterWithCoreTemplate = `import { {{corePresenter}} } from '$core/Presenters/index.js';
6
+ const presenterWithCoreTemplate = `import { {{corePresenter}} } from '\$core/Presenter';
7
7
  {{serviceImport}}
8
8
 
9
- export const {{contextName}}{{name}}Presenter = {{corePresenter}}('{{contextName}}{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
9
+ export const {{name}}Presenter = {{corePresenter}}('{{name}}Presenter', ({ createAsyncResource, createAsyncAction }) => {
10
10
  \t{{serviceInit}}
11
11
  \t
12
12
  \tconst example = async () => {
13
13
  \t\t// Implement presenter logic
14
- \t\treturn {};
14
+ \t\treturn await createAsyncResource(Promise.resolve({}));
15
15
  \t};
16
16
  \t
17
17
  \treturn { example };
@@ -20,7 +20,7 @@ export const {{contextName}}{{name}}Presenter = {{corePresenter}}('{{contextName
20
20
  const presenterWithPackageTemplate = `import { createPresenter } from '@azure-net/kit';
21
21
  {{serviceImport}}
22
22
 
23
- export const {{contextName}}{{name}}Presenter = createPresenter('{{contextName}}{{name}}Presenter', () => {
23
+ export const {{name}}Presenter = createPresenter('{{name}}Presenter', () => {
24
24
  \t{{serviceInit}}
25
25
  \t
26
26
  \tconst example = async () => {
@@ -42,12 +42,12 @@ export default async function generatePresenter() {
42
42
  const { name } = await prompts({
43
43
  type: 'text',
44
44
  name: 'name',
45
- message: 'Presenter name (without "Presenter" suffix):'
45
+ message: 'Module name (will create folder in Delivery):'
46
46
  });
47
47
 
48
48
  // Check for core presenters
49
49
  const corePresenters = await getAvailableFiles(
50
- path.join(process.cwd(), 'src/app/core/Presenters')
50
+ path.join(process.cwd(), 'src/app/core/Presenter')
51
51
  );
52
52
 
53
53
  const presenterChoices = [
@@ -70,7 +70,7 @@ export default async function generatePresenter() {
70
70
 
71
71
  const serviceChoices = [
72
72
  { title: 'Without service', value: null },
73
- ...services.map(s => ({ title: `${s}Service`, value: `${s}Service` }))
73
+ ...services.map(s => ({ title: `${s}`, value: s }))
74
74
  ];
75
75
 
76
76
  const { service } = await prompts({
@@ -81,12 +81,11 @@ export default async function generatePresenter() {
81
81
  });
82
82
 
83
83
  const pascalName = toPascalCase(name);
84
- const contextName = toPascalCase(context);
85
- const presenterPath = path.join(contextPath, 'Delivery', 'Presenters');
86
- const filePath = path.join(presenterPath, `${pascalName}Presenter.ts`);
84
+ const modulePath = path.join(contextPath, 'Delivery', pascalName);
85
+ const filePath = path.join(modulePath, `${pascalName}Presenter.ts`);
87
86
 
88
87
  const serviceImport = service
89
- ? `import { ApplicationProvider } from '../../Application/index.js';`
88
+ ? `import { ApplicationProvider } from '\$${context}/Application';`
90
89
  : '';
91
90
 
92
91
  const serviceInit = service
@@ -95,19 +94,20 @@ export default async function generatePresenter() {
95
94
 
96
95
  const content = presenterType === 'package'
97
96
  ? presenterWithPackageTemplate
98
- .replace(/{{contextName}}/g, contextName)
99
97
  .replace(/{{name}}/g, pascalName)
100
98
  .replace(/{{serviceImport}}/g, serviceImport)
101
99
  .replace(/{{serviceInit}}/g, serviceInit)
102
100
  : presenterWithCoreTemplate
103
- .replace(/{{contextName}}/g, contextName)
104
101
  .replace(/{{name}}/g, pascalName)
105
102
  .replace(/{{corePresenter}}/g, presenterType)
106
103
  .replace(/{{serviceImport}}/g, serviceImport)
107
104
  .replace(/{{serviceInit}}/g, serviceInit);
108
105
 
109
106
  await writeIfNotExists(filePath, content);
110
- await updateIndexTs(presenterPath);
107
+ await writeIfNotExists(
108
+ path.join(modulePath, 'index.ts'),
109
+ `export * from './${pascalName}Presenter';`
110
+ );
111
111
 
112
112
  console.log(`✅ Presenter created at ${filePath}`);
113
113
  }
@@ -1,7 +1,7 @@
1
1
  import prompts from 'prompts';
2
2
  import fs from 'fs/promises';
3
3
  import path from 'path';
4
- import { selectContext, getContextPath, toPascalCase, getAvailableFiles } from '../../utils/contextUtils.js';
4
+ import { selectContext, getContextPath, toPascalCase, getAvailableFiles, toCamelCase } from '../../utils/contextUtils.js';
5
5
  import { writeIfNotExists, updateIndexTs } from '../../utils/fileUtils.js';
6
6
  import { ensureProvider, addToProvider } from '../../utils/providerUtils.js';
7
7
 
@@ -34,15 +34,15 @@ export default async function generateRepository() {
34
34
  // Get available datasources
35
35
  const contextPath = getContextPath(context);
36
36
  const coreDatasources = await getAvailableFiles(
37
- path.join(process.cwd(), 'src/app/core/Datasources')
37
+ path.join(process.cwd(), 'src/app/core/Datasource')
38
38
  );
39
39
  const contextDatasources = await getAvailableFiles(
40
- path.join(contextPath, 'Infrastructure', 'Datasources')
40
+ path.join(contextPath, 'Infrastructure', 'Http', 'Datasource')
41
41
  );
42
42
 
43
43
  const allDatasources = [
44
- ...coreDatasources.map(d => ({ title: `${d}Datasource (core)`, value: { name: `${d}Datasource`, from: 'core' } })),
45
- ...contextDatasources.map(d => ({ title: `${d}Datasource (${context})`, value: { name: `${d}Datasource`, from: context } }))
44
+ ...coreDatasources.map(d => ({ title: `${d} (core)`, value: { name: d, from: 'core' } })),
45
+ ...contextDatasources.map(d => ({ title: `${d} (${context})`, value: { name: d, from: context } }))
46
46
  ];
47
47
 
48
48
  if (allDatasources.length === 0) {
@@ -58,40 +58,45 @@ export default async function generateRepository() {
58
58
  });
59
59
 
60
60
  const pascalName = toPascalCase(name);
61
- const repoPath = path.join(contextPath, 'Infrastructure', 'Repositories');
61
+ const contextName = toPascalCase(context);
62
+ const repoPath = path.join(contextPath, 'Infrastructure', 'Http', 'Repositories');
62
63
  const filePath = path.join(repoPath, `${pascalName}Repository.ts`);
63
64
 
64
65
  // Generate repository
65
66
  const datasourceImport = datasource.from === 'core'
66
- ? '$core/Datasources/index.js'
67
- : '../Datasources/index.js';
67
+ ? '$core/Datasource'
68
+ : '../Datasource';
68
69
 
69
70
  const content = repositoryTemplate
70
71
  .replace(/{{name}}/g, pascalName)
71
72
  .replace(/{{datasource}}/g, datasource.name)
72
73
  .replace(/{{datasourceImport}}/g, datasourceImport)
73
- .replace(/{{datasourceVar}}/g, toPascalCase(datasource.name).charAt(0).toLowerCase() + toPascalCase(datasource.name).slice(1));
74
+ .replace(/{{datasourceVar}}/g, toCamelCase(datasource.name));
74
75
 
75
76
  await writeIfNotExists(filePath, content);
76
77
  await updateIndexTs(repoPath);
77
78
 
78
79
  // Ensure DatasourceProvider exists
79
- const datasourceProvider = await ensureProvider(context, 'Infrastructure', { datasource: datasource.name });
80
+ const datasourceProviderPath = path.join(contextPath, 'Infrastructure', 'Providers', 'DatasourceProvider.ts');
81
+ if (!(await fs.access(datasourceProviderPath).then(() => true).catch(() => false))) {
82
+ const datasourceProviderContent = `import { createBoundaryProvider } from '@azure-net/kit';
83
+ import { ${datasource.name} } from '${datasource.from === 'core' ? '$core/Datasource' : '../Http/Datasource'}';
84
+
85
+ export const DatasourceProvider = createBoundaryProvider('${contextName}DatasourceProvider', () => ({
86
+ \t${datasource.name}: () => new ${datasource.name}()
87
+ }));`;
88
+ await writeIfNotExists(datasourceProviderPath, datasourceProviderContent);
89
+ await updateIndexTs(path.dirname(datasourceProviderPath));
90
+ }
80
91
 
81
- // Ensure InfrastructureProvider exists and add repository
82
- const infraProvider = await ensureProvider(context, 'Infrastructure', { datasource: datasource.name });
92
+ // Ensure InfrastructureProvider and add repository
93
+ const infraProvider = await ensureProvider(context, 'Infrastructure', { hasDatasource: true });
83
94
  await addToProvider(
84
95
  infraProvider.path,
85
96
  `${pascalName}Repository`,
86
- '../Repositories/index.js'
97
+ '../Http/Repositories',
98
+ `DatasourceProvider.${datasource.name}`
87
99
  );
88
100
 
89
- // Add datasource dependency to repository in provider
90
- let providerContent = await fs.readFile(infraProvider.path, 'utf-8');
91
- const repoFactory = `${pascalName}Repository: () => new ${pascalName}Repository()`;
92
- const repoWithDatasource = `${pascalName}Repository: () => new ${pascalName}Repository(DatasourceProvider.${datasource.name})`;
93
- providerContent = providerContent.replace(repoFactory, repoWithDatasource);
94
- await fs.writeFile(infraProvider.path, providerContent, 'utf-8');
95
-
96
101
  console.log(`✅ Repository created at ${filePath}`);
97
102
  }