proteum 1.0.2 → 2.0.0-1

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 (185) hide show
  1. package/AGENTS.md +101 -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 +13 -11
  11. package/cli/app/index.ts +74 -82
  12. package/cli/bin.js +1 -1
  13. package/cli/commands/build.ts +51 -14
  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 +189 -64
  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 +18 -0
  22. package/cli/commands/typecheck.ts +18 -0
  23. package/cli/compiler/client/identite.ts +80 -53
  24. package/cli/compiler/client/index.ts +139 -213
  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 +11 -22
  31. package/cli/compiler/common/generatedRouteModules.ts +368 -0
  32. package/cli/compiler/common/index.ts +31 -65
  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 +781 -230
  37. package/cli/compiler/server/index.ts +59 -75
  38. package/cli/compiler/writeIfChanged.ts +21 -0
  39. package/cli/index.ts +71 -72
  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/doc/TODO.md +1 -1
  95. package/eslint.js +62 -0
  96. package/package.json +14 -49
  97. package/prettier.config.cjs +9 -0
  98. package/scripts/cleanup-generated-controllers.ts +62 -0
  99. package/scripts/fix-reference-app-typing.ts +490 -0
  100. package/scripts/refactor-client-app-imports.ts +244 -0
  101. package/scripts/refactor-client-pages.ts +587 -0
  102. package/scripts/refactor-server-controllers.ts +470 -0
  103. package/scripts/refactor-server-runtime-aliases.ts +360 -0
  104. package/scripts/restore-client-app-import-files.ts +41 -0
  105. package/scripts/restore-files-from-git-head.ts +20 -0
  106. package/scripts/update-codex-agents.ts +35 -0
  107. package/server/app/commands.ts +35 -64
  108. package/server/app/container/config.ts +48 -59
  109. package/server/app/container/console/index.ts +202 -248
  110. package/server/app/container/index.ts +33 -71
  111. package/server/app/controller/index.ts +61 -0
  112. package/server/app/index.ts +39 -105
  113. package/server/app/service/container.ts +41 -42
  114. package/server/app/service/index.ts +120 -147
  115. package/server/context.ts +1 -1
  116. package/server/index.ts +25 -1
  117. package/server/services/auth/index.ts +75 -115
  118. package/server/services/auth/router/index.ts +31 -32
  119. package/server/services/auth/router/request.ts +14 -16
  120. package/server/services/cron/CronTask.ts +13 -26
  121. package/server/services/cron/index.ts +14 -36
  122. package/server/services/disks/driver.ts +40 -58
  123. package/server/services/disks/drivers/local/index.ts +79 -90
  124. package/server/services/disks/drivers/s3/index.ts +116 -163
  125. package/server/services/disks/index.ts +23 -38
  126. package/server/services/email/index.ts +45 -104
  127. package/server/services/email/utils.ts +14 -27
  128. package/server/services/fetch/index.ts +53 -85
  129. package/server/services/prisma/Facet.ts +39 -91
  130. package/server/services/prisma/index.ts +74 -110
  131. package/server/services/router/generatedRuntime.ts +29 -0
  132. package/server/services/router/http/index.ts +78 -73
  133. package/server/services/router/http/multipart.ts +19 -42
  134. package/server/services/router/index.ts +378 -365
  135. package/server/services/router/request/api.ts +26 -25
  136. package/server/services/router/request/index.ts +44 -51
  137. package/server/services/router/request/service.ts +7 -11
  138. package/server/services/router/request/validation/zod.ts +111 -148
  139. package/server/services/router/response/index.ts +110 -125
  140. package/server/services/router/response/mask/Filter.ts +31 -72
  141. package/server/services/router/response/mask/index.ts +8 -15
  142. package/server/services/router/response/mask/selecteurs.ts +11 -25
  143. package/server/services/router/response/page/clientManifest.ts +25 -0
  144. package/server/services/router/response/page/document.tsx +199 -127
  145. package/server/services/router/response/page/index.tsx +89 -94
  146. package/server/services/router/service.ts +13 -15
  147. package/server/services/schema/index.ts +17 -26
  148. package/server/services/schema/request.ts +19 -33
  149. package/server/services/schema/router/index.ts +8 -11
  150. package/server/services/security/encrypt/aes/index.ts +15 -35
  151. package/server/utils/slug.ts +29 -35
  152. package/skills/clean-project-code/SKILL.md +63 -0
  153. package/skills/clean-project-code/agents/openai.yaml +4 -0
  154. package/tsconfig.common.json +4 -3
  155. package/tsconfig.json +4 -1
  156. package/types/aliases.d.ts +17 -21
  157. package/types/controller-input.test.ts +48 -0
  158. package/types/express-extra.d.ts +6 -0
  159. package/types/global/constants.d.ts +13 -0
  160. package/types/global/express-extra.d.ts +6 -0
  161. package/types/global/modules.d.ts +13 -16
  162. package/types/global/utils.d.ts +17 -49
  163. package/types/global/vendors.d.ts +62 -0
  164. package/types/icons.d.ts +65 -1
  165. package/types/uuid.d.ts +3 -0
  166. package/types/vendors.d.ts +62 -0
  167. package/cli/compiler/common/babel/index.ts +0 -170
  168. package/cli/compiler/common/babel/plugins/index.ts +0 -0
  169. package/cli/compiler/common/babel/plugins/services.ts +0 -586
  170. package/cli/compiler/common/babel/routes/imports.ts +0 -127
  171. package/cli/compiler/common/babel/routes/routes.ts +0 -1130
  172. package/client/services/captcha/index.ts +0 -67
  173. package/client/services/socket/index.ts +0 -147
  174. package/common/data/rte/nodes.ts +0 -83
  175. package/common/data/stats.ts +0 -90
  176. package/common/utils/rte.ts +0 -183
  177. package/server/services/auth/old.ts +0 -277
  178. package/server/services/cache/commands.ts +0 -41
  179. package/server/services/cache/index.ts +0 -297
  180. package/server/services/cache/service.json +0 -6
  181. package/server/services/socket/index.ts +0 -162
  182. package/server/services/socket/scope.ts +0 -226
  183. package/server/services/socket/service.json +0 -6
  184. package/server/services_old/SocketClient.ts +0 -92
  185. package/server/services_old/Token.old.ts +0 -97
