@azure-net/cli 4.1.1 → 5.0.0-3

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
@@ -28,14 +28,6 @@ createCommand
28
28
  await handler();
29
29
  });
30
30
 
31
- createCommand
32
- .command('middleware-manager')
33
- .description('Create middleware manager in src/core/middleware-manager')
34
- .action(async () => {
35
- const handler = (await import('../src/scaffolds/createCoreMiddleware.js')).default;
36
- await handler();
37
- });
38
-
39
31
  createCommand
40
32
  .command('translation-manager')
41
33
  .description('Create translation manager in src/core/translation')
@@ -60,14 +52,6 @@ createCommand
60
52
  await handler();
61
53
  });
62
54
 
63
- createCommand
64
- .command('middleware')
65
- .description('Create middleware in src/core/middleware-manager/middlewares')
66
- .action(async () => {
67
- const handler = (await import('../src/scaffolds/createMiddleware.js')).default;
68
- await handler();
69
- });
70
-
71
55
  createCommand
72
56
  .command('response')
73
57
  .description('Create response in src/core/response')
@@ -265,6 +249,22 @@ checkCommand
265
249
  await handler();
266
250
  });
267
251
 
252
+ checkCommand
253
+ .command('hooks')
254
+ .description('Check that manual SvelteKit hooks are not present')
255
+ .action(async () => {
256
+ const handler = (await import('../src/checks/checkHooks.js')).default;
257
+ await handler();
258
+ });
259
+
260
+ checkCommand
261
+ .command('layer-imports')
262
+ .description('Check forbidden imports between layers inside each context')
263
+ .action(async () => {
264
+ const handler = (await import('../src/checks/checkLayerImports.js')).default;
265
+ await handler();
266
+ });
267
+
268
268
  checkCommand
269
269
  .command('layer-boundaries')
270
270
  .description('Check layer boundaries between contexts by aliases')
package/bin/azure-net.ts CHANGED
@@ -28,14 +28,6 @@ createCommand
28
28
  await handler();
29
29
  });
30
30
 
31
- createCommand
32
- .command('middleware-manager')
33
- .description('Create middleware manager in src/core/middleware-manager')
34
- .action(async () => {
35
- const handler = (await import('../src/scaffolds/createCoreMiddleware.js')).default;
36
- await handler();
37
- });
38
-
39
31
  createCommand
40
32
  .command('translation-manager')
41
33
  .description('Create translation manager in src/core/translation')
@@ -60,14 +52,6 @@ createCommand
60
52
  await handler();
61
53
  });
62
54
 
63
- createCommand
64
- .command('middleware')
65
- .description('Create middleware in src/core/middleware-manager/middlewares')
66
- .action(async () => {
67
- const handler = (await import('../src/scaffolds/createMiddleware.js')).default;
68
- await handler();
69
- });
70
-
71
55
  createCommand
72
56
  .command('response')
73
57
  .description('Create response in src/core/response')
@@ -265,6 +249,22 @@ checkCommand
265
249
  await handler();
266
250
  });
267
251
 
252
+ checkCommand
253
+ .command('hooks')
254
+ .description('Check that manual SvelteKit hooks are not present')
255
+ .action(async () => {
256
+ const handler = (await import('../src/checks/checkHooks.js')).default;
257
+ await handler();
258
+ });
259
+
260
+ checkCommand
261
+ .command('layer-imports')
262
+ .description('Check forbidden imports between layers inside each context')
263
+ .action(async () => {
264
+ const handler = (await import('../src/checks/checkLayerImports.js')).default;
265
+ await handler();
266
+ });
267
+
268
268
  checkCommand
269
269
  .command('layer-boundaries')
270
270
  .description('Check layer boundaries between contexts by aliases')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure-net/cli",
3
- "version": "4.1.1",
3
+ "version": "5.0.0-3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "azure-net": "bin/azure-net.js"
@@ -0,0 +1,22 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const forbiddenHooks = ['hooks.server.ts', 'hooks.server.js', 'hooks.client.ts', 'hooks.client.js'];
5
+
6
+ export default async function checkHooks() {
7
+ const srcPath = path.join(process.cwd(), 'src');
8
+ const found = forbiddenHooks.map((filename) => path.join(srcPath, filename)).filter((filepath) => fs.existsSync(filepath));
9
+
10
+ if (!found.length) {
11
+ console.log('✅ No manual SvelteKit hooks found.');
12
+ return;
13
+ }
14
+
15
+ console.error('❌ Manual SvelteKit hooks are not allowed with @azure-net/kit AzureNetPlugin.');
16
+ console.error('Move lifecycle code to src/program.ts through createApp(). Found:');
17
+ for (const filepath of found) {
18
+ console.error(` - ${path.relative(process.cwd(), filepath)}`);
19
+ }
20
+
21
+ process.exitCode = 1;
22
+ }
@@ -0,0 +1,22 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const forbiddenHooks = ['hooks.server.ts', 'hooks.server.js', 'hooks.client.ts', 'hooks.client.js'];
5
+
6
+ export default async function checkHooks(): Promise<void> {
7
+ const srcPath = path.join(process.cwd(), 'src');
8
+ const found = forbiddenHooks.map((filename) => path.join(srcPath, filename)).filter((filepath) => fs.existsSync(filepath));
9
+
10
+ if (!found.length) {
11
+ console.log('✅ No manual SvelteKit hooks found.');
12
+ return;
13
+ }
14
+
15
+ console.error('❌ Manual SvelteKit hooks are not allowed with @azure-net/kit AzureNetPlugin.');
16
+ console.error('Move lifecycle code to src/program.ts through createApp(). Found:');
17
+ for (const filepath of found) {
18
+ console.error(` - ${path.relative(process.cwd(), filepath)}`);
19
+ }
20
+
21
+ process.exitCode = 1;
22
+ }
@@ -5,12 +5,16 @@ import checkDomain from './checkDomain.js';
5
5
  import checkLayerBoundaries from './checkLayerBoundaries.js';
