proteum 1.0.3 → 2.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 (184) hide show
  1. package/AGENTS.md +92 -0
  2. package/agents/codex/AGENTS.md +95 -0
  3. package/agents/codex/CODING_STYLE.md +71 -0
  4. package/agents/codex/agents.md.zip +0 -0
  5. package/agents/codex/client/AGENTS.md +102 -0
  6. package/agents/codex/client/pages/AGENTS.md +35 -0
  7. package/agents/codex/server/routes/AGENTS.md +12 -0
  8. package/agents/codex/server/services/AGENTS.md +137 -0
  9. package/agents/codex/tests/AGENTS.md +8 -0
  10. package/cli/app/config.ts +12 -17
  11. package/cli/app/index.ts +59 -99
  12. package/cli/bin.js +1 -1
  13. package/cli/commands/build.ts +23 -12
  14. package/cli/commands/check.ts +19 -0
  15. package/cli/commands/deploy/app.ts +4 -8
  16. package/cli/commands/deploy/web.ts +16 -20
  17. package/cli/commands/dev.ts +185 -75
  18. package/cli/commands/devEvents.ts +106 -0
  19. package/cli/commands/init.ts +63 -57
  20. package/cli/commands/lint.ts +21 -0
  21. package/cli/commands/refresh.ts +6 -6
  22. package/cli/commands/typecheck.ts +18 -0
  23. package/cli/compiler/client/identite.ts +79 -49
  24. package/cli/compiler/client/index.ts +132 -214
  25. package/cli/compiler/common/bundleAnalysis.ts +94 -0
  26. package/cli/compiler/common/clientManifest.ts +67 -0
  27. package/cli/compiler/common/controllers.ts +288 -0
  28. package/cli/compiler/common/files/autres.ts +7 -18
  29. package/cli/compiler/common/files/images.ts +40 -37
  30. package/cli/compiler/common/files/style.ts +12 -25
  31. package/cli/compiler/common/generatedRouteModules.ts +368 -0
  32. package/cli/compiler/common/index.ts +29 -68
  33. package/cli/compiler/common/loaders/forbid-ssr-import.js +13 -0
  34. package/cli/compiler/common/rspackAliases.ts +13 -0
  35. package/cli/compiler/common/scripts.ts +37 -0
  36. package/cli/compiler/index.ts +764 -234
  37. package/cli/compiler/server/index.ts +52 -77
  38. package/cli/compiler/writeIfChanged.ts +21 -0
  39. package/cli/index.ts +65 -90
  40. package/cli/paths.ts +51 -57
  41. package/cli/print.ts +17 -11
  42. package/cli/tsconfig.json +5 -4
  43. package/cli/utils/agents.ts +100 -0
  44. package/cli/utils/check.ts +71 -0
  45. package/cli/utils/index.ts +1 -3
  46. package/cli/utils/keyboard.ts +8 -25
  47. package/cli/utils/runProcess.ts +30 -0
  48. package/client/app/component.tsx +29 -29
  49. package/client/app/index.ts +36 -57
  50. package/client/app/service.ts +7 -12
  51. package/client/app.tsconfig.json +2 -2
  52. package/client/components/Dialog/Manager.ssr.tsx +40 -0
  53. package/client/components/Dialog/Manager.tsx +119 -150
  54. package/client/components/Dialog/status.tsx +3 -3
  55. package/client/components/index.ts +1 -1
  56. package/client/components/types.d.ts +1 -3
  57. package/client/dev/hmr.ts +65 -0
  58. package/client/global.d.ts +2 -2
  59. package/client/hooks.ts +6 -9
  60. package/client/index.ts +2 -1
  61. package/client/islands/index.ts +7 -0
  62. package/client/islands/useDeferredModule.ts +199 -0
  63. package/client/pages/_layout/index.tsx +4 -12
  64. package/client/pages/useHeader.tsx +14 -21
  65. package/client/router.ts +27 -0
  66. package/client/services/router/components/Link.tsx +34 -27
  67. package/client/services/router/components/Page.tsx +6 -14
  68. package/client/services/router/components/router.ssr.tsx +36 -0
  69. package/client/services/router/components/router.tsx +63 -83
  70. package/client/services/router/index.tsx +185 -220
  71. package/client/services/router/request/api.ts +97 -119
  72. package/client/services/router/request/history.ts +2 -2
  73. package/client/services/router/request/index.ts +13 -12
  74. package/client/services/router/request/multipart.ts +72 -62
  75. package/client/services/router/response/index.tsx +68 -61
  76. package/client/services/router/response/page.ts +28 -32
  77. package/client/utils/dom.ts +17 -33
  78. package/common/app/index.ts +3 -3
  79. package/common/data/chaines/index.ts +22 -23
  80. package/common/data/dates.ts +35 -70
  81. package/common/data/markdown.ts +42 -39
  82. package/common/dev/serverHotReload.ts +26 -0
  83. package/common/errors/index.tsx +110 -142
  84. package/common/router/contracts.ts +29 -0
  85. package/common/router/index.ts +89 -108
  86. package/common/router/layouts.ts +34 -47
  87. package/common/router/pageSetup.ts +50 -0
  88. package/common/router/register.ts +53 -24
  89. package/common/router/request/api.ts +30 -36
  90. package/common/router/request/index.ts +2 -8
  91. package/common/router/response/index.ts +8 -15
  92. package/common/router/response/page.ts +70 -58
  93. package/common/utils.ts +1 -1
  94. package/eslint.js +62 -0
  95. package/package.json +12 -45
  96. package/prettier.config.cjs +9 -0
  97. package/scripts/cleanup-generated-controllers.ts +62 -0
  98. package/scripts/fix-reference-app-typing.ts +490 -0
  99. package/scripts/refactor-client-app-imports.ts +244 -0
  100. package/scripts/refactor-client-pages.ts +587 -0
  101. package/scripts/refactor-server-controllers.ts +470 -0
  102. package/scripts/refactor-server-runtime-aliases.ts +360 -0
  103. package/scripts/restore-client-app-import-files.ts +41 -0
  104. package/scripts/restore-files-from-git-head.ts +20 -0
  105. package/scripts/update-codex-agents.ts +35 -0
  106. package/server/app/commands.ts +35 -64
  107. package/server/app/container/config.ts +39 -69
  108. package/server/app/container/console/index.ts +202 -248
  109. package/server/app/container/index.ts +33 -71
  110. package/server/app/controller/index.ts +61 -0
  111. package/server/app/index.ts +39 -105
  112. package/server/app/service/container.ts +41 -42
  113. package/server/app/service/index.ts +120 -147
  114. package/server/context.ts +1 -1
  115. package/server/index.ts +25 -1
  116. package/server/services/auth/index.ts +75 -115
  117. package/server/services/auth/router/index.ts +31 -32
  118. package/server/services/auth/router/request.ts +14 -16
  119. package/server/services/cron/CronTask.ts +13 -26
  120. package/server/services/cron/index.ts +14 -36
  121. package/server/services/disks/driver.ts +40 -58
  122. package/server/services/disks/drivers/local/index.ts +79 -90
  123. package/server/services/disks/drivers/s3/index.ts +116 -163
  124. package/server/services/disks/index.ts +23 -38
  125. package/server/services/email/index.ts +45 -104
  126. package/server/services/email/utils.ts +14 -27
  127. package/server/services/fetch/index.ts +53 -85
  128. package/server/services/prisma/Facet.ts +39 -91
  129. package/server/services/prisma/index.ts +74 -110
  130. package/server/services/router/generatedRuntime.ts +29 -0
  131. package/server/services/router/http/index.ts +77 -72
  132. package/server/services/router/http/multipart.ts +19 -42
  133. package/server/services/router/index.ts +378 -365
  134. package/server/services/router/request/api.ts +26 -25
  135. package/server/services/router/request/index.ts +44 -51
  136. package/server/services/router/request/service.ts +7 -11
  137. package/server/services/router/request/validation/zod.ts +111 -148
  138. package/server/services/router/response/index.ts +110 -125
  139. package/server/services/router/response/mask/Filter.ts +31 -72
  140. package/server/services/router/response/mask/index.ts +8 -15
  141. package/server/services/router/response/mask/selecteurs.ts +11 -25
  142. package/server/services/router/response/page/clientManifest.ts +25 -0
  143. package/server/services/router/response/page/document.tsx +199 -127
  144. package/server/services/router/response/page/index.tsx +89 -94
  145. package/server/services/router/service.ts +13 -15
  146. package/server/services/schema/index.ts +17 -26
  147. package/server/services/schema/request.ts +19 -33
  148. package/server/services/schema/router/index.ts +8 -11
  149. package/server/services/security/encrypt/aes/index.ts +15 -35
  150. package/server/utils/slug.ts +29 -32
  151. package/skills/clean-project-code/SKILL.md +63 -0
  152. package/skills/clean-project-code/agents/openai.yaml +4 -0
  153. package/tsconfig.common.json +4 -3
  154. package/tsconfig.json +4 -1
  155. package/types/aliases.d.ts +17 -21
  156. package/types/controller-input.test.ts +48 -0
  157. package/types/express-extra.d.ts +6 -0
  158. package/types/global/constants.d.ts +1 -0
  159. package/types/global/express-extra.d.ts +6 -0
  160. package/types/global/modules.d.ts +13 -16
  161. package/types/global/utils.d.ts +17 -49
  162. package/types/global/vendors.d.ts +62 -0
  163. package/types/icons.d.ts +65 -1
  164. package/types/uuid.d.ts +3 -0
  165. package/types/vendors.d.ts +62 -0
  166. package/cli/compiler/common/babel/index.ts +0 -173
  167. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  168. package/cli/compiler/common/babel/plugins/services.ts +0 -586
  169. package/cli/compiler/common/babel/routes/imports.ts +0 -127
  170. package/cli/compiler/common/babel/routes/routes.ts +0 -1170
  171. package/client/services/captcha/index.ts +0 -67
  172. package/client/services/socket/index.ts +0 -147
  173. package/common/data/rte/nodes.ts +0 -83
  174. package/common/data/stats.ts +0 -90
  175. package/common/utils/rte.ts +0 -183
  176. package/server/services/auth/old.ts +0 -277
  177. package/server/services/cache/commands.ts +0 -41
  178. package/server/services/cache/index.ts +0 -297
  179. package/server/services/cache/service.json +0 -6
  180. package/server/services/socket/index.ts +0 -162
  181. package/server/services/socket/scope.ts +0 -226
  182. package/server/services/socket/service.json +0 -6
  183. package/server/services_old/SocketClient.ts +0 -92
  184. package/server/services_old/Token.old.ts +0 -97