@@ -3,107 +3,232 @@
3
3
  ----------------------------------*/
4
4
 
5
5
  // Npm
6
- import fs from 'fs-extra';
6
+ import path from 'path';
7
7
  import { spawn, ChildProcess } from 'child_process';
8
8
 
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
 
19
26
  /*----------------------------------
20
- - COMMANDE
27
+ - CONSTANTS
21
28
  ----------------------------------*/
22
- export const run = () => new Promise<void>(async () => {
23
29
 
24
- const compiler = new Compiler('dev', {
25
- before: (compiler) => {
30
+ // Watch rules shared by the dev compiler and hot reload gate.
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];
26
40
 
27
- const changedFilesList = compiler.modifiedFiles ? [...compiler.modifiedFiles] : [];
28
-
29
- if (changedFilesList.length === 0)
30
- stopApp("Starting a new compilation");
31
- else
32
- stopApp("Need to recompile because files changed:\n" + changedFilesList.join('\n'));
33
-
34
- },
35
- after: () => {
36
-
37
-
38
- }
41
+ /*----------------------------------
42
+ - MAIN PROCESS
43
+ ----------------------------------*/
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
+ });
39
159
  });
40
160
 
41
- const multiCompiler = await compiler.create();
42
-
43
- multiCompiler.watch({
44
-
45
- // https://webpack.js.org/configuration/watch/#watchoptions
46
- // Watching may not work with NFS and machines in VirtualBox
47
- // Uncomment next line if it is your case (use true or interval in milliseconds)
48
- //poll: 1000,
49
-
50
- // Decrease CPU or memory usage in some file systems
51
- // Ignore updated from:
52
- // - Node modules except 5HTP core (framework dev mode)
53
- // - Generated files during runtime (cause infinite loop. Ex: models.d.ts)
54
- ignored: /(node_modules\/(?!proteum\/))|(\.generated\/)|(\.cache\/)/
161
+ /*----------------------------------
162
+ - STATE
163
+ ----------------------------------*/
55
164
 
56
- //aggregateTimeout: 1000,
57
- }, async (error, stats) => {
165
+ // Current server child process used by the dev loop.
166
+ let cp: ChildProcess | undefined = undefined;
58
167
 
59
- if (error) {
60
- console.error(`Error in milticompiler.watch`, error, stats?.toString());
61
- return;
62
- }
168
+ /*----------------------------------
169
+ - HELPERS
170
+ ----------------------------------*/
63
171
 
64
- console.log("Watch callback. Reloading app ...");
65
- startApp(app);
172
+ async function startApp(app: App) {
173
+ stopApp('Restart asked');
66
174
 
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'],
67
179
  });
68
180
 
69
- Keyboard.input('ctrl+r', async () => {
181
+ cp.on('message', (message: unknown) => {
182
+ if (!isServerHotReloadResult(message)) return;
70
183
 
71
- console.log(`Waiting for compilers to be ready ...`, Object.keys(compiler.compiling));
72
- await Promise.all(Object.values(compiler.compiling));
184
+ if (message.type === serverHotReloadMessageType.succeeded) {
185
+ console.log('Server hot reload applied without restarting app.');
186
+ return;
187
+ }
73
188
 
74
- console.log(`Reloading app ...`);
189
+ console.error('Server hot reload failed. Restarting app with a fresh process.', message.error || '');
75
190
  startApp(app);
76
-
77
191
  });
192
+ }
78
193
 
79
- Keyboard.input('ctrl+c', () => {
80
- stopApp("CTRL+C Pressed");
81
- });
82
- });
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
+ }
83
201
 