6
6
  import checkFoldersStructure from './checkFoldersStructure.js';
7
7
  import checkImports from './checkImports.js';
8
+ import checkHooks from './checkHooks.js';
9
+ import checkLayerImports from './checkLayerImports.js';
8
10
  const tasks = [
9
11
  { name: 'presenter-names', run: checkPresenterNames },
10
12
  { name: 'provider-names', run: checkProviderNames },
11
13
  { name: 'provider-graph', run: checkProviderGraph },
12
14
  { name: 'domain', run: checkDomain },
13
15
  { name: 'imports', run: checkImports },
16
+ { name: 'hooks', run: checkHooks },
17
+ { name: 'layer-imports', run: checkLayerImports },
14
18
  { name: 'layer-boundaries', run: checkLayerBoundaries },
15
19
  { name: 'folders-structure', run: checkFoldersStructure }
16
20
  ];
@@ -5,6 +5,8 @@ import checkDomain from './checkDomain.js';
5
5
  import checkLayerBoundaries from './checkLayerBoundaries.js';
6
6
  import checkFoldersStructure from './checkFoldersStructure.js';
7
7
  import checkImports from './checkImports.js';
8
+ import checkHooks from './checkHooks.js';
9
+ import checkLayerImports from './checkLayerImports.js';
8
10
 