@@ -9,116 +9,226 @@ import { spawn, ChildProcess } from 'child_process';
9
9
  // Cor elibs
10
10
  import cli from '..';
11
11
  import Keyboard from '../utils/keyboard';
12
+ import {
13
+ isServerHotReloadResult,
14
+ serverHotReloadMessageType,
15
+ TServerHotReloadRequest,
16
+ } from '../../common/dev/serverHotReload';
12
17
 
13
18
  // Configs
14
19
  import Compiler from '../compiler';
20
+ import { createDevEventServer } from './devEvents';
21
+ import { ensureProjectAgentSymlinks } from '../utils/agents';
15
22
 
16
23
  // Core
17
24
  import { app, App } from '../app';
18
25
 
26
+ /*----------------------------------
27
+ - CONSTANTS
28
+ ----------------------------------*/
29
+
30
+ // Watch rules shared by the dev compiler and hot reload gate.
19
31
  const ignoredWatchPathPatterns = /(node_modules\/(?!proteum\/))|(\.generated\/)|(\.cache\/)/;
32
+ const hotReloadableServerPathPatterns = [
33
+ /^client\/pages\//,
34
+ /^client\/components\//,
35
+ /^client\/islands\//,
36
+ /^server\/routes\//,
37
+ /^server\/services\/.+\.controller\.[jt]sx?$/,
38
+ ];
39
+ const hotReloadableRoots = [() => app.paths.root, () => cli.paths.core.root];
20
40
 