202
+ function requestServerHotReload(changedFiles: string[]) {
203
+ if (!cp || !cp.connected) return false;
84
204
 
85
- /*----------------------------------
86
- - APP RUN
87
- ----------------------------------*/
88
- let cp: ChildProcess | undefined = undefined;
205
+ const message: TServerHotReloadRequest = { type: serverHotReloadMessageType.request, changedFiles };
89
206
 
90
- async function startApp( app: App ) {
207
+ cp.send(message);
208
+ return true;
209
+ }
91
210
 
92
- stopApp('Restart asked');
211
+ function isServerHotReloadEligible(changedFiles: string[]) {
212
+ if (changedFiles.length === 0) return false;
93
213
 
94
- console.info(`Launching new server ...`);
95
- cp = spawn('node', ['' + app.paths.bin + '/server.js', '--preserve-symlinks'], {
214
+ return changedFiles.every((changedFile) => {
215
+ const normalizedChangedFile = normalizeWatchPath(changedFile);
96
216
 
97
- // sdin, sdout, sderr
98
- 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
+ }
99
225
 
226
+ const relativePath = normalizedChangedFile.substring(normalizedRootPath.length + 1);
227
+ return hotReloadableServerPathPatterns.some((pattern) => pattern.test(relativePath));
228
+ });
100
229
  });
101
230
  }
102
231
 
103
- function stopApp( reason: string ) {
104
- if (cp !== undefined) {
105
- console.info(`Killing current server instance (ID: ${cp.pid}) for the following reason:`, reason);
106
- cp.kill();
107
- }
108
-
232
+ function normalizeWatchPath(watchPath: string) {
233
+ return path.resolve(watchPath).replace(/\\/g, '/').replace(/\/$/, '');
109
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
+ };
@@ -0,0 +1,18 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Configs
6
+ import Compiler from '../compiler';
7
+
8
+ /*----------------------------------
9
+ - COMMAND
10
+ ----------------------------------*/
11
+ export const run = (): Promise<void> =>
12
+ new Promise(async (resolve) => {
13
+ const compiler = new Compiler('dev');
14
+
15
+ await compiler.refreshGeneratedTypings();
16
+
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
+ };