9
11
  type CheckTask = {
10
12
  name: string;
@@ -17,6 +19,8 @@ const tasks: CheckTask[] = [
17
19
  { name: 'provider-graph', run: checkProviderGraph },
18
20
  { name: 'domain', run: checkDomain },
19
21
  { name: 'imports', run: checkImports },
22
+ { name: 'hooks', run: checkHooks },
23
+ { name: 'layer-imports', run: checkLayerImports },
20
24
  { name: 'layer-boundaries', run: checkLayerBoundaries },
21
25
  { name: 'folders-structure', run: checkFoldersStructure }
22
26
  ];
@@ -0,0 +1,136 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { normalizeContexts } from '../init/initAliases.js';
4
+ import { loadUserConfig } from '../utils/loadConfig.js';
5
+ const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
6
+ const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
7
+ const LAYERS = ['domain', 'application', 'infrastructure', 'presentation'];
8
+ const FORBIDDEN_LAYER_IMPORTS = {
9
+ infrastructure: ['application', 'presentation'],
10
+ application: ['presentation'],
11
+ domain: ['application', 'infrastructure', 'presentation'],
12
+ presentation: []
13
+ };
14
+ const IMPORT_FROM_PATTERN = /\bimport\s+(?:type\s+)?[\s\S]*?\sfrom\s*['"]([^'"]+)['"]/g;
15
+ const EXPORT_FROM_PATTERN = /\bexport\s+(?:type\s+)?\{[\s\S]*?\}\sfrom\s*['"]([^'"]+)['"]/g;
16
+ const DYNAMIC_IMPORT_PATTERN = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
17
+ const toPosixPath = (value) => value.split(path.sep).join('/');
18
+ const findLineByIndex = (content, index) => {
19
+ let line = 1;
20
+ for (let i = 0; i < index; i += 1) {
21
+ if (content[i] === '\n') line += 1;
22
+ }
23
+ return line;
24
+ };
25
+ const getRelativePath = (filePath) => toPosixPath(path.relative(process.cwd(), filePath));
26
+ const listFiles = async (rootDir) => {
27
+ const files = [];
28
+ const stack = [rootDir];
29
+ while (stack.length) {
30
+ const current = stack.pop();
31
+ if (!current) continue;
32
+ let entries = [];
33
+ try {
34
+ entries = await fs.readdir(current, { withFileTypes: true });
35
+ } catch {
36
+ continue;
37
+ }
38
+ for (const entry of entries) {
39
+ if (entry.isDirectory()) {
40
+ if (!IGNORED_DIRS.has(entry.name)) {
41
+ stack.push(path.join(current, entry.name));
42
+ }
43
+ continue;
44
+ }
45
+ if (!entry.isFile()) continue;
46
+ const filePath = path.join(current, entry.name);
47
+ if (!TARGET_EXTENSIONS.has(path.extname(filePath))) continue;
48
+ files.push(filePath);
49
+ }
50
+ }
51
+ return files;
52
+ };
53
+ const extractImports = (content) => {
54
+ const matches = [];
55
+ const patterns = [IMPORT_FROM_PATTERN, EXPORT_FROM_PATTERN, DYNAMIC_IMPORT_PATTERN];
56
+ for (const pattern of patterns) {
57
+ const regex = new RegExp(pattern.source, 'g');
58
+ let match;
59
+ while ((match = regex.exec(content)) !== null) {
60
+ const value = match[1];
61
+ if (!value) continue;
62
+ matches.push({ value, line: findLineByIndex(content, match.index) });
63
+ }
64
+ }
65
+ return matches;
66
+ };
67
+ const getLayerFromPath = (filePath, contextRoot) => {
68
+ for (const layer of LAYERS) {
69
+ const layerRoot = path.join(contextRoot, 'layers', layer);
70
+ if (filePath === layerRoot || filePath.startsWith(`${layerRoot}${path.sep}`)) {
71
+ return layer;
72
+ }
73
+ }
74
+ return null;
75
+ };
76
+ const getLayerFromAliasImport = (importPath, context) => {
77
+ if (importPath !== context.alias && !importPath.startsWith(`${context.alias}/`)) return null;
78
+ const withoutAlias = importPath.slice(context.alias.length).replace(/^\/+/, '');
79
+ for (const layer of LAYERS) {
80
+ if (withoutAlias === `layers/${layer}` || withoutAlias.startsWith(`layers/${layer}/`)) return layer;
81
+ if (withoutAlias === layer || withoutAlias.startsWith(`${layer}/`)) return layer;
82
+ }
83
+ return null;
84
+ };
85
+ const getLayerFromRelativeImport = (filePath, importPath, contextRoot) => {
86
+ if (!importPath.startsWith('.')) return null;
87
+ const absoluteImportPath = path.resolve(path.dirname(filePath), importPath);
88
+ return getLayerFromPath(absoluteImportPath, contextRoot);
89
+ };
90
+ const createMessage = (sourceLayer, targetLayer, context) =>
91
+ `Layer import violation: "${context.name}" ${sourceLayer} layer cannot import from ${targetLayer} layer`;
92
+ export default async function checkLayerImports() {
93
+ const config = await loadUserConfig();
94
+ const contexts = normalizeContexts(config.contexts);
95
+ if (!contexts.length) {
96
+ console.error('❌ No contexts found in azure-net.config.ts/js. Fill `contexts` first.');
97
+ process.exitCode = 1;
98
+ return;
99
+ }
100
+ const issues = [];
101
+ for (const context of contexts) {
102
+ const contextRoot = path.join(process.cwd(), 'src', 'app', context.name);
103
+ try {
104
+ await fs.access(contextRoot);
105
+ } catch {
106
+ continue;
107
+ }
108
+ const files = await listFiles(path.join(contextRoot, 'layers'));
109
+ for (const filePath of files) {
110
+ const sourceLayer = getLayerFromPath(filePath, contextRoot);
111
+ if (!sourceLayer) continue;
112
+ const forbiddenLayers = FORBIDDEN_LAYER_IMPORTS[sourceLayer];
113
+ if (!forbiddenLayers.length) continue;
114
+ const content = await fs.readFile(filePath, 'utf-8');
115
+ const imports = extractImports(content);
116
+ for (const entry of imports) {
117
+ const targetLayer = getLayerFromAliasImport(entry.value, context) ?? getLayerFromRelativeImport(filePath, entry.value, contextRoot);
118
+ if (!targetLayer || !forbiddenLayers.includes(targetLayer)) continue;
119
+ issues.push({
120
+ file: filePath,
121
+ line: entry.line,
122
+ message: createMessage(sourceLayer, targetLayer, context)
123
+ });
124
+ }
125
+ }
126
+ }
127
+ if (!issues.length) {
128
+ console.log('✅ Layer imports check passed.');
129
+ return;
130
+ }
131
+ console.error('❌ Layer imports check failed:');
132
+ for (const issue of issues) {
133
+ console.error(`• ${getRelativePath(issue.file)}:${issue.line} — ${issue.message}`);
134
+ }
135
+ process.exitCode = 1;
136
+ }
@@ -0,0 +1,185 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { normalizeContexts } from '../init/initAliases.js';
4
+ import { loadUserConfig } from '../utils/loadConfig.js';
5
+
6
+ type LayerName = 'domain' | 'application' | 'infrastructure' | 'presentation';
7
+
8
+ type ContextConfig = {
9
+ name: string;
10
+ alias: string;
11
+ };
12
+
13
+ type LayerImportIssue = {
14
+ file: string;
15
+ line: number;
16
+ message: string;
17
+ };
18
+
19
+ const TARGET_EXTENSIONS = new Set(['.ts', '.js', '.svelte']);
20
+ const IGNORED_DIRS = new Set(['node_modules', 'dist', '.svelte-kit', '.git']);
21
+ const LAYERS: LayerName[] = ['domain', 'application', 'infrastructure', 'presentation'];
22
+ const FORBIDDEN_LAYER_IMPORTS: Record<LayerName, LayerName[]> = {
23
+ infrastructure: ['application', 'presentation'],
24
+ application: ['presentation'],
25
+ domain: ['application', 'infrastructure', 'presentation'],
26
+ presentation: []
27
+ };
28
+
29
+ const IMPORT_FROM_PATTERN = /\bimport\s+(?:type\s+)?[\s\S]*?\sfrom\s*['"]([^'"]+)['"]/g;
30
+ const EXPORT_FROM_PATTERN = /\bexport\s+(?:type\s+)?\{[\s\S]*?\}\sfrom\s*['"]([^'"]+)['"]/g;
31
+ const DYNAMIC_IMPORT_PATTERN = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
32
+
33
+ const toPosixPath = (value: string): string => value.split(path.sep).join('/');
34
+
35
+ const findLineByIndex = (content: string, index: number): number => {
36
+ let line = 1;
37
+ for (let i = 0; i < index; i += 1) {
38
+ if (content[i] === '\n') line += 1;
39
+ }
40
+ return line;
41
+ };
42
+
43
+ const getRelativePath = (filePath: string): string => toPosixPath(path.relative(process.cwd(), filePath));
44
+
45
+ const listFiles = async (rootDir: string): Promise<string[]> => {
46
+ const files: string[] = [];
47
+ const stack: string[] = [rootDir];
48
+
49
+ while (stack.length) {
50
+ const current = stack.pop();
51
+ if (!current) continue;
52
+
53
+ let entries: import('node:fs').Dirent[] = [];
54
+ try {
55
+ entries = await fs.readdir(current, { withFileTypes: true });
56
+ } catch {
57
+ continue;
58
+ }
59
+
60
+ for (const entry of entries) {
61
+ if (entry.isDirectory()) {
62
+ if (!IGNORED_DIRS.has(entry.name)) {
63
+ stack.push(path.join(current, entry.name));
64
+ }
65
+ continue;
66
+ }
67
+
68
+ if (!entry.isFile()) continue;
69
+ const filePath = path.join(current, entry.name);
70
+ if (!TARGET_EXTENSIONS.has(path.extname(filePath))) continue;
71
+ files.push(filePath);
72
+ }
73
+ }
74
+
75
+ return files;
76
+ };
77
+
78
+ const extractImports = (content: string): Array<{ value: string; line: number }> => {
79
+ const matches: Array<{ value: string; line: number }> = [];
80
+ const patterns = [IMPORT_FROM_PATTERN, EXPORT_FROM_PATTERN, DYNAMIC_IMPORT_PATTERN];
81
+
82
+ for (const pattern of patterns) {
83
+ const regex = new RegExp(pattern.source, 'g');
84
+ let match: RegExpExecArray | null;
85
+ while ((match = regex.exec(content)) !== null) {
86
+ const value = match[1];
87
+ if (!value) continue;
88
+ matches.push({ value, line: findLineByIndex(content, match.index) });
89
+ }
90
+ }
91
+
92
+ return matches;
93
+ };
94
+
95
+ const getLayerFromPath = (filePath: string, contextRoot: string): LayerName | null => {
96
+ for (const layer of LAYERS) {
97
+ const layerRoot = path.join(contextRoot, 'layers', layer);
98
+ if (filePath === layerRoot || filePath.startsWith(`${layerRoot}${path.sep}`)) {
99
+ return layer;
100
+ }
101
+ }
102
+
103
+ return null;
104
+ };
105
+
106
+ const getLayerFromAliasImport = (importPath: string, context: ContextConfig): LayerName | null => {
107
+ if (importPath !== context.alias && !importPath.startsWith(`${context.alias}/`)) return null;
108
+
109
+ const withoutAlias = importPath.slice(context.alias.length).replace(/^\/+/, '');
110
+ for (const layer of LAYERS) {
111
+ if (withoutAlias === `layers/${layer}` || withoutAlias.startsWith(`layers/${layer}/`)) return layer;
112
+ if (withoutAlias === layer || withoutAlias.startsWith(`${layer}/`)) return layer;
113
+ }
114
+
115
+ return null;
116
+ };
117
+
118
+ const getLayerFromRelativeImport = (filePath: string, importPath: string, contextRoot: string): LayerName | null => {
119
+ if (!importPath.startsWith('.')) return null;
120
+
121
+ const absoluteImportPath = path.resolve(path.dirname(filePath), importPath);
122
+ return getLayerFromPath(absoluteImportPath, contextRoot);
123
+ };
124
+
125
+ const createMessage = (sourceLayer: LayerName, targetLayer: LayerName, context: ContextConfig): string =>
126
+ `Layer import violation: "${context.name}" ${sourceLayer} layer cannot import from ${targetLayer} layer`;
127
+
128
+ export default async function checkLayerImports(): Promise<void> {
129
+ const config = await loadUserConfig();
130
+ const contexts = normalizeContexts(config.contexts) as ContextConfig[];
131
+
132
+ if (!contexts.length) {
133
+ console.error('❌ No contexts found in azure-net.config.ts/js. Fill `contexts` first.');
134
+ process.exitCode = 1;
135
+ return;
136
+ }
137
+
138
+ const issues: LayerImportIssue[] = [];
139
+
140
+ for (const context of contexts) {
141
+ const contextRoot = path.join(process.cwd(), 'src', 'app', context.name);
142
+
143
+ try {
144
+ await fs.access(contextRoot);
145
+ } catch {
146
+ continue;
147
+ }
148
+
149
+ const files = await listFiles(path.join(contextRoot, 'layers'));
150
+
151
+ for (const filePath of files) {
152
+ const sourceLayer = getLayerFromPath(filePath, contextRoot);
153
+ if (!sourceLayer) continue;
154
+
155
+ const forbiddenLayers = FORBIDDEN_LAYER_IMPORTS[sourceLayer];
156
+ if (!forbiddenLayers.length) continue;
157
+
158
+ const content = await fs.readFile(filePath, 'utf-8');
159
+ const imports = extractImports(content);
160
+
161
+ for (const entry of imports) {
162
+ const targetLayer = getLayerFromAliasImport(entry.value, context) ?? getLayerFromRelativeImport(filePath, entry.value, contextRoot);
163
+ if (!targetLayer || !forbiddenLayers.includes(targetLayer)) continue;
164
+
165
+ issues.push({
166
+ file: filePath,
167
+ line: entry.line,
168
+ message: createMessage(sourceLayer, targetLayer, context)
169
+ });
170
+ }
171
+ }
172
+ }
173
+
174
+ if (!issues.length) {
175
+ console.log('✅ Layer imports check passed.');
176
+ return;
177
+ }
178
+
179
+ console.error('❌ Layer imports check failed:');
180
+ for (const issue of issues) {
181
+ console.error(`• ${getRelativePath(issue.file)}:${issue.line} — ${issue.message}`);
182
+ }
183
+
184
+ process.exitCode = 1;
185
+ }
@@ -3,6 +3,8 @@ export { default as checkProviderNames } from './checkProviderNames.js';
3
3
  export { default as checkProviderGraph } from './checkProviderGraph.js';
4
4
  export { default as checkDomain } from './checkDomain.js';
5
5
  export { default as checkImports } from './checkImports.js';
6
+ export { default as checkHooks } from './checkHooks.js';
7
+ export { default as checkLayerImports } from './checkLayerImports.js';
6
8
  export { default as checkLayerBoundaries } from './checkLayerBoundaries.js';
7
9
  export { default as checkFoldersStructure } from './checkFoldersStructure.js';
8
10
  export { default as checkInternal } from './checkInternal.js';
@@ -3,6 +3,8 @@ export { default as checkProviderNames } from './checkProviderNames.js';
3
3
  export { default as checkProviderGraph } from './checkProviderGraph.js';
4
4
  export { default as checkDomain } from './checkDomain.js';
5
5
  export { default as checkImports } from './checkImports.js';
6
+ export { default as checkHooks } from './checkHooks.js';
7
+ export { default as checkLayerImports } from './checkLayerImports.js';
6
8
  export { default as checkLayerBoundaries } from './checkLayerBoundaries.js';
7
9
  export { default as checkFoldersStructure } from './checkFoldersStructure.js';
8
10
  export { default as checkInternal } from './checkInternal.js';
@@ -5,12 +5,11 @@ import initStructure from '../init/initStructure.js';
5
5
  import { normalizeContexts } from '../init/initAliases.js';
6
6
  import { loadUserConfig } from '../utils/loadConfig.js';
7
7
  import {
8
- createCoreMiddleware,
9
8
  createCoreSchema,
10
9
  createDatasource,
11
10
  createDatasourceProvider,
12
- createMiddleware,
13
11
  createPresenterFactory,
12
+ createProgram,
14
13
  createResponse,
15
14
  createTranslationManager
16
15
  } from '../scaffolds/index.js';
@@ -60,19 +59,12 @@ export default async function installFresh() {
60
59
 
61
60
  await initStructure();
62
61
  await initEdges();
62
+ await createProgram();
63
63
 
64
64
  await runOptionalStep('Add commit tools (lint/commit conventions)?', async () => {
65
65
  await addLint();
66
66
  });
67
67
 
68
- await runOptionalStep('Create middleware manager?', async () => {
69
- await createCoreMiddleware();
70
- });
71
-
72
- await runOptionalStep('Create starter middleware?', async () => {
73
- await createMiddleware();
74
- });
75
-
76
68
  await runOptionalStep('Create schema factory?', async () => {
77
69
  await createCoreSchema();
78
70
  });
@@ -5,12 +5,11 @@ import initStructure from '../init/initStructure.js';
5
5
  import { normalizeContexts } from '../init/initAliases.js';
6
6
  import { loadUserConfig } from '../utils/loadConfig.js';
7
7
  import {
8
- createCoreMiddleware,
9
8
  createCoreSchema,
10
9
  createDatasource,
11
10
  createDatasourceProvider,
12
- createMiddleware,
13
11
  createPresenterFactory,
12
+ createProgram,
14
13
  createResponse,
15
14
  createTranslationManager
16
15
  } from '../scaffolds/index.js';
@@ -60,19 +59,12 @@ export default async function installFresh(): Promise<void> {
60
59
 
61
60
  await initStructure();
62
61
  await initEdges();
62
+ await createProgram();
63
63
 
64
64
  await runOptionalStep('Add commit tools (lint/commit conventions)?', async () => {
65
65
  await addLint();
66
66
  });
67
67
 
68
- await runOptionalStep('Create middleware manager?', async () => {
69
- await createCoreMiddleware();
70
- });
71
-
72
- await runOptionalStep('Create starter middleware?', async () => {
73
- await createMiddleware();
74
- });
75
-
76
68
  await runOptionalStep('Create schema factory?', async () => {
77
69
  await createCoreSchema();
78
70
  });
@@ -0,0 +1,19 @@
1
+ import path from 'node:path';
2
+ import { writeIfNotExists } from '../utils/fileUtils.js';
3
+
4
+ const createProgramTemplate = () => `import { createApp } from '@azure-net/kit';
5
+
6
+ export const { register, App } = createApp((app) => app);
7
+ `;
8
+
9
+ export default async function createProgram() {
10
+ const programPath = path.join(process.cwd(), 'src', 'program.ts');
11
+ const created = await writeIfNotExists(programPath, createProgramTemplate());
12
+
13
+ if (!created) {
14
+ console.log('⚠️ Program already exists. Nothing was overwritten.');
15
+ return;
16
+ }
17
+
18
+ console.log(`✅ Program created: ${programPath}`);
19
+ }
@@ -0,0 +1,19 @@
1
+ import path from 'node:path';
2
+ import { writeIfNotExists } from '../utils/fileUtils.js';
3
+
4
+ const createProgramTemplate = (): string => `import { createApp } from '@azure-net/kit';
5
+
6
+ export const { register, Container } = createApp((app) => app);
7
+ `;
8
+
9
+ export default async function createProgram(): Promise<void> {
10
+ const programPath = path.join(process.cwd(), 'src', 'program.ts');
11
+ const created = await writeIfNotExists(programPath, createProgramTemplate());
12
+
13
+ if (!created) {
14
+ console.log('⚠️ Program already exists. Nothing was overwritten.');
15
+ return;
16
+ }
17
+
18
+ console.log(`✅ Program created: ${programPath}`);
19
+ }
@@ -1,9 +1,8 @@
1
1
  export { default as createCoreSchema } from './createCoreSchema.js';
2
- export { default as createCoreMiddleware } from './createCoreMiddleware.js';
3
2
  export { default as createTranslationManager } from './createTranslationManager.js';
4
3
  export { default as createPresenterFactory } from './createPresenterFactory.js';
5
4
  export { default as createSchemaRule } from './createSchemaRule.js';
6
- export { default as createMiddleware } from './createMiddleware.js';
7
5
  export { default as createResponse } from './createResponse.js';
8
6
  export { default as createDatasource } from './createDatasource.js';
9
7
  export { default as createDatasourceProvider } from './createDatasourceProvider.js';
8
+ export { default as createProgram } from './createProgram.js';
@@ -1,9 +1,8 @@
1
1
  export { default as createCoreSchema } from './createCoreSchema.js';
2
- export { default as createCoreMiddleware } from './createCoreMiddleware.js';
3
2
  export { default as createTranslationManager } from './createTranslationManager.js';
4
3
  export { default as createPresenterFactory } from './createPresenterFactory.js';
5
4
  export { default as createSchemaRule } from './createSchemaRule.js';
6
- export { default as createMiddleware } from './createMiddleware.js';
7
5
  export { default as createResponse } from './createResponse.js';
8
6
  export { default as createDatasource } from './createDatasource.js';
9
7
  export { default as createDatasourceProvider } from './createDatasourceProvider.js';
8
+ export { default as createProgram } from './createProgram.js';
@@ -1,27 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { writeIfNotExists } from '../utils/fileUtils.js';
4
-
5
- const createMiddlewareManagerTemplate = () => `import { createMiddlewareManager } from '@azure-net/kit';
6
-
7
- export const { clientMiddleware, serverMiddleware, executeMiddlewares } = createMiddlewareManager([]);
8
- `;
9
-
10
- export default async function createCoreMiddleware() {
11
- const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
12
- const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
13
- const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
14
- const indexFile = path.join(middlewareManagerPath, 'index.ts');
15
-
16
- await fs.mkdir(middlewaresPath, { recursive: true });
17
-
18
- const createdManager = await writeIfNotExists(middlewareManagerFile, createMiddlewareManagerTemplate());
19
- const createdIndex = await writeIfNotExists(indexFile, `export * from './MiddlewareManager';\n`);
20
-
21
- if (!createdManager && !createdIndex) {
22
- console.log('⚠️ Core middleware manager already exists. Nothing was overwritten.');
23
- return;
24
- }
25
-
26
- console.log(`✅ Core middleware manager created: ${middlewareManagerPath}`);
27
- }
@@ -1,27 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import { writeIfNotExists } from '../utils/fileUtils.js';
4
-
5
- const createMiddlewareManagerTemplate = (): string => `import { createMiddlewareManager } from '@azure-net/kit';
6
-
7
- export const { clientMiddleware, serverMiddleware, executeMiddlewares } = createMiddlewareManager([]);
8
- `;
9
-
10
- export default async function createCoreMiddleware(): Promise<void> {
11
- const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
12
- const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
13
- const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
14
- const indexFile = path.join(middlewareManagerPath, 'index.ts');
15
-
16
- await fs.mkdir(middlewaresPath, { recursive: true });
17
-
18
- const createdManager = await writeIfNotExists(middlewareManagerFile, createMiddlewareManagerTemplate());
19
- const createdIndex = await writeIfNotExists(indexFile, `export * from './MiddlewareManager';\n`);
20
-
21
- if (!createdManager && !createdIndex) {
22
- console.log('⚠️ Core middleware manager already exists. Nothing was overwritten.');
23
- return;
24
- }
25
-
26
- console.log(`✅ Core middleware manager created: ${middlewareManagerPath}`);
27
- }
@@ -1,105 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import prompts from 'prompts';
4
- import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
5
- import { toPascalCase } from '../utils/contextUtils.js';
6
-
7
- const createMiddlewareTemplate = (middlewareName) => `import { type IMiddleware } from '@azure-net/kit';
8
-
9
- export const ${middlewareName}: IMiddleware = async ({ next }) => {
10
- \treturn next();
11
- };
12
- `;
13
-
14
- const addMiddlewareToManager = (content, middlewareName) => {
15
- const wordRegex = new RegExp(`\\b${middlewareName}\\b`);
16
- if (wordRegex.test(content)) return content;
17
-
18
- const importLine = `import { ${middlewareName} } from './middlewares';`;
19
- if (!content.includes(importLine)) {
20
- const importMatches = Array.from(content.matchAll(/^import[^\n]*$/gm));
21
- if (importMatches.length) {
22
- const lastImport = importMatches[importMatches.length - 1];
23
- const insertPos = (lastImport.index ?? 0) + lastImport[0].length;
24
- content = `${content.slice(0, insertPos)}\n${importLine}${content.slice(insertPos)}`;
25
- } else {
26
- content = `${importLine}\n${content}`;
27
- }
28
- }
29
-
30
- const managerCallRegex = /createMiddlewareManager\s*\(\s*\[([\s\S]*?)\]\s*\)/m;
31
- const match = content.match(managerCallRegex);
32
- if (!match) return content;
33
-
34
- const arrayBody = match[1] ?? '';
35
- if (wordRegex.test(arrayBody)) return content;
36
-
37
- const isMultiline = arrayBody.includes('\n');
38
- const hasItems = arrayBody.trim().length > 0;
39
-
40
- if (!isMultiline) {
41
- const trimmed = arrayBody.trim();
42
- const nextArrayBody = hasItems ? `${trimmed}, ${middlewareName}` : middlewareName;
43
- return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
44
- }
45
-
46
- if (!hasItems) {
47
- const baseIndent = arrayBody.match(/\n([ \t]*)$/)?.[1] ?? '\t';
48
- const nextArrayBody = `\n${baseIndent}${middlewareName}\n`;
49
- return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
50
- }
51
-
52
- const trimmedBody = arrayBody.trimEnd();
53
- const lastItemIndent = trimmedBody.match(/(?:^|\n)([ \t]*)\S[^\n]*$/)?.[1] ?? '\t';
54
- const nextArrayBody = `${trimmedBody},\n${lastItemIndent}${middlewareName}\n`;
55
-
56
- return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
57
- };
58
-
59
- export default async function createMiddleware() {
60
- const { middlewareNameRaw } = await prompts({
61
- type: 'text',
62
- name: 'middlewareNameRaw',
63
- message: 'Middleware name:',
64
- initial: 'RoutesGuard'
65
- });
66
-
67
- const middlewareName = toPascalCase(String(middlewareNameRaw ?? 'RoutesGuard')) || 'RoutesGuard';
68
- const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
69
- const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
70
- const middlewareFilePath = path.join(middlewaresPath, `${middlewareName}.ts`);
71
- const middlewaresIndexPath = path.join(middlewaresPath, 'index.ts');
72
- const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
73
-
74
- await fs.mkdir(middlewaresPath, { recursive: true });
75
-
76
- const created = await writeIfNotExists(middlewareFilePath, createMiddlewareTemplate(middlewareName));
77
- await updateIndexTs(middlewaresPath);
78
-
79
- if (!created) {
80
- console.log(`⚠️ Middleware "${middlewareName}" already exists. File was not overwritten.`);
81
- return;
82
- }
83
-
84
- let managerContent;
85
- try {
86
- managerContent = await fs.readFile(middlewareManagerFile, 'utf-8');
87
- } catch {
88
- console.log(`⚠️ Middleware manager not found at ${middlewareManagerFile}. Run "azure-net create middleware-manager" first.`);
89
- return;
90
- }
91
-
92
- const nextManagerContent = addMiddlewareToManager(managerContent, middlewareName);
93
- await fs.writeFile(middlewareManagerFile, nextManagerContent, 'utf-8');
94
-
95
- const hasIndex = await fs
96
- .access(middlewaresIndexPath)
97
- .then(() => true)
98
- .catch(() => false);
99
-
100
- console.log(`✅ Middleware created: ${middlewareFilePath}`);
101
- if (hasIndex) {
102
- console.log(`✅ Middlewares index updated: ${middlewaresIndexPath}`);
103
- }
104
- console.log(`✅ Middleware manager updated: ${middlewareManagerFile}`);
105
- }
@@ -1,105 +0,0 @@
1
- import fs from 'node:fs/promises';
2
- import path from 'node:path';
3
- import prompts from 'prompts';
4
- import { writeIfNotExists, updateIndexTs } from '../utils/fileUtils.js';
5
- import { toPascalCase } from '../utils/contextUtils.js';
6
-
7
- const createMiddlewareTemplate = (middlewareName: string): string => `import { type IMiddleware } from '@azure-net/kit';
8
-
9
- export const ${middlewareName}: IMiddleware = async ({ next }) => {
10
- \treturn next();
11
- };
12
- `;
13
-
14
- const addMiddlewareToManager = (content: string, middlewareName: string): string => {
15
- const wordRegex = new RegExp(`\\b${middlewareName}\\b`);
16
- if (wordRegex.test(content)) return content;
17
-
18
- const importLine = `import { ${middlewareName} } from './middlewares';`;
19
- if (!content.includes(importLine)) {
20
- const importMatches = Array.from(content.matchAll(/^import[^\n]*$/gm));
21
- const lastImport = importMatches.at(-1);
22
- if (lastImport) {
23
- const insertPos = (lastImport.index ?? 0) + lastImport[0].length;
24
- content = `${content.slice(0, insertPos)}\n${importLine}${content.slice(insertPos)}`;
25
- } else {
26
- content = `${importLine}\n${content}`;
27
- }
28
- }
29
-
30
- const managerCallRegex = /createMiddlewareManager\s*\(\s*\[([\s\S]*?)\]\s*\)/m;
31
- const match = content.match(managerCallRegex);
32
- if (!match) return content;
33
-
34
- const arrayBody = match[1] ?? '';
35
- if (wordRegex.test(arrayBody)) return content;
36
-
37
- const isMultiline = arrayBody.includes('\n');
38
- const hasItems = arrayBody.trim().length > 0;
39
-
40
- if (!isMultiline) {
41
- const trimmed = arrayBody.trim();
42
- const nextArrayBody = hasItems ? `${trimmed}, ${middlewareName}` : middlewareName;
43
- return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
44
- }
45
-
46
- if (!hasItems) {
47
- const baseIndent = arrayBody.match(/\n([ \t]*)$/)?.[1] ?? '\t';
48
- const nextArrayBody = `\n${baseIndent}${middlewareName}\n`;
49
- return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
50
- }
51
-
52
- const trimmedBody = arrayBody.trimEnd();
53
- const lastItemIndent = trimmedBody.match(/(?:^|\n)([ \t]*)\S[^\n]*$/)?.[1] ?? '\t';
54
- const nextArrayBody = `${trimmedBody},\n${lastItemIndent}${middlewareName}\n`;
55
-
56
- return content.replace(managerCallRegex, `createMiddlewareManager([${nextArrayBody}])`);
57
- };
58
-
59
- export default async function createMiddleware(): Promise<void> {
60
- const { middlewareNameRaw } = await prompts({
61
- type: 'text',
62
- name: 'middlewareNameRaw',
63
- message: 'Middleware name:',
64
- initial: 'RoutesGuard'
65
- });
66
-
67
- const middlewareName = toPascalCase(String(middlewareNameRaw ?? 'RoutesGuard')) || 'RoutesGuard';
68
- const middlewareManagerPath = path.join(process.cwd(), 'src', 'core', 'middleware-manager');
69
- const middlewaresPath = path.join(middlewareManagerPath, 'middlewares');
70
- const middlewareFilePath = path.join(middlewaresPath, `${middlewareName}.ts`);
71
- const middlewaresIndexPath = path.join(middlewaresPath, 'index.ts');
72
- const middlewareManagerFile = path.join(middlewareManagerPath, 'MiddlewareManager.ts');
73
-
74
- await fs.mkdir(middlewaresPath, { recursive: true });
75
-
76
- const created = await writeIfNotExists(middlewareFilePath, createMiddlewareTemplate(middlewareName));
77
- await updateIndexTs(middlewaresPath);
78
-
79
- if (!created) {
80
- console.log(`⚠️ Middleware "${middlewareName}" already exists. File was not overwritten.`);
81
- return;
82
- }
83
-
84
- let managerContent: string;
85
- try {
86
- managerContent = await fs.readFile(middlewareManagerFile, 'utf-8');
87
- } catch {
88
- console.log(`⚠️ Middleware manager not found at ${middlewareManagerFile}. Run "azure-net create middleware-manager" first.`);
89
- return;
90
- }
91
-
92
- const nextManagerContent = addMiddlewareToManager(managerContent, middlewareName);
93
- await fs.writeFile(middlewareManagerFile, nextManagerContent, 'utf-8');
94
-
95
- const hasIndex = await fs
96
- .access(middlewaresIndexPath)
97
- .then(() => true)
98
- .catch(() => false);
99
-
100
- console.log(`✅ Middleware created: ${middlewareFilePath}`);
101
- if (hasIndex) {
102
- console.log(`✅ Middlewares index updated: ${middlewaresIndexPath}`);
103
- }
104
- console.log(`✅ Middleware manager updated: ${middlewareManagerFile}`);
105
- }