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
package/cli/paths.ts CHANGED
@@ -7,7 +7,6 @@ import fs from 'fs';
7
7
  import path from 'path';
8
8
  import TsAlias from 'ts-alias';
9
9
  import moduleAlias from 'module-alias';
10
- import { filenameToImportName } from 'babel-plugin-glob-import';
11
10
 
12
11
  // Core
13
12
 
@@ -19,23 +18,22 @@ import type { App } from './app';
19
18
  import type { TAppSide } from './app';
20
19
 
21
20
  export type TPathInfosOptions = {
22
- basePath?: string,
23
- shortenExtensions: string[],
21
+ basePath?: string;
22
+ shortenExtensions: string[];
24
23
  // Indexed will be trimed only when the extension can be shorten
25
- trimIndex: boolean,
26
- }
24
+ trimIndex: boolean;
25
+ };
27
26
 
28
27
  export type TPathInfos = {
29
-
30
- original: string,
31
- absolute: string,
32
- relative: string,
28
+ original: string;
29
+ absolute: string;
30
+ relative: string;
33
31
  //forImport: string,
34
32
 
35
- name: string,
36
- extension: string,
37
- isIndex: boolean
38
- }
33
+ name: string;
34
+ extension: string;
35
+ isIndex: boolean;
36
+ };
39
37
 
40
38
  /*----------------------------------
41
39
  - CONFIG
@@ -43,10 +41,7 @@ export type TPathInfos = {
43
41
 
44
42
  export const staticAssetName = /*isDebug ? '[name].[ext].[hash:8]' :*/ '[hash:8][ext]';
45
43
 
46
- const pathInfosDefaultOpts = {
47
- shortenExtensions: ['ts', 'js', 'tsx', 'jsx'],
48
- trimIndex: true,
49
- }
44
+ const pathInfosDefaultOpts = { shortenExtensions: ['ts', 'js', 'tsx', 'jsx'], trimIndex: true };
50
45
 
51
46
  const resolveCoreRoot = (appRoot: string) => {
52
47
  const localInstall = path.join(appRoot, 'node_modules', 'proteum');
@@ -55,66 +50,62 @@ const resolveCoreRoot = (appRoot: string) => {
55
50
  // When running `npx`/global installs, there may be no local `node_modules/proteum` yet.
56
51
  // Fall back to the installed package root (this file lives in `<root>/cli`).
57
52
  return path.resolve(__dirname, '..');
58
- }
53
+ };
54
+
55
+ const normalizeImportPath = (value: string) => value.replace(/\\/g, '/');
56
+
57
+ const filenameToImportName = (value: string) =>
58
+ normalizeImportPath(value).replace(/[^A-Za-z0-9_]+/g, '_');
59
59
 
60
60
  /*----------------------------------
61
61
  - LIB
62
62
  ----------------------------------*/
63
63
  export default class Paths {
64
-
65
64
  /*----------------------------------
66
65
  - LISTE
67
66
  ----------------------------------*/
68
67
 
69
- public constructor(
68
+ public constructor(
70
69
  public appRoot: string,
71
- public coreRoot = resolveCoreRoot(appRoot)
72
- ) {
73
-
74
- }
70
+ public coreRoot = resolveCoreRoot(appRoot),
71
+ ) {}
75
72
 
76
- public core = {
77
- cli: path.resolve(__dirname, '.'),
78
- root: this.coreRoot,
79
- pages: this.coreRoot + '/client/pages',
80
- }
73
+ public core = { cli: path.resolve(__dirname, '.'), root: this.coreRoot, pages: this.coreRoot + '/client/pages' };
81
74
 
82
75
  /*----------------------------------
83
76
  - EXTRACTION
84
77
  ----------------------------------*/
85
78
 
86
79
  public infos(filename: string, givenOpts: Partial<TPathInfosOptions> = {}): TPathInfos {
87
-
88
- const opts: TPathInfosOptions = { ...pathInfosDefaultOpts, ...givenOpts }
80
+ const opts: TPathInfosOptions = { ...pathInfosDefaultOpts, ...givenOpts };
89
81
 
90
82
  // Extraction élements du chemin
91
- const decomp = filename.split('/')
92
- let [nomFichier, extension] = (decomp.pop() as string).split('.');
83
+ const decomp = filename.split('/');
84
+ const nomComplet = decomp.pop() as string;
85
+ const lastDotIndex = nomComplet.lastIndexOf('.');
86
+ const nomFichier = lastDotIndex === -1 ? nomComplet : nomComplet.substring(0, lastDotIndex);
87
+ const extension = lastDotIndex === -1 ? '' : nomComplet.substring(lastDotIndex + 1);
93
88
  const shortenExtension = opts.shortenExtensions && opts.shortenExtensions.includes(extension);
94
89
 
95
90
  // Vire l'index
96
- const isIndex = nomFichier === 'index'
91
+ const isIndex = nomFichier === 'index';
97
92
  let cheminAbsolu: string;
98
93
  let nomReel: string;
99
94
  if (isIndex && shortenExtension && opts.trimIndex) {
100
95
  cheminAbsolu = decomp.join('/');
101
96
  nomReel = decomp.pop() as string;
102
97
  } else {
103
- cheminAbsolu = [...decomp, nomFichier].join('/')
104
- nomReel = nomFichier
98
+ cheminAbsolu = [...decomp, nomFichier].join('/');
99
+ nomReel = nomFichier;
105
100
  }
106
101
 
107
102
  // Conserve l'extension si nécessaire
108
- if (!shortenExtension)
109
- cheminAbsolu += '.' + extension;
103
+ if (!shortenExtension) cheminAbsolu += '.' + extension;
110
104
 
111
- const relative = opts.basePath === undefined
112
- ? ''
113
- : cheminAbsolu.substring( opts.basePath.length + 1 )
105
+ const relative = opts.basePath === undefined ? '' : cheminAbsolu.substring(opts.basePath.length + 1);
114
106
 
115
107
  // Retour
116
108
  const retour = {
117
-
118
109
  original: filename,
119
110
  absolute: cheminAbsolu,
120
111
  relative,
@@ -124,16 +115,15 @@ export default class Paths {
124
115
 
125
116
  name: nomReel,
126
117
  extension,
127
- isIndex
128
- }
118
+ isIndex,
119
+ };
129
120
 
130
121
  return retour;
131
122
  }
132
123
 
133
- public getPageChunk( app: App, file: string ) {
134
-
135
- const infos = this.infos( file, {
136
- basePath: file.startsWith( app.paths.pages ) ? app.paths.pages : this.core.pages,
124
+ public getPageChunk(app: App, file: string) {
125
+ const infos = this.infos(file, {
126
+ basePath: file.startsWith(app.paths.pages) ? app.paths.pages : this.core.pages,
137
127
  // Avoid potential conflicts between /landing.tsx and /landing/index.tsx
138
128
  trimIndex: false,
139
129
  });
@@ -145,21 +135,25 @@ export default class Paths {
145
135
  let chunkId = filenameToImportName(filepath);
146
136
 
147
137
  // nsure it's non-empty
148
- if (chunkId.length === 0) // = /index.tsx
149
- chunkId = "main";
138
+ if (chunkId.length === 0)
139
+ // = /index.tsx
140
+ chunkId = 'main';
150
141
 
151
- return { filepath, chunkId }
142
+ return { filepath, chunkId };
143
+ }
152
144
 
145
+ public getLayoutChunk(app: App, file: string) {
146
+ const layoutDir = path.dirname(path.dirname(file));
147
+ const relativeLayoutDir = path.relative(app.paths.pages, layoutDir);
148
+ const filepath = relativeLayoutDir === '' ? '' : normalizeImportPath(relativeLayoutDir);
149
+
150
+ return { filepath, chunkId: filenameToImportName(filepath) };
153
151
  }
154
152
 
155
153
  public applyAliases() {
156
-
157
- const aliases = new TsAlias({
158
- rootDir: this.core.cli
159
- });
154
+ const aliases = new TsAlias({ rootDir: this.core.cli });
160
155
 
161
156
  //console.log('Applying Aliases ...', aliases.forModuleAlias());
162
- moduleAlias.addAliases( aliases.forModuleAlias() );
163
-
157
+ moduleAlias.addAliases(aliases.forModuleAlias());
164
158
  }
165
159
  }
package/cli/print.ts CHANGED
@@ -1,12 +1,18 @@
1
- export const h1 = (titre: string) => console.log(
2
- '\n' +
3
- '######################################################\n' +
4
- '# ' + titre.toUpperCase() + '\n' +
5
- '######################################################'
6
- );
1
+ export const h1 = (titre: string) =>
2
+ console.log(
3
+ '\n' +
4
+ '######################################################\n' +
5
+ '# ' +
6
+ titre.toUpperCase() +
7
+ '\n' +
8
+ '######################################################',
9
+ );
7
10
 
8
- export const h2 = (titre: string) => console.log(
9
- '------------------------------------------------------\n' +
10
- '> ' + titre.toUpperCase() + '\n' +
11
- '------------------------------------------------------'
12
- );
11
+ export const h2 = (titre: string) =>
12
+ console.log(
13
+ '------------------------------------------------------\n' +
14
+ '> ' +
15
+ titre.toUpperCase() +
16
+ '\n' +
17
+ '------------------------------------------------------',
18
+ );
package/cli/tsconfig.json CHANGED
@@ -10,13 +10,14 @@
10
10
  "skipLibCheck": true,
11
11
  "allowJs": true,
12
12
  "strict": false,
13
+ "noImplicitAny": true,
14
+ "noImplicitThis": true,
15
+ "strictBindCallApply": true,
16
+ "useUnknownInCatchVariables": true,
13
17
  "downlevelIteration": true,
14
18
 
15
- // Equivalent webpack: resolve.symblinks = false
19
+ // Keep symlinked package paths stable for local framework development.
16
20
  "preserveSymlinks": true,
17
- // Décorateurs
18
- "experimentalDecorators": true,
19
-
20
21
  // React
21
22
  "jsx": "react-jsx",
22
23
  "jsxImportSource": "preact",
@@ -0,0 +1,100 @@
1
+ /*----------------------------------
2
+ - DEPENDANCES
3
+ ----------------------------------*/
4
+
5
+ // Npm
6
+ import fs from 'fs-extra';
7
+ import path from 'path';
8
+
9
+ /*----------------------------------
10
+ - TYPES
11
+ ----------------------------------*/
12
+
13
+ type TEnsureProjectAgentSymlinksArgs = { appRoot: string; coreRoot: string };
14
+
15
+ type TAgentLinkDefinition = { projectPath: string; sourcePath: string; ensureParentDir?: boolean };
16
+
17
+ /*----------------------------------
18
+ - CONSTANTS
19
+ ----------------------------------*/
20
+
21
+ // Project-local AGENTS entrypoints mapped to their framework-owned source files.
22
+ const codexAgentLinkDefinitions: TAgentLinkDefinition[] = [
23
+ { projectPath: 'AGENTS.md', sourcePath: 'AGENTS.md' },
24
+ { projectPath: 'CODING_STYLE.md', sourcePath: 'CODING_STYLE.md' },
25
+ { projectPath: path.join('client', 'AGENTS.md'), sourcePath: path.join('client', 'AGENTS.md') },
26
+ { projectPath: path.join('client', 'pages', 'AGENTS.md'), sourcePath: path.join('client', 'pages', 'AGENTS.md') },
27
+ {
28
+ projectPath: path.join('server', 'services', 'AGENTS.md'),
29
+ sourcePath: path.join('server', 'services', 'AGENTS.md'),
30
+ },
31
+ { projectPath: path.join('server', 'routes', 'AGENTS.md'), sourcePath: path.join('server', 'routes', 'AGENTS.md') },
32
+ { projectPath: path.join('tests', 'e2e', 'AGENTS.md'), sourcePath: path.join('tests', 'AGENTS.md') },
33
+ ];
34
+
35
+ /*----------------------------------
36
+ - PUBLIC API
37
+ ----------------------------------*/
38
+
39
+ export function ensureProjectAgentSymlinks({ appRoot, coreRoot }: TEnsureProjectAgentSymlinksArgs) {
40
+ const agentSourceRoot = path.join(coreRoot, 'agents', 'codex');
41
+ const agentLinks = codexAgentLinkDefinitions.map((linkDefinition) => ({
42
+ ...linkDefinition,
43
+ sourcePath: path.join(agentSourceRoot, linkDefinition.sourcePath),
44
+ }));
45
+
46
+ ensureSymlinks(appRoot, agentLinks, '[agents]');
47
+ ensureProjectSkillSymlinks({ appRoot, coreRoot });
48
+ }
49
+
50
+ /*----------------------------------
51
+ - HELPERS
52
+ ----------------------------------*/
53
+
54
+ function ensureProjectSkillSymlinks({ appRoot, coreRoot }: TEnsureProjectAgentSymlinksArgs) {
55
+ const frameworkSkillsRoot = path.join(coreRoot, 'skills');
56
+ if (!fs.existsSync(frameworkSkillsRoot)) return;
57
+
58
+ const skillLinks: TAgentLinkDefinition[] = fs
59
+ .readdirSync(frameworkSkillsRoot, { withFileTypes: true })
60
+ .filter((dirent) => dirent.isDirectory())
61
+ .map((dirent) => ({
62
+ projectPath: path.join('skills', dirent.name),
63
+ sourcePath: path.join(frameworkSkillsRoot, dirent.name),
64
+ ensureParentDir: true,
65
+ }))
66
+ .filter((linkDefinition) => pathEntryExists(path.join(linkDefinition.sourcePath, 'SKILL.md')));
67
+
68
+ ensureSymlinks(appRoot, skillLinks, '[skills]');
69
+ }
70
+
71
+ function ensureSymlinks(appRoot: string, linkDefinitions: TAgentLinkDefinition[], logPrefix: string) {
72
+ for (const linkDefinition of linkDefinitions) {
73
+ const projectFilepath = path.join(appRoot, linkDefinition.projectPath);
74
+ const projectParentDir = path.dirname(projectFilepath);
75
+
76
+ if (linkDefinition.ensureParentDir) fs.ensureDirSync(projectParentDir);
77
+ else if (!fs.existsSync(projectParentDir)) continue;
78
+
79
+ if (pathEntryExists(projectFilepath)) continue;
80
+
81
+ const sourceFilepath = linkDefinition.sourcePath;
82
+ if (!fs.existsSync(sourceFilepath)) {
83
+ throw new Error(`Missing framework asset: ${sourceFilepath}`);
84
+ }
85
+
86
+ const symlinkTarget = path.relative(projectParentDir, sourceFilepath);
87
+ fs.symlinkSync(symlinkTarget, projectFilepath);
88
+
89
+ console.info(`${logPrefix} Created ${path.relative(appRoot, projectFilepath) || '.'} -> ${symlinkTarget}`);
90
+ }
91
+ }
92
+
93
+ function pathEntryExists(filepath: string) {
94
+ try {
95
+ fs.lstatSync(filepath);
96
+ return true;
97
+ } catch {
98
+ return false;
99
+ }
100
+ }
@@ -0,0 +1,71 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ import cli from '..';
5
+ import Compiler from '../compiler';
6
+ import { runProcess } from './runProcess';
7
+
8
+ const tsconfigPaths = ['client/tsconfig.json', 'server/tsconfig.json'];
9
+ const eslintConfigPaths = ['eslint.config.mjs', 'eslint.config.js', 'eslint.config.cjs'];
10
+
11
+ const resolveInstalledBinary = (name: string) => {
12
+ const binary = path.join(cli.paths.core.root, 'node_modules', '.bin', name);
13
+
14
+ if (!fs.existsSync(binary)) throw new Error(`Missing required binary "${name}" in Proteum dependencies.`);
15
+
16
+ return binary;
17
+ };
18
+
19
+ const resolveExistingAppPaths = (paths: string[]) =>
20
+ paths
21
+ .map((relativePath) => ({ relativePath, absolutePath: path.join(cli.paths.appRoot, relativePath) }))
22
+ .filter(({ absolutePath }) => fs.existsSync(absolutePath));
23
+
24
+ const getTypecheckEnv = () => {
25
+ const existingNodeOptions = process.env.NODE_OPTIONS ?? '';
26
+
27
+ if (existingNodeOptions.includes('max-old-space-size')) return {};
28
+
29
+ return {
30
+ NODE_OPTIONS: [existingNodeOptions, '--max-old-space-size=8192'].filter(Boolean).join(' '),
31
+ };
32
+ };
33
+
34
+ export const refreshGeneratedTypings = async () => {
35
+ const compiler = new Compiler('dev');
36
+
37
+ await compiler.refreshGeneratedTypings();
38
+ };
39
+
40
+ export const runAppTypecheck = async () => {
41
+ const existingProjects = resolveExistingAppPaths(tsconfigPaths);
42
+
43
+ if (existingProjects.length === 0)
44
+ throw new Error(`No TypeScript app projects found. Expected one of: ${tsconfigPaths.join(', ')}.`);
45
+
46
+ const tsc = resolveInstalledBinary('tsc');
47
+
48
+ for (const { relativePath } of existingProjects)
49
+ await runProcess(tsc, ['-p', relativePath, '--noEmit', '--pretty', 'false'], {
50
+ cwd: cli.paths.appRoot,
51
+ env: getTypecheckEnv(),
52
+ });
53
+ };
54
+
55
+ export const runAppLint = async ({ fix = false } = {}) => {
56
+ const [config] = resolveExistingAppPaths(eslintConfigPaths);
57
+
58
+ if (!config)
59
+ throw new Error(
60
+ `No ESLint config found. Expected one of: ${eslintConfigPaths
61
+ .map((relativePath) => path.join(cli.paths.appRoot, relativePath))
62
+ .join(', ')}.`,
63
+ );
64
+
65
+ const eslint = resolveInstalledBinary('eslint');
66
+ const args = ['.', '--config', config.absolutePath, '--no-config-lookup'];
67
+
68
+ if (fix) args.push('--fix');
69
+
70
+ await runProcess(eslint, args, { cwd: cli.paths.appRoot });
71
+ };
@@ -6,12 +6,10 @@
6
6
  import fs from 'fs-extra';
7
7
  import path from 'path';
8
8
 
9
-
10
9
  /*----------------------------------
11
10
  - TYPES
12
11
  ----------------------------------*/
13
12
 
14
-
15
13
  /*----------------------------------
16
14
  - UTILS
17
15
  ----------------------------------*/
@@ -19,4 +17,4 @@ import path from 'path';
19
17
  export const api = (method: string, path: string, data: object, local: boolean = false) =>
20
18
  `curl -X ${method} ${local ? 'http://localhost:3010' : 'https://dopamyn.io'}${path} ` +
21
19
  `-H 'Content-Type: application/json' -H 'Accept: application/json' ` +
22
- `-d '${JSON.stringify(data)}';`;
20
+ `-d '${JSON.stringify(data)}';`;
@@ -9,28 +9,22 @@ import readline, { Key } from 'readline';
9
9
  - TYPES
10
10
  ----------------------------------*/
11
11
 
12
- type TKeyboardCommand = {
13
- remove?: boolean,
14
- run: (str: string, chunk: string, key: Key) => void
15
- }
12
+ type TKeyboardCommand = { remove?: boolean; run: (str: string, chunk: string, key: Key) => void };
16
13
 
17
14
  /*----------------------------------
18
15
  - METHODS
19
16
  ----------------------------------*/
20
17
  class KeyboardCommands {
21
-
22
- private commands: { [input: string]: TKeyboardCommand } = {}
18
+ private commands: { [input: string]: TKeyboardCommand } = {};
23
19
 
24
20
  public constructor() {
25
21
  this.listen();
26
22
  }
27
23
 
28
24
  private listen() {
29
-
30
25
  readline.emitKeypressEvents(process.stdin);
31
26
  process.stdin.setRawMode(true);
32
27
  process.stdin.on('keypress', async (chunk: string, key: Key) => {
33
-
34
28
  let str = key.name;
35
29
  if (!str) return;
36
30
  if (str === 'return') str = 'enter';
@@ -41,38 +35,27 @@ class KeyboardCommands {
41
35
 
42
36
  const kCommand = this.commands[str] || this.commands.fallback;
43
37
  if (kCommand) {
44
-
45
38
  kCommand.run(str, chunk, key);
46
39
 
47
- if (kCommand.remove)
48
- delete this.commands[str];
40
+ if (kCommand.remove) delete this.commands[str];
49
41
  }
50
42
 
51
43
  if (str === 'ctrl+c') {
52
-
53
44
  console.log(`Exiting ...`);
54
45
  process.exit(0);
55
-
56
- }
57
-
58
-
46
+ }
59
47
  });
60
48
  }
61
49
 
62
-
63
- public input(str: string, run: TKeyboardCommand["run"], options: Omit<TKeyboardCommand, 'run'> = {}) {
64
- this.commands[str] = { run, ...options }
50
+ public input(str: string, run: TKeyboardCommand['run'], options: Omit<TKeyboardCommand, 'run'> = {}) {
51
+ this.commands[str] = { run, ...options };
65
52
  }
66
53
 
67
54
  public waitForInput(str: string): Promise<void> {
68
55
  return new Promise((resolve) => {
69
- this.commands[str] = {
70
- run: () => resolve(),
71
- remove: true
72
- }
56
+ this.commands[str] = { run: () => resolve(), remove: true };
73
57
  });
74
58
  }
75
-
76
59
  }
77
60
 
78
- export default new KeyboardCommands
61
+ export default new KeyboardCommands();
@@ -0,0 +1,30 @@
1
+ import cp from 'child_process';
2
+
3
+ type TRunProcessOptions = {
4
+ cwd?: string;
5
+ env?: NodeJS.ProcessEnv;
6
+ };
7
+
8
+ export const runProcess = (command: string, args: string[] = [], options: TRunProcessOptions = {}) =>
9
+ new Promise<void>((resolve, reject) => {
10
+ const child = cp.spawn(command, args, {
11
+ cwd: options.cwd,
12
+ env: { ...process.env, ...options.env },
13
+ stdio: 'inherit',
14
+ });
15
+
16
+ child.on('error', reject);
17
+ child.on('exit', (code, signal) => {
18
+ if (signal) {
19
+ reject(new Error(`Command "${command}" was interrupted by signal ${signal}.`));
20
+ return;
21
+ }
22
+
23
+ if (code === 0) {
24
+ resolve();
25
+ return;
26
+ }
27
+
28
+ reject(new Error(`Command "${command}" exited with code ${code ?? 'unknown'}.`));
29
+ });
30
+ });
@@ -7,48 +7,48 @@ import React from 'react';
7
7
 
8
8
  // Core
9
9
  import type { Layout } from '@common/router';
10
- import { ReactClientContext } from '@/client/context';
11
- import DialogManager from '@client/components/Dialog/Manager'
10
+ import type { LayoutProps } from '@common/router/layouts';
11
+ import { ReactClientContext, type ClientContext } from '@/client/context';
12
+ import DialogManager from '@client/components/Dialog/Manager';
12
13
 
13
14
  // Core components
14
15
  import RouterComponent from '@client/services/router/components/router';
15
- import type { TClientOrServerContextForPage } from '@common/router';
16
16
 
17
17
  /*----------------------------------
18
18
  - COMPOSANT
19
19
  ----------------------------------*/
20
- export default function App({ context }: {
21
- context: TClientOrServerContextForPage,
22
- }) {
23
-
20
+ export default function App({ context }: { context: ClientContext }) {
24
21
  const curLayout = context.page?.layout;
25
22
  const [layout, setLayout] = React.useState<Layout | false | undefined>(curLayout);
26
23
  const [apiData, setApiData] = React.useState<{ [k: string]: any } | null>(context.page?.data || {});
27
24
 
28
25
  // TODO: context.page is always provided in the context on the client side
29
- if (context.app.side === "client")
30
- context.app.setLayout = setLayout;
26
+ if (context.app.side === 'client') context.app.setLayout = setLayout;
27
+
28
+ const layoutProps: LayoutProps = {
29
+ ...context,
30
+ context,
31
+ data: { ...apiData, ...context.request.data },
32
+ menu: undefined,
33
+ children: undefined,
34
+ };
31
35
 
32
36
  return (
33
37
  <ReactClientContext.Provider value={context}>
34
-
35
- <DialogManager context={context} />
36
-
37
- {!layout ? <>
38
- {/* TODO: move to app, because here, we're not aware that the router service has been defined */}
39
- <RouterComponent service={context.Router} />
40
- </> : <> {/* Same as router/components/Page.tsx */}
41
- <layout.Component
42
- // Services
43
- {...context}
44
- // API data & URL params
45
- data={{
46
- ...apiData,
47
- ...context.request.data
48
- }}
49
- />
50
- </>}
51
-
38
+ <DialogManager />
39
+
40
+ {!layout ? (
41
+ <>
42
+ {/* TODO: move to app, because here, we're not aware that the router service has been defined */}
43
+ <RouterComponent service={context.Router} />
44
+ </>
45
+ ) : (
46
+ <>
47
+ {' '}
48
+ {/* Same as router/components/Page.tsx */}
49
+ <layout.Component {...layoutProps} />
50
+ </>
51
+ )}
52
52
  </ReactClientContext.Provider>
53
- )
54
- }
53
+ );
54
+ }