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
@@ -6,27 +6,27 @@
6
6
 
7
7
  import RequestService from './service';
8
8
 
9
- import ApiClientService, {
10
- TApiFetchOptions, TFetcherList, TFetcherArgs, TFetcher,
11
- TDataReturnedByFetchers
9
+ import ApiClientService, {
10
+ TFetcherList,
11
+ TFetcherArgs,
12
+ TFetcher,
13
+ TDataReturnedByFetchers,
12
14
  } from '@common/router/request/api';
13
15
 
14
16
  /*----------------------------------
15
17
  - TYPES
16
18
  ----------------------------------*/
17
19
 
18
-
19
20
  /*----------------------------------
20
21
  - SERVICE
21
22
  ----------------------------------*/
22
23
  export default class ApiClientRequest extends RequestService implements ApiClientService {
23
-
24
24
  /*----------------------------------
25
25
  - HIGH LEVEL
26
26
  ----------------------------------*/
27
27
 
28
- public fetch<TProvidedData extends TFetcherList = TFetcherList>(
29
- fetchers: TFetcherList
28
+ public fetch<TProvidedData extends TFetcherList = TFetcherList>(
29
+ _fetchers: TFetcherList,
30
30
  ): TDataReturnedByFetchers<TProvidedData> {
31
31
  throw new Error("api.fetch shouldn't be called here.");
32
32
  }
@@ -35,34 +35,36 @@ export default class ApiClientRequest extends RequestService implements ApiClien
35
35
  - PLACEHOLDERS
36
36
  ----------------------------------*/
37
37
 
38
- public set( newData: TObjetDonnees ) {
39
- throw new Error("api.set is not available on server side.");
38
+ public set(_newData: TObjetDonnees) {
39
+ throw new Error('api.set is not available on server side.');
40
40
  }
41
41
 
42
- public reload( ids?: string | string[], params?: TObjetDonnees ) {
43
- throw new Error("api.set is not available on server side.");
42
+ public reload(_ids?: string | string[], _params?: TObjetDonnees) {
43
+ throw new Error('api.set is not available on server side.');
44
44
  }
45
45
 
46
46
  /*----------------------------------
47
47
  - API CALLS FROM SERVER
48
48
  ----------------------------------*/
49
49
 
50
- public createFetcher<TData extends unknown = unknown>(...[method, path, data, options]: TFetcherArgs): TFetcher<TData> {
51
- return {
52
- method, path, data, options,
50
+ public createFetcher<TData extends unknown = unknown>(
51
+ ...[method, path, data, options]: TFetcherArgs
52
+ ): TFetcher<TData> {
53
+ return {
54
+ method,
55
+ path,
56
+ data,
57
+ options,
53
58
  // We don't put the then and catch methods so the api consumer on server side will know it's a fetcher and not a promize to wait
54
59
  } as TFetcher<TData>;
55
60
  }
56
61
 
57
62
  public async fetchSync(fetchers: TFetcherList, alreadyLoadedData: {}): Promise<TObjetDonnees> {
58
-
59
63
  const fetchedData: TObjetDonnees = { ...alreadyLoadedData };
60
64
 
61
65
  for (const id in fetchers) {
62
-
63
- const fetcher = fetchers[id]
64
- if (!fetcher)
65
- continue;
66
+ const fetcher = fetchers[id];
67
+ if (!fetcher) continue;
66
68
 
67
69
  // Promise Fetcher (direct call from service method)
68
70
  if ('then' in fetcher) {
@@ -70,18 +72,17 @@ export default class ApiClientRequest extends RequestService implements ApiClien
70
72
  continue;
71
73
  }
72
74
 
73
- const { method, path, data, options } = fetcher;
75
+ const { method, path, data } = fetcher;
74
76
  //this.router.config.debug && console.log(`[api] Resolving from internal api`, method, path, data);
75
77
 
76
78
  // We don't fetch the already given data
77
- if (id in fetchedData)
78
- continue;
79
+ if (id in fetchedData) continue;
79
80
 
80
81
  // Create a children request to resolve the api data
81
82
  const request = this.request.children(method, path, data);
82
- fetchedData[id] = await request.router.resolve(request).then(res => res.data);
83
+ fetchedData[id] = await request.router.resolve(request).then((res) => res.data);
83
84
  }
84
85
 
85
86
  return fetchedData;
86
- }
87
- }
87
+ }
88
+ }
@@ -6,16 +6,13 @@
6
6
  import type express from 'express';
7
7
  import ISO6391 from 'iso-639-1';
8
8
  import accepts from 'accepts';
9
- import Bowser from "bowser";
9
+ import Bowser from 'bowser';
10
10
 
11
11
  // Core
12
12
  import BaseRequest from '@common/router/request';
13
13
 
14
14
  // Specific
15
- import type {
16
- default as Router, Config as RouterConfig,
17
- HttpMethod, HttpHeaders
18
- } from '..';
15
+ import type { HttpMethod, HttpHeaders } from '..';
19
16
  import ApiClient from './api';
20
17
  import ServerResponse from '../response';
21
18
  import type { TAnyRouter } from '..';
@@ -25,30 +22,24 @@ import type { TAnyRouter } from '..';
25
22
  ----------------------------------*/
26
23
 
27
24
  const localeFilter = (input: any) => {
28
-
29
25
  // Data type
30
- if (typeof input !== 'string')
31
- return;
26
+ if (typeof input !== 'string') return;
32
27
 
33
28
  // Extract ISO code
34
29
  let lang = input.trim().split(/[-_]/)[0].toLowerCase();
35
-
30
+
36
31
  // Check size
37
- if (!ISO6391.validate(lang))
38
- return;
32
+ if (!ISO6391.validate(lang)) return;
39
33
 
40
34
  return lang.toUpperCase();
41
- }
35
+ };
42
36
 
43
- export type UploadedFile = File
37
+ export type UploadedFile = File;
44
38
 
45
39
  /*----------------------------------
46
40
  - CONTEXTE
47
41
  ----------------------------------*/
48
- export default class ServerRequest<
49
- TRouter extends TAnyRouter = TAnyRouter
50
- > extends BaseRequest {
51
-
42
+ export default class ServerRequest<TRouter extends TAnyRouter = TAnyRouter> extends BaseRequest {
52
43
  /*----------------------------------
53
44
  - PROPRIÉTÉS
54
45
  ----------------------------------*/
@@ -79,26 +70,24 @@ export default class ServerRequest<
79
70
  /*----------------------------------
80
71
  - INITIALISATION
81
72
  ----------------------------------*/
82
- public constructor(
83
-
73
+ public constructor(
84
74
  id: string,
85
- method: HttpMethod,
86
- path: string,
75
+ method: HttpMethod,
76
+ path: string,
87
77
  data: TObjetDonnees | undefined,
88
78
  headers: HttpHeaders | undefined,
89
79
 
90
- res: express.Response,
80
+ res: express.Response,
91
81
  router: TRouter,
92
- isVirtual: boolean = false
82
+ isVirtual: boolean = false,
93
83
  ) {
94
-
95
84
  super(path);
96
85
 
97
86
  this.id = id;
98
87
  this.isVirtual = isVirtual;
99
88
 
100
89
  this.req = res.req;
101
- this.res = res
90
+ this.res = res;
102
91
  this.router = router;
103
92
  this.api = new ApiClient(this);
104
93
 
@@ -116,48 +105,46 @@ export default class ServerRequest<
116
105
  }
117
106
 
118
107
  public children(method: HttpMethod, path: string, data: TObjetDonnees | undefined) {
119
- const children = new ServerRequest(
120
- this.id, method, path, data, { ...this.headers, accept: 'application/json' },
121
- this.res, this.router, true
108
+ const children = new ServerRequest(
109
+ this.id,
110
+ method,
111
+ path,
112
+ data,
113
+ { ...this.headers, accept: 'application/json' },
114
+ this.res,
115
+ this.router,
116
+ true,
122
117
  );
123
118
  children.user = this.user;
124
119
  return children;
125
120
  }
126
121
 
127
122
  private getLocale() {
128
-
129
123
  const fromQuery = localeFilter(this.req.query.lang);
130
124
  if (fromQuery) {
131
125
  this.res.cookie('lang', fromQuery);
132
126
  return fromQuery;
133
- }
127
+ }
134
128
 
135
- const locale = (
129
+ const locale =
136
130
  // Member settings
137
- this.user?.locale
138
- ||
131
+ this.user?.locale ||
139
132
  // URL
140
- localeFilter( this.req.cookies.lang )
141
- ||
133
+ localeFilter(this.req.cookies.lang) ||
142
134
  // Browser
143
- localeFilter( this.req.acceptsLanguages()[0] )
144
- ||
135
+ localeFilter(this.req.acceptsLanguages()[0]) ||
145
136
  // Default
146
- 'EN'
147
- )
137
+ 'EN';
148
138
 
149
- return locale ? locale.toUpperCase() : 'EN'
139
+ return locale ? locale.toUpperCase() : 'EN';
150
140
  }
151
141
 
152
- public cookie( key: string, consume: boolean = false ) {
153
-
154
- const value = this.req.cookies[ key ];
142
+ public cookie(key: string, consume: boolean = false) {
143
+ const value = this.req.cookies[key];
155
144
 
156
- if (consume)
157
- this.res.clearCookie(key);
145
+ if (consume) this.res.clearCookie(key);
158
146
 
159
147
  return value;
160
-
161
148
  }
162
149
 
163
150
  /*----------------------------------
@@ -170,15 +157,21 @@ export default class ServerRequest<
170
157
  }
171
158
 
172
159
  public device(): Bowser.Parser.ParsedResult | undefined {
173
- return this.headers['user-agent'] !== undefined
174
- ? Bowser.parse(this.headers['user-agent'])
175
- : undefined;
160
+ return this.headers['user-agent'] !== undefined ? Bowser.parse(this.headers['user-agent']) : undefined;
176
161
  }
177
162
 
178
163
  public deviceString(): string | undefined {
179
164
  const info = this.device();
180
165
  if (info === undefined) return undefined;
181
166
  const { os, browser } = info;
182
- return (os.name || 'Unknown OS') + ' ' + (os.versionName || os.version || '') + ' / ' + (browser.name || 'Unknown browser') + ' ' + (browser.version || '');
167
+ return (
168
+ (os.name || 'Unknown OS') +
169
+ ' ' +
170
+ (os.versionName || os.version || '') +
171
+ ' / ' +
172
+ (browser.name || 'Unknown browser') +
173
+ ' ' +
174
+ (browser.version || '')
175
+ );
183
176
  }
184
- }
177
+ }
@@ -2,20 +2,16 @@
2
2
  - DEPENDANCES
3
3
  ----------------------------------*/
4
4
 
5
- import type Router from '..';
5
+ import type { TAnyRouter } from '..';
6
6
  import type ServerRequest from '.';
7
7
 
8
8
  /*----------------------------------
9
9
  - SERVICE
10
10
  ----------------------------------*/
11
- export default abstract class RequestService {
12
-
11
+ export default abstract class RequestService<TRequest extends ServerRequest<TAnyRouter> = ServerRequest<TAnyRouter>> {
13
12
  public constructor(
14
- public request: ServerRequest<Router>,
15
- public router = request.router,
16
- public app = router.app
17
- ) {
18
-
19
- }
20
-
21
- }
13
+ public request: TRequest,
14
+ public router: TRequest['router'] = request.router,
15
+ public app: TRequest['router']['app'] = router.app,
16
+ ) {}
17
+ }
@@ -1,177 +1,140 @@
1
1
  import { InputError } from '@common/errors';
2
- import zod, { _ZodType } from 'zod';
3
-
4
- export type TRichTextValidatorOptions = {
5
- attachements?: boolean
2
+ import zod from 'zod';
3
+
4
+ export type TRichTextValidatorOptions = { attachements?: boolean };
5
+ export type TValidationSchema = zod.ZodTypeAny;
6
+ export type TValidationShape = zod.ZodRawShape;
7
+
8
+ type TChoiceOption = { value: PrimitiveValue; label: string };
9
+
10
+ type TLexicalNode = {
11
+ type: string;
12
+ text?: string;
13
+ children?: TLexicalNode[];
14
+ };
15
+
16
+ type TLexicalRoot = {
17
+ root: {
18
+ type: 'root';
19
+ children: TLexicalNode[];
20
+ };
21
+ };
22
+
23
+ const isRecord = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null;
24
+
25
+ const hasChoiceValue = (value: unknown): value is { value: PrimitiveValue } => isRecord(value) && 'value' in value;
26
+
27
+ const normalizeChoiceValue = (value: unknown): PrimitiveValue =>
28
+ (hasChoiceValue(value) ? value.value : value) as PrimitiveValue;
29
+
30
+ const isLexicalNode = (value: unknown): value is TLexicalNode =>
31
+ isRecord(value) && typeof value.type === 'string';
32
+
33
+ const isLexicalRoot = (value: unknown): value is TLexicalRoot => {
34
+ if (!isRecord(value)) return false;
35
+ if (!isRecord(value.root)) return false;
36
+ if (value.root.type !== 'root') return false;
37
+ return Array.isArray(value.root.children);
38
+ };
39
+
40
+ export const isZodSchema = (fields: unknown): fields is TValidationSchema => {
41
+ return (
42
+ typeof fields === 'object' &&
43
+ fields !== null &&
44
+ 'safeParse' in fields &&
45
+ typeof (fields as TValidationSchema).safeParse === 'function'
46
+ );
47
+ };
48
+
49
+ export function toValidationSchema<TSchema extends TValidationSchema>(fields: TSchema): TSchema;
50
+ export function toValidationSchema<TShape extends TValidationShape>(fields: TShape): zod.ZodObject<TShape>;
51
+ export function toValidationSchema(
52
+ fields: TValidationSchema | TValidationShape,
53
+ ): TValidationSchema | zod.ZodObject<TValidationShape>;
54
+ export function toValidationSchema(fields: TValidationSchema | TValidationShape) {
55
+ return isZodSchema(fields) ? fields : zod.object(fields);
6
56
  }
7
57
 
8
- export const preprocessSchema = (schema: zod.ZodObject): zod.ZodObject => {
9
-
10
- // Not working, data is {}
11
- return schema;
12
-
13
- if (!(schema instanceof zod.ZodObject))
14
- return schema;
15
-
16
- if (schema.withPreprocessing)
17
- return schema;
58
+ // Legacy hook kept as an identity to preserve the public surface without relying on removed Zod internals.
59
+ export const preprocessSchema = <TSchema extends zod.ZodObject<any>>(schema: TSchema): TSchema => schema;
18
60
 
19
- const shape = schema.def.shape;
20
- const newShape: Record<string, zod.ZodTypeAny> = {};
61
+ const createChoiceValueSchema = (
62
+ choices: readonly string[] | readonly TChoiceOption[] | zod.ZodTypeAny,
63
+ ): zod.ZodTypeAny => {
64
+ if (isZodSchema(choices)) return choices;
21
65
 
22
- for (const key in shape) {
23
-
24
- if (!['newEntity', 'email'].includes(key))
25
- continue;
26
-
27
- let current: zod.ZodTypeAny = shape[key];
28
- while (current) {
29
-
30
- const origType = current.type;
31
- const preprocessor = toPreprocess[origType];
32
-
33
- if (origType === 'object') {
34
- newShape[key] = preprocessSchema(current as zod.ZodObject);
35
- break;
36
- }
37
-
38
- if (preprocessor) {
39
- newShape[key] = preprocessor(current);
40
- console.log('====newShape', key, newShape[key]);
41
- break;
42
- }
43
-
44
- current = current.def;
45
- }
46
- }
47
-
48
- const newSchema = zod.object(newShape);
49
- newSchema.withPreprocessing = true;
50
- return newSchema;
51
- }
66
+ const allowedValues = new Set(choices.map((choice) => normalizeChoiceValue(choice)));
52
67
 
53
- const toPreprocess = {
68
+ return zod.custom<PrimitiveValue>((value) => allowedValues.has(normalizeChoiceValue(value)), {
69
+ message: 'Invalid choice.',
70
+ });
71
+ };
54
72
 
55
- string: (zString: zod.ZodString) => zod.preprocess( val => {
56
- return val === '' ? undefined : val;
57
- }, zString),
58
-
59
- int: (zInt: zod.ZodInt) => zod.preprocess( val => {
60
- return typeof val === 'string' ? Number.parseInt(val) : val;
61
- }, zInt),
62
-
63
- }
64
-
65
- export const schema = {
66
- ...zod,
73
+ export const schema = {
74
+ ...zod,
67
75
 
68
76
  file: () => {
69
-
70
- // Chaine = url ancien fichier = exclusion de la valeur pour conserver l'ancien fichier
71
- // NOTE: Si la valeur est présente mais undefined, alors on supprimera le fichier
72
- /*if (typeof val === 'string')
73
- return true;*/
74
-
77
+ // String = existing file URL, so callers can omit replacing an uploaded file.
75
78
  return zod.file();
76
79
  },
77
80
 
78
- choice: ( choices: string[] | { value: any, label: string }[] | _ZodType, options: { multiple?: boolean } = {} ) => {
79
-
80
- const normalizeValue = (value: any) => typeof value === 'object' ? value.value : value;
81
-
82
- const valueType: _ZodType = Array.isArray(choices)
83
- ? zod.enum( choices.map(normalizeValue) )
84
- : zod.string();
85
-
86
- const itemType = zod.union([
87
-
88
- zod.object({ value: valueType, label: zod.string() }),
89
-
90
- valueType
91
-
92
- ]);
93
-
94
- const type = options.multiple ? zod.array( itemType ) : itemType;
95
-
96
- return type.transform(v => {
97
- if (options.multiple) {
98
- return v.map(normalizeValue);
99
- } else {
100
- return normalizeValue(v);
101
- }
81
+ choice: (
82
+ choices: readonly string[] | readonly TChoiceOption[] | zod.ZodTypeAny,
83
+ options: { multiple?: boolean } = {},
84
+ ) => {
85
+ const valueType = createChoiceValueSchema(choices);
86
+ const itemType = zod.union([zod.object({ value: valueType, label: zod.string() }), valueType]);
87
+ const choiceType = options.multiple ? zod.array(itemType) : itemType;
88
+
89
+ return choiceType.transform((value: unknown) => {
90
+ if (options.multiple) return (value as unknown[]).map((entry: unknown) => normalizeChoiceValue(entry));
91
+ return normalizeChoiceValue(value);
102
92
  });
103
93
  },
104
94
 
105
- richText: (opts: TRichTextValidatorOptions = {}) => schema.custom(val => {
106
-
107
- if (typeof val !== 'string') {
108
- console.error("Invalid rich text format.", val);
109
- return false;
110
- }
111
-
112
- // We get a stringified json as input since the editor workds with JSON string
113
- try {
114
- val = JSON.parse(val);
115
- } catch (error) {
116
- console.error("Failed to parse rich text json:", error, val);
117
- return false;//throw new InputError("Invalid rich text format.");
118
- }
119
-
120
- // Check that the root exists and has a valid type
121
- if (!val || typeof val !== 'object' || typeof val.root !== 'object' || val.root.type !== 'root') {
122
- console.error("Invalid rich text value (1).", val);
123
- return false;//throw new InputError("Invalid rich text value (1).");
124
- }
125
-
126
- // Check if root has children array
127
- if (!Array.isArray(val.root.children)) {
128
- console.error("Invalid rich text value (2).", val);
129
- return false;
130
- }
95
+ richText: (opts: TRichTextValidatorOptions = {}) =>
96
+ schema.custom((value) => {
97
+ if (typeof value !== 'string') {
98
+ console.error('Invalid rich text format.', value);
99
+ return false;
100
+ }
131
101
 
132
- // Validate each child node in root
133
- for (const child of val.root.children) {
134
- if (!validateLexicalNode(child, opts))
102
+ let parsed: unknown;
103
+ try {
104
+ parsed = JSON.parse(value);
105
+ } catch (error) {
106
+ console.error('Failed to parse rich text json:', error, value);
135
107
  return false;
136
- }
108
+ }
137
109
 
138
- return true;
139
- })
140
- }
110
+ if (!isLexicalRoot(parsed)) {
111
+ console.error('Invalid rich text value.', parsed);
112
+ return false;
113
+ }
141
114
 
142
- // Recursive function to validate each node
143
- function validateLexicalNode(node: any, opts: TRichTextValidatorOptions ) {
115
+ return parsed.root.children.every((child) => validateLexicalNode(child, opts));
116
+ }),
117
+ };
144
118
 
145
- // Each node should be an object with a `type` property
146
- if (typeof node !== 'object' || !node.type || typeof node.type !== 'string')
147
- throw new InputError("Invalid rich text value (3).");
119
+ function validateLexicalNode(node: unknown, opts: TRichTextValidatorOptions) {
120
+ if (!isLexicalNode(node)) throw new InputError('Invalid rich text value (3).');
148
121
 
149
- // Validate text nodes
150
122
  if (node.type === 'text') {
123
+ if (typeof node.text !== 'string') throw new InputError('Invalid rich text value (4).');
124
+ return true;
125
+ }
151
126
 
152
- if (typeof node.text !== 'string')
153
- throw new InputError("Invalid rich text value (4).");
154
-
155
- // Validate paragraph, heading, or other structural nodes that may contain children
156
- } else if (['paragraph', 'heading', 'list', 'listitem'].includes(node.type)) {
157
-
158
- if (!Array.isArray(node.children) || !node.children.every(children => validateLexicalNode(children, opts))) {
159
- throw new InputError("Invalid rich text value (5).");
127
+ if (['paragraph', 'heading', 'list', 'listitem'].includes(node.type)) {
128
+ if (!Array.isArray(node.children) || !node.children.every((child) => validateLexicalNode(child, opts))) {
129
+ throw new InputError('Invalid rich text value (5).');
160
130
  }
161
131
 
162
- // Files upload
163
- } else if (node.type === 'image') {
164
-
165
- // Check if allowed
166
- /*if (opts.attachements === undefined)
167
- throw new InputError("Image attachments not allowed in this rich text field.");*/
168
-
169
- // TODO: check mime
170
-
171
-
172
- // Upload file
173
-
132
+ return true;
133
+ }
174
134
 
135
+ if (node.type === 'image') {
136
+ // Attachments are validated by the upload pipeline; rich-text validation only enforces node shape.
137
+ return true;
175
138
  }
176
139
 
177
140
  return true;