21
41
  /*----------------------------------
22
- - COMMANDE
42
+ - MAIN PROCESS
23
43
  ----------------------------------*/
24
- export const run = () => new Promise<void>(async () => {
25
-
26
- const compiler = new Compiler('dev', {
27
- before: (compiler) => {
44
+ export const run = () =>
45
+ new Promise<void>(async () => {
46
+ ensureProjectAgentSymlinks({ appRoot: app.paths.root, coreRoot: cli.paths.core.root });
47
+
48
+ const devEventServer = await createDevEventServer(app.env.router.port + 1);
49
+ app.devEventPort = devEventServer.port;
50
+ console.info(`Proteum dev event server ready on http://localhost:${devEventServer.port}/__proteum_hmr`);
51
+
52
+ const compiler = new Compiler('dev', {
53
+ before: (compiler) => {
54
+ if (compiler.name !== 'server') return;
55
+
56
+ const changedFilesList = compiler.modifiedFiles ? [...compiler.modifiedFiles] : [];
57
+
58
+ if (changedFilesList.length === 0) {
59
+ console.info('Server compilation started. App restart will wait for a successful server build.');
60
+ } else {
61
+ console.info('Need to recompile server because files changed:\n' + changedFilesList.join('\n'));
62
+ }
63
+ },
64
+ after: () => {},
65
+ });
66
+
67
+ const multiCompiler = await compiler.create();
68
+ const ignoredOutputPaths = [app.paths.bin, app.paths.dev].map(normalizeWatchPath);
69
+
70
+ multiCompiler.watch(
71
+ {
72
+ // Watching may not work with NFS and machines in VirtualBox
73
+ // Uncomment next line if it is your case (use true or interval in milliseconds)
74
+ //poll: 1000,
75
+
76
+ // Decrease CPU or memory usage in some file systems
77
+ // Ignore updated from:
78
+ // - Node modules except 5HTP core (framework dev mode)
79
+ // - Generated files during runtime (cause infinite loop. Ex: models.d.ts)
80
+ // - Webpack output folders (`./dev`, legacy `./bin`)
81
+ ignored: (watchPath: string) => {
82
+ const normalizedPath = normalizeWatchPath(watchPath);
83
+ return (
84
+ ignoredWatchPathPatterns.test(normalizedPath) ||
85
+ ignoredOutputPaths.some(
86
+ (outputPath) =>
87
+ normalizedPath === outputPath || normalizedPath.startsWith(outputPath + '/'),
88
+ )
89
+ );
90
+ },
91
+
92
+ //aggregateTimeout: 1000,
93
+ },
94
+ async (error, stats) => {
95
+ if (error) {
96
+ compiler.consumeRecentCompilationResults();
97
+ console.error('Error in milticompiler.watch', error, stats?.toString());
98
+ return;
99
+ }
100
+
101
+ const recentCompilationResults = compiler.consumeRecentCompilationResults();
102
+ const serverResult = recentCompilationResults.server;
103
+ const clientResult = recentCompilationResults.client;
104
+
105
+ let restartedServer = false;
106
+
107
+ if (serverResult?.succeeded === true) {
108
+ const changedFilesList = serverResult.modifiedFiles || [];
109
+ const canHotReloadServer = isServerHotReloadEligible(changedFilesList);
110
+
111
+ if (canHotReloadServer && requestServerHotReload(changedFilesList)) {
112
+ console.log(
113
+ 'Watch callback. Server route bundle changed; hot-swapping generated routes without restarting app.',
114
+ );
115
+ } else {
116
+ console.log('Watch callback. Reloading app because server bundle changed ...');
117
+ startApp(app);
118
+ restartedServer = true;
119
+ devEventServer.broadcast({ type: 'reload', reason: 'server' });
120
+ }
121
+ }
122
+
123
+ if (serverResult?.succeeded === false) {
124
+ console.log('Watch callback. Server compilation failed; keeping current app instance.');
125
+ }
126
+
127
+ if (!restartedServer && clientResult?.succeeded === true) {
128
+ console.log('Watch callback. Client assets updated; server restart skipped.');
129
+ devEventServer.broadcast({ type: 'reload', reason: 'client' });
130
+ return;
131
+ }
132
+
133
+ if (!restartedServer && clientResult?.succeeded === false) {
134
+ console.log('Watch callback. Client compilation failed; server restart skipped.');
135
+ return;
136
+ }
137
+
138
+ if (restartedServer || serverResult?.succeeded === true || serverResult?.succeeded === false) {
139
+ return;
140
+ }
141
+
142
+ console.log('Watch callback. No compiler changes were tracked.');
143
+ },
144
+ );
145
+
146
+ Keyboard.input('ctrl+r', async () => {
147
+ console.log('Waiting for compilers to be ready ...', Object.keys(compiler.compiling));
148
+ await Promise.all(Object.values(compiler.compiling));
149
+
150
+ console.log('Reloading app ...');
151
+ startApp(app);
152
+ devEventServer.broadcast({ type: 'reload', reason: 'manual' });
153
+ });
154
+
155
+ Keyboard.input('ctrl+c', () => {
156
+ stopApp('CTRL+C Pressed');
157
+ void devEventServer.close();
158
+ });
159
+ });
28
160
 
29
- const changedFilesList = compiler.modifiedFiles ? [...compiler.modifiedFiles] : [];
161
+ /*----------------------------------
162
+ - STATE
163
+ ----------------------------------*/
30
164
 
31
- if (changedFilesList.length === 0)
32
- stopApp("Starting a new compilation");
33
- else
34
- stopApp("Need to recompile because files changed:\n" + changedFilesList.join('\n'));
165
+ // Current server child process used by the dev loop.
166
+ let cp: ChildProcess | undefined = undefined;
35
167
 
36
- },
37
- after: () => {
168
+ /*----------------------------------
169
+ - HELPERS
170
+ ----------------------------------*/
38
171
 
172
+ async function startApp(app: App) {
173
+ stopApp('Restart asked');
39
174
 
40
- }
175
+ console.info('Launching new server ...');
176
+ cp = spawn('node', ['--preserve-symlinks', app.outputPath('dev') + '/server.js'], {
177
+ // sdin, sdout, sderr
178
+ stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
41
179
  });
42
180
 
43
- const multiCompiler = await compiler.create();
44
- const ignoredOutputPaths = [app.paths.bin, app.paths.dev].map(normalizeWatchPath);
45
-
46
- multiCompiler.watch({
47
-
48
- // https://webpack.js.org/configuration/watch/#watchoptions
49
- // Watching may not work with NFS and machines in VirtualBox
50
- // Uncomment next line if it is your case (use true or interval in milliseconds)
51
- //poll: 1000,
52
-
53
- // Decrease CPU or memory usage in some file systems
54
- // Ignore updated from:
55
- // - Node modules except 5HTP core (framework dev mode)
56
- // - Generated files during runtime (cause infinite loop. Ex: models.d.ts)
57
- // - Webpack output folders (`./dev`, legacy `./bin`)
58
- ignored: (watchPath: string) => {
59
- const normalizedPath = normalizeWatchPath(watchPath);
60
- return ignoredWatchPathPatterns.test(normalizedPath)
61
- || ignoredOutputPaths.some(outputPath =>
62
- normalizedPath === outputPath
63
- || normalizedPath.startsWith(outputPath + '/')
64
- );
65
- }
181
+ cp.on('message', (message: unknown) => {
182
+ if (!isServerHotReloadResult(message)) return;
66
183
 
67
- //aggregateTimeout: 1000,
68
- }, async (error, stats) => {
69
-
70
- if (error) {
71
- console.error(`Error in milticompiler.watch`, error, stats?.toString());
184
+ if (message.type === serverHotReloadMessageType.succeeded) {
185
+ console.log('Server hot reload applied without restarting app.');
72
186
  return;
73
187
  }
74
188
 
75
- console.log("Watch callback. Reloading app ...");
76
- startApp(app);
77
-
78
- });
79
-
80
- Keyboard.input('ctrl+r', async () => {
81
-
82
- console.log(`Waiting for compilers to be ready ...`, Object.keys(compiler.compiling));
83
- await Promise.all(Object.values(compiler.compiling));
84
-
85
- console.log(`Reloading app ...`);
189
+ console.error('Server hot reload failed. Restarting app with a fresh process.', message.error || '');
86
190
  startApp(app);
87
-
88
191
  });
192
+ }
89
193
 
90
- Keyboard.input('ctrl+c', () => {
91
- stopApp("CTRL+C Pressed");
92
- });
93
- });
194
+ function stopApp(reason: string) {
195
+ if (cp !== undefined) {
196
+ console.info(`Killing current server instance (ID: ${cp.pid}) for the following reason:`, reason);
197
+ cp.kill();
198
+ cp = undefined;
199
+ }
200
+ }
94
201
 
202
+ function requestServerHotReload(changedFiles: string[]) {
203
+ if (!cp || !cp.connected) return false;
95
204
 
96
- /*----------------------------------
97
- - APP RUN
98
- ----------------------------------*/
99
- let cp: ChildProcess | undefined = undefined;
205
+ const message: TServerHotReloadRequest = { type: serverHotReloadMessageType.request, changedFiles };
100
206
 
101
- async function startApp( app: App ) {
207
+ cp.send(message);
208
+ return true;
209
+ }
102
210
 
103
- stopApp('Restart asked');
211
+ function isServerHotReloadEligible(changedFiles: string[]) {
212
+ if (changedFiles.length === 0) return false;
104
213
 
105
- console.info(`Launching new server ...`);
106
- cp = spawn('node', ['' + app.outputPath('dev') + '/server.js', '--preserve-symlinks'], {
214
+ return changedFiles.every((changedFile) => {
215
+ const normalizedChangedFile = normalizeWatchPath(changedFile);
107
216
 
108
- // sdin, sdout, sderr
109
- stdio: ['inherit', 'inherit', 'inherit']
217
+ return hotReloadableRoots.some((getRootPath) => {
218
+ const normalizedRootPath = normalizeWatchPath(getRootPath());
219
+ if (
220
+ normalizedChangedFile !== normalizedRootPath &&
221
+ !normalizedChangedFile.startsWith(normalizedRootPath + '/')
222
+ ) {
223
+ return false;
224
+ }
110
225
 
226
+ const relativePath = normalizedChangedFile.substring(normalizedRootPath.length + 1);
227
+ return hotReloadableServerPathPatterns.some((pattern) => pattern.test(relativePath));
228
+ });
111
229
  });
112
230
  }
113
231
 
114
- function stopApp( reason: string ) {
115
- if (cp !== undefined) {
116
- console.info(`Killing current server instance (ID: ${cp.pid}) for the following reason:`, reason);
117
- cp.kill();
118
- }
119
-
120
- }
121
-
122
232
  function normalizeWatchPath(watchPath: string) {
123
233
  return path.resolve(watchPath).replace(/\\/g, '/').replace(/\/$/, '');
124
234
  }
@@ -0,0 +1,106 @@
1
+ import http, { type IncomingMessage, type ServerResponse } from 'http';
2
+
3
+ export type TDevEvent = { type: 'reload'; reason: 'server' | 'manual' | 'client' };
4
+
5
+ const devEventsPath = '/__proteum_hmr';
6
+
7
+ export class DevEventServer {
8
+ private readonly clients = new Set<ServerResponse<IncomingMessage>>();
9
+ private readonly server = http.createServer(this.handleRequest.bind(this));
10
+
11
+ public constructor(public port: number) {}
12
+
13
+ public broadcast(event: TDevEvent) {
14
+ const payload = `data: ${JSON.stringify(event)}\n\n`;
15
+
16
+ for (const client of this.clients) {
17
+ client.write(payload);
18
+ }
19
+ }
20
+
21
+ public async close() {
22
+ for (const client of this.clients) {
23
+ client.end();
24
+ }
25
+ this.clients.clear();
26
+
27
+ await new Promise<void>((resolve, reject) => {
28
+ this.server.close((error) => {
29
+ if (error) {
30
+ reject(error);
31
+ return;
32
+ }
33
+
34
+ resolve();
35
+ });
36
+ });
37
+ }
38
+
39
+ private handleRequest(request: IncomingMessage, response: ServerResponse<IncomingMessage>) {
40
+ if (request.url !== devEventsPath) {
41
+ response.statusCode = 404;
42
+ response.end();
43
+ return;
44
+ }
45
+
46
+ response.writeHead(200, {
47
+ 'Access-Control-Allow-Origin': '*',
48
+ 'Cache-Control': 'no-store',
49
+ Connection: 'keep-alive',
50
+ 'Content-Type': 'text/event-stream',
51
+ 'X-Accel-Buffering': 'no',
52
+ });
53
+ response.write(': connected\n\n');
54
+ response.socket?.setKeepAlive(true);
55
+ this.clients.add(response);
56
+
57
+ request.on('close', () => {
58
+ this.clients.delete(response);
59
+ response.end();
60
+ });
61
+ }
62
+
63
+ public static async create(preferredPort: number) {
64
+ const server = new DevEventServer(preferredPort);
65
+ server.port = await server.listen(preferredPort);
66
+ return server;
67
+ }
68
+
69
+ private async listen(preferredPort: number) {
70
+ const initialPort = Number.isInteger(preferredPort) ? preferredPort : 0;
71
+
72
+ return await new Promise<number>((resolve, reject) => {
73
+ const tryListen = (port: number) => {
74
+ const onError = (error: NodeJS.ErrnoException) => {
75
+ this.server.off('listening', onListening);
76
+
77
+ if (error.code === 'EADDRINUSE' && port !== 0) {
78
+ tryListen(port + 1);
79
+ return;
80
+ }
81
+
82
+ reject(error);
83
+ };
84
+
85
+ const onListening = () => {
86
+ this.server.off('error', onError);
87
+ const address = this.server.address();
88
+ if (!address || typeof address === 'string') {
89
+ reject(new Error('Unable to resolve the dev event server port.'));
90
+ return;
91
+ }
92
+
93
+ resolve(address.port);
94
+ };
95
+
96
+ this.server.once('error', onError);
97
+ this.server.once('listening', onListening);
98
+ this.server.listen(port, '0.0.0.0');
99
+ };
100
+
101
+ tryListen(initialPort);
102
+ });
103
+ }
104
+ }
105
+
106
+ export const createDevEventServer = async (preferredPort: number) => DevEventServer.create(preferredPort);
@@ -11,75 +11,81 @@ import replaceOnce from 'replace-once';
11
11
 
12
12
  // Cor elibs
13
13
  import cli from '..';
14
+ import { ensureProjectAgentSymlinks } from '../utils/agents';
14
15
 
15
16
  // Configs
16
- const filesToConfig = [
17
- 'package.json',
18
- 'identity.yaml'
19
- ]
17
+ const filesToConfig = ['package.json', 'identity.yaml'];
20
18
 
21
19
  /*----------------------------------
22
20
  - COMMANDE
23
21
  ----------------------------------*/
24
- export const run = () => new Promise<void>(async () => {
25
-
26
- const config = await prompts([{
27
- type: 'text', name: 'name',
28
- message: 'Project name ?',
29
- initial: "MyProject",
30
- validate: value => /[a-z0-9\-\.]/i.test(value) || "Must only include alphanumeric characters, and - . "
31
- },{
32
- type: 'text', name: 'dirname',
33
- message: 'Folder name ?',
34
- initial: value => value.toLowerCase(),
35
- validate: value => /[a-z0-9\-\.]/.test(value) || "Must only include lowercase alphanumeric characters, and - . "
36
- },{
37
- type: 'text', name: 'description',
38
- message: 'Briefly describe your project to your mom:',
39
- initial: "It will revolutionnize the world",
40
- validate: value => /[a-z0-9\-\. ]/i.test(value) || "Must only include alphanumeric characters, and - . "
41
- },{
42
- type: 'toggle', name: 'microservice',
43
- message: 'Separate API from the UI servers ?'
44
- }]);
22
+ export const run = () =>
23
+ new Promise<void>(async () => {
24
+ const config = await prompts([
25
+ {
26
+ type: 'text',
27
+ name: 'name',
28
+ message: 'Project name ?',
29
+ initial: 'MyProject',
30
+ validate: (value) =>
31
+ /[a-z0-9\-\.]/i.test(value) || 'Must only include alphanumeric characters, and - . ',
32
+ },
33
+ {
34
+ type: 'text',
35
+ name: 'dirname',
36
+ message: 'Folder name ?',
37
+ initial: (value) => value.toLowerCase(),
38
+ validate: (value) =>
39
+ /[a-z0-9\-\.]/.test(value) || 'Must only include lowercase alphanumeric characters, and - . ',
40
+ },
41
+ {
42
+ type: 'text',
43
+ name: 'description',
44
+ message: 'Briefly describe your project to your mom:',
45
+ initial: 'It will revolutionnize the world',
46
+ validate: (value) =>
47
+ /[a-z0-9\-\. ]/i.test(value) || 'Must only include alphanumeric characters, and - . ',
48
+ },
49
+ { type: 'toggle', name: 'microservice', message: 'Separate API from the UI servers ?' },
50
+ ]);
45
51
 
46
- const placeholders = {
47
- PROJECT_NAME: config.name,
48
- PACKAGE_NAME: config.name.toLowerCase(),
49
- PROJECT_DESCRIPTION: config.description
50
- }
52
+ const placeholders = {
53
+ PROJECT_NAME: config.name,
54
+ PACKAGE_NAME: config.name.toLowerCase(),
55
+ PROJECT_DESCRIPTION: config.description,
56
+ };
51
57
 
52
- const paths = {
53
- skeleton: path.join( cli.paths.core.cli, 'skeleton'),
54
- project: path.join( process.cwd(), config.dirname)
55
- }
58
+ const paths = {
59
+ skeleton: path.join(cli.paths.core.cli, 'skeleton'),
60
+ project: path.join(process.cwd(), config.dirname),
61
+ };
56
62
 
57
- // Copy skeleton to cwd/<project-name>
58
- console.info("Creating project skeleton ...");
59
- fs.copySync( paths.skeleton, paths.project );
60
-
61
- // Replace placeholders
62
- console.info("Configuring project ...");
63
- for (const file of filesToConfig) {
64
- console.log('- ' + file);
63
+ // Copy skeleton to cwd/<project-name>
64
+ console.info('Creating project skeleton ...');
65
+ fs.copySync(paths.skeleton, paths.project);
65
66
 
66
- const filepath = path.join( paths.project, file )
67
- const content = fs.readFileSync(filepath, 'utf-8');
67
+ // Sync framework-owned Codex assets into the new project.
68
+ ensureProjectAgentSymlinks({ appRoot: paths.project, coreRoot: cli.paths.core.root });
68
69
 
69
- const placeholders_keys = Object.keys(placeholders).map(k => '{{ ' + k + ' }}')
70
- const values = Object.values(placeholders);
70
+ // Replace placeholders
71
+ console.info('Configuring project ...');
72
+ for (const file of filesToConfig) {
73
+ console.log('- ' + file);
71
74
 
72
- fs.writeFileSync(filepath,
73
- replaceOnce(content, placeholders_keys, values)
74
- );
75
- }
75
+ const filepath = path.join(paths.project, file);
76
+ const content = fs.readFileSync(filepath, 'utf-8');
76
77
 
77
- // Npm install
78
- console.info("Installing packages ...");
79
- cmd.runSync(`cd "${paths.project}" && npm i`);
78
+ const placeholders_keys = Object.keys(placeholders).map((k) => '{{ ' + k + ' }}');
79
+ const values = Object.values(placeholders);
80
80
 
81
- // Run demo app
82
- /*console.info("Run demo ...");
83
- await cli.shell('5htp dev');*/
81
+ fs.writeFileSync(filepath, replaceOnce(content, placeholders_keys, values));
82
+ }
83
+
84
+ // Npm install
85
+ console.info('Installing packages ...');
86
+ cmd.runSync(`cd "${paths.project}" && npm i`);
84
87
 
85
- });
88
+ // Run demo app
89
+ /*console.info("Run demo ...");
90
+ await cli.shell('5htp dev');*/
91
+ });
@@ -0,0 +1,21 @@
1
+ import cli from '..';
2
+ import { runAppLint } from '../utils/check';
3
+
4
+ const allowedLintArgs = new Set(['fix']);
5
+
6
+ const validateLintArgs = () => {
7
+ const enabledArgs = Object.entries(cli.args)
8
+ .filter(([name, value]) => name !== 'workdir' && value === true)
9
+ .map(([name]) => name);
10
+
11
+ const invalidArgs = enabledArgs.filter((arg) => !allowedLintArgs.has(arg));
12
+
13
+ if (invalidArgs.length > 0)
14
+ throw new Error(`Unknown lint argument(s): ${invalidArgs.join(', ')}. Allowed values: fix.`);
15
+ };
16
+
17
+ export const run = async (): Promise<void> => {
18
+ validateLintArgs();
19
+
20
+ await runAppLint({ fix: cli.args.fix === true });
21
+ };
@@ -8,11 +8,11 @@ import Compiler from '../compiler';
8
8
  /*----------------------------------
9
9
  - COMMAND
10
10
  ----------------------------------*/
11
- export const run = (): Promise<void> => new Promise(async (resolve) => {
11
+ export const run = (): Promise<void> =>
12
+ new Promise(async (resolve) => {
13
+ const compiler = new Compiler('dev');
12
14
 
13
- const compiler = new Compiler('dev');
15
+ await compiler.refreshGeneratedTypings();
14
16
 
15
- await compiler.refreshGeneratedTypings();
16
-
17
- resolve();
18
- });
17
+ resolve();
18
+ });
@@ -0,0 +1,18 @@
1
+ import cli from '..';
2
+ import { refreshGeneratedTypings, runAppTypecheck } from '../utils/check';
3
+
4
+ const validateTypecheckArgs = () => {
5
+ const enabledArgs = Object.entries(cli.args).filter(([name, value]) => name !== 'workdir' && value === true);
6
+
7
+ if (enabledArgs.length > 0)
8
+ throw new Error(
9
+ `Unknown typecheck argument(s): ${enabledArgs.map(([name]) => name).join(', ')}. This command does not accept options.`,
10
+ );
11
+ };
12
+
13
+ export const run = async (): Promise<void> => {
14
+ validateTypecheckArgs();
15
+
16
+ await refreshGeneratedTypings();
17
+ await runAppTypecheck();
18
+ };