proteum 1.0.2 → 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 (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 +12 -47
  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
@@ -1,67 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- // Npm
6
- import loadScript from 'load-script';
7
-
8
- /*----------------------------------
9
- - SERVICE
10
- ----------------------------------*/
11
- export default class Recaptcha {
12
-
13
- private idClientCaptcha: string | null = null;
14
- public init(): Promise<void> {
15
- const idConteneurBadge = 'badge-recaptcha';
16
- return new Promise((resolve: Function, reject: Function) => {
17
-
18
- // Déjà chargé
19
- if (this.idClientCaptcha !== null)
20
- return resolve();
21
-
22
- loadScript('https://www.google.com/recaptcha/api.js?render=explicit', (err/*, script*/) => {
23
-
24
- if (err) {
25
- reject(err);
26
- return;
27
- }
28
-
29
- grecaptcha.ready(() => {
30
-
31
- const conteneur = document.getElementById(idConteneurBadge);
32
-
33
- if (!conteneur)
34
- throw new Error("Conteneur badge recaptcha pas trouvé");
35
-
36
- if (this.idClientCaptcha === null) {
37
- if (conteneur.dataset.flottant)
38
- this.idClientCaptcha = grecaptcha.render(idConteneurBadge, {
39
- 'sitekey': this.app.apis.recaptcha.pub,
40
- 'size': 'invisible'
41
- });
42
- else
43
- this.idClientCaptcha = grecaptcha.render(idConteneurBadge, {
44
- 'sitekey': this.app.apis.recaptcha.pub,
45
- 'badge': 'inline',
46
- 'size': 'invisible'
47
- });
48
- }
49
-
50
- if (this.idClientCaptcha !== null)
51
- resolve();
52
- else
53
- reject("Attendez que la page soit complètement chargée. Si c'est déjà le cas, rechargez la page.");
54
- });
55
- })
56
- });
57
- }
58
-
59
- public async check(action: string): Promise<string> {
60
-
61
- await this.init();
62
-
63
- const token = grecaptcha.execute(this.idClientCaptcha, { action: action });
64
-
65
- return token;
66
- }
67
- }
@@ -1,147 +0,0 @@
1
- /*----------------------------------
2
- - DEPENDANCES
3
- ----------------------------------*/
4
-
5
- import type { ClientContext } from '../../context';
6
-
7
- import type { TDialogControls } from '@client/components/Dialog/Manager';
8
-
9
- /*----------------------------------
10
- - TYPE
11
- ----------------------------------*/
12
-
13
- type TEventsList = {[name: string]: TEventCallback[]};
14
-
15
- type TEventCallback = (data: any) => void
16
-
17
- let netwokrStatusToast: TDialogControls;
18
-
19
- /*----------------------------------
20
- - SERVICE
21
- ----------------------------------*/
22
- class SocketScope {
23
-
24
- public ws?: WebSocket;
25
- public events: TEventsList = {}
26
-
27
- public constructor(
28
- public path: string,
29
- public context: ClientContext,
30
- events: {[name: string]: TEventCallback} = {}
31
- ) {
32
- this.connect();
33
-
34
- for (const event in events)
35
- this.events[event] = [events[event]];
36
- }
37
-
38
- private connect() {
39
-
40
- const protocol = window.location.protocol === 'http:' ? 'ws:' : 'wss:';
41
- const url = protocol + '//' + this.context.request.host + this.path;
42
-
43
- try {
44
- this.ws = new WebSocket(url);
45
- } catch (error) {
46
- console.warn(`[socket] Connection failed for ${url}`, error);
47
- return;
48
- }
49
-
50
- this.ws.onopen = () => {
51
- console.log(`[socket] Connected to the live data provider:`, url);
52
-
53
- if (netwokrStatusToast) {
54
- netwokrStatusToast.close(true);
55
- this.context.toast.success("Your connection has been restored", null, null, {
56
- autohide: 3
57
- });
58
- }
59
- }
60
-
61
- this.ws.onmessage = (event: MessageEvent<any>) => {
62
-
63
- const [name, rawData] = event.data.split('>');
64
- if (this.events[name] === undefined) {
65
- console.warn("Unknown command: " + name + `. Raw command:`, event.data);
66
- return;
67
- }
68
-
69
- let data;
70
- try {
71
- data = JSON.parse(rawData);
72
- } catch (error) {
73
- console.warn(`[socket] Error decoding data`, rawData, error);
74
- return;
75
- }
76
-
77
- for (const callback of this.events[name])
78
- callback(data);
79
-
80
- }
81
-
82
- this.ws.onerror = (event) => {
83
- console.log(`[socket] Network error for ${url}`, event);
84
- this.close();
85
- }
86
-
87
- this.ws.onclose = (event) => {
88
-
89
- // Fermeture volontaire = on ne retente pas de se connecter
90
- if (event.wasClean) {
91
- console.log(`[socket] Disconnected from ${url}. Reason: `, event.reason);
92
- return;
93
- }
94
-
95
- if (!netwokrStatusToast)
96
- netwokrStatusToast = this.context.toast.error("You're offline", "Please check your connection", null, {
97
- autohide: false,
98
- prison: true
99
- });
100
- console.log(`[socket] Disconnected from ${url}. Retry in 5 seconds ...`, event);
101
- setTimeout(() => this.connect(), 5000);
102
- }
103
-
104
- }
105
-
106
- public close() {
107
- if (this.ws)
108
- this.ws.close();
109
- }
110
-
111
- public on(event: string, callback: TEventCallback) {
112
-
113
- if (this.events[event] === undefined)
114
- this.events[event] = [callback]
115
- else
116
- this.events[event].push(callback)
117
-
118
- return this;
119
- }
120
-
121
- public off(event: string) {
122
-
123
- delete this.events[event];
124
-
125
- return this;
126
- }
127
-
128
- }
129
-
130
- export default class SocketClient {
131
-
132
- public scopes: { [name: string]: SocketScope } = {}
133
-
134
- public constructor( public context: ClientContext ) {
135
-
136
-
137
- }
138
-
139
- public open(path: string, commands: {[name: string]: TEventCallback} = {}) {
140
-
141
- if (!(path in this.scopes))
142
- this.scopes[path] = new SocketScope(path, this.context, commands);
143
-
144
- return this.scopes[path]
145
- }
146
-
147
- }
@@ -1,83 +0,0 @@
1
- /**
2
- * Copyright (c) Meta Platforms, Inc. and affiliates.
3
- *
4
- * This source code is licensed under the MIT license found in the
5
- * LICENSE file in the root directory of this source tree.
6
- *
7
- */
8
-
9
- import type { Klass, LexicalNode } from 'lexical';
10
-
11
- import { CodeHighlightNode, CodeNode } from '@lexical/code';
12
- import { HashtagNode } from '@lexical/hashtag';
13
- import { AutoLinkNode, LinkNode } from '@lexical/link';
14
- import { ListItemNode, ListNode } from '@lexical/list';
15
- import { MarkNode } from '@lexical/mark';
16
- import { OverflowNode } from '@lexical/overflow';
17
- import { HorizontalRuleNode } from '@lexical/react/LexicalHorizontalRuleNode';
18
- import { HeadingNode, QuoteNode } from '@lexical/rich-text';
19
- import { TableCellNode, TableNode, TableRowNode } from '@lexical/table';
20
-
21
- import { CollapsibleContainerNode } from '@client/components/Rte/plugins/CollapsiblePlugin/CollapsibleContainerNode';
22
- import { CollapsibleContentNode } from '@client/components/Rte/plugins/CollapsiblePlugin/CollapsibleContentNode';
23
- import { CollapsibleTitleNode } from '@client/components/Rte/plugins/CollapsiblePlugin/CollapsibleTitleNode';
24
- import { AutocompleteNode } from '@client/components/Rte/nodes/AutocompleteNode';
25
- import { EmojiNode } from '@client/components/Rte/nodes/EmojiNode';
26
- import { ImageNode } from '@client/components/Rte/nodes/ImageNode';
27
- import { InlineImageNode } from '@client/components/Rte/nodes/InlineImageNode/InlineImageNode';
28
- import { KeywordNode } from '@client/components/Rte/nodes/KeywordNode';
29
- import { LayoutContainerNode } from '@client/components/Rte/nodes/LayoutContainerNode';
30
- import { LayoutItemNode } from '@client/components/Rte/nodes/LayoutItemNode';
31
- import { MentionNode } from '@client/components/Rte/nodes/MentionNode';
32
- import { PageBreakNode } from '@client/components/Rte/nodes/PageBreakNode';
33
- import { PollNode } from '@client/components/Rte/nodes/PollNode';
34
- import { StickyNode } from '@client/components/Rte/nodes/StickyNode';
35
- import { TweetNode } from '@client/components/Rte/nodes/TweetNode';
36
- import { YouTubeNode } from '@client/components/Rte/nodes/YouTubeNode';
37
-
38
- import HeadingWithAnchorNode from '@client/components/Rte/nodes/HeadingNode';
39
- import ReferenceLinkNode from '@client/components/Rte/nodes/ReferenceLinkNode';
40
-
41
- const PlaygroundNodes: Array<Klass<LexicalNode>> = [
42
- /*HeadingNode, */HeadingWithAnchorNode,
43
- {
44
- replace: HeadingNode,
45
- with: (node) => {
46
- return new HeadingWithAnchorNode( node.getTag() );
47
- }
48
- },
49
- ListNode,
50
- ListItemNode,
51
- QuoteNode,
52
- CodeNode,
53
- TableNode,
54
- TableCellNode,
55
- TableRowNode,
56
- HashtagNode,
57
- CodeHighlightNode,
58
- AutoLinkNode,
59
- LinkNode,
60
- OverflowNode,
61
- PollNode,
62
- StickyNode,
63
- ImageNode, InlineImageNode,
64
- MentionNode,
65
- EmojiNode,
66
- AutocompleteNode,
67
- KeywordNode,
68
- HorizontalRuleNode,
69
- TweetNode,
70
- YouTubeNode,
71
- MarkNode,
72
- CollapsibleContainerNode,
73
- CollapsibleContentNode,
74
- CollapsibleTitleNode,
75
- PageBreakNode,
76
- LayoutContainerNode,
77
- LayoutItemNode,
78
-
79
- // Custom
80
- ReferenceLinkNode
81
- ];
82
-
83
- export default PlaygroundNodes;
@@ -1,90 +0,0 @@
1
- import hInterval from 'human-interval';
2
-
3
- type TObjDonneesStats = { [cheminStats: string]: number }
4
- export type TStat<TDonnees extends TObjDonneesStats> = { date: string } & TDonnees
5
- export type TTimeStat<TDonnees extends TObjDonneesStats> = { time: number } & TDonnees
6
-
7
- /*----------------------------------
8
- - OUTILS DE TRAITEMENT
9
- ----------------------------------*/
10
- type TRetourStats<TDonnees extends TObjDonneesStats> = {
11
- graph: TTimeStat<TDonnees>[],
12
- total: TDonnees,
13
-
14
- start: Date,
15
- end: Date,
16
- interval: number,
17
- }
18
-
19
- export const buildStats = <TDonnees extends TObjDonneesStats>(
20
- periodStr: string,
21
- intervalStr: string,
22
- data: TStat<TDonnees>[]
23
- ): TRetourStats<TDonnees> => {
24
-
25
- // NOTE: On ne génère pas le timestamp via la bdd pour éviter les incohérences de timezone
26
-
27
- if (!Array.isArray(data)) {
28
- console.log('data =', data);
29
- throw new Error(`Stats data must be an array (received ${typeof data}). See console for full provided data.`);
30
- }
31
-
32
- const periodTime = hInterval(periodStr);
33
- if (periodTime === undefined) throw new Error(`Invalid period string`);
34
- const intervalTime = hInterval(intervalStr);
35
- if (intervalTime === undefined) throw new Error(`Invalid interval string`);
36
-
37
- let start = Date.now() - periodTime
38
- start -= start % intervalTime; // Round start date to the specified interval
39
- const end = Date.now()
40
-
41
- const total: TDonnees = {} as TDonnees
42
- let graph: TTimeStat<TDonnees>[] = []
43
- if (data.length > 0) {
44
-
45
- // Group data by time
46
- const groups: { [timestamp: number]: TTimeStat<TDonnees> } = {};
47
- for (const { date, ...stats } of data) {
48
-
49
- if (date === undefined)
50
- throw new Error(`La date est absente des données statistiques. Est-elle bien spécifiée dans le SELECT ?`);
51
-
52
- const timeA = new Date(date).getTime();
53
- groups[timeA] = { time: timeA, ...stats };
54
- }
55
-
56
- // Completion
57
- for (let timeA = start; timeA <= end; timeA += intervalTime) {
58
-
59
- const stats = { time: timeA, ...total }
60
-
61
- if (groups[timeA] !== undefined)
62
- for (const nom in groups[timeA]) {
63
-
64
- // numeric value only
65
- if (typeof groups[timeA][nom] !== 'number')
66
- continue;
67
-
68
- // Add cumulated value
69
- stats[nom] = stats[nom] === undefined
70
- ? groups[timeA][nom]
71
- : stats[nom] + groups[timeA][nom];
72
-
73
- total[nom] = stats[nom];
74
-
75
- }
76
-
77
- graph.push(stats);
78
-
79
- }
80
- }
81
-
82
- return {
83
- graph,
84
- total,
85
- start: new Date(start),
86
- end: new Date(end),
87
- interval: intervalTime
88
- };
89
-
90
- }
@@ -1,183 +0,0 @@
1
-
2
- import type Driver from '@server/services/disks/driver';
3
- import { Anomaly } from '@common/errors';
4
-
5
- export type LexicalNode = {
6
- version: number,
7
- type: string,
8
- children?: LexicalNode[],
9
- // Attachement
10
- src?: string;
11
- // Headhing
12
- text?: string;
13
- anchor?: string;
14
- tag?: string;
15
- }
16
-
17
- export type LexicalState = {
18
- root: LexicalNode
19
- }
20
-
21
- export type TRenderOptions = {
22
-
23
- format?: 'html' | 'text', // Default = html
24
- transform?: RteUtils["transformNode"],
25
-
26
- render?: (
27
- node: LexicalNode,
28
- parent: LexicalNode | null,
29
- options: TRenderOptions
30
- ) => Promise<LexicalNode>,
31
-
32
- attachements?: {
33
- disk: Driver,
34
- directory: string,
35
- prevVersion?: string | LexicalState | null,
36
- }
37
- }
38
-
39
- export type TSkeleton = {
40
- id: string,
41
- title: string,
42
- level: number,
43
- childrens: TSkeleton
44
- }[];
45
-
46
- export type TContentAssets = {
47
- attachements: string[],
48
- skeleton: TSkeleton
49
- }
50
-
51
- export default abstract class RteUtils {
52
-
53
- public async render(
54
- content: string | LexicalState,
55
- options: TRenderOptions = {}
56
- ): Promise<TContentAssets & {
57
- html: string | null,
58
- json: string | LexicalState,
59
- }> {
60
-
61
- // Transform content
62
- const assets: TContentAssets = {
63
- attachements: [],
64
- skeleton: []
65
- }
66
-
67
- // Parse content if string
68
- let json = this.parseState(content);
69
- if (json === false)
70
- return { html: '', json: content, ...assets }
71
-
72
- // Parse prev version if string
73
- if (typeof options?.attachements?.prevVersion === 'string') {
74
- try {
75
- options.attachements.prevVersion = JSON.parse(options.attachements.prevVersion) as LexicalState;
76
- } catch (error) {
77
- throw new Anomaly("Invalid JSON format for the given JSON RTE prev version.");
78
- }
79
- }
80
-
81
- const root = await this.processContent(json.root, null, async (node, parent) => {
82
- return await this.transformNode(node, parent, assets, options);
83
- });
84
-
85
- json = { ...json, root };
86
-
87
- // Delete unused attachements
88
- const attachementOptions = options?.attachements;
89
- if (attachementOptions && attachementOptions.prevVersion !== undefined) {
90
-
91
- await this.processContent(root, null, async (node) => {
92
- return await this.deleteUnusedFile(node, assets, attachementOptions);
93
- });
94
- }
95
-
96
- // Convert json to HTML
97
- let html: string | null;
98
- if (options.format === 'text')
99
- html = await this.jsonToText( json.root );
100
- else
101
- html = await this.jsonToHtml( json, options );
102
-
103
- return { html, json: content, ...assets };
104
- }
105
-
106
- private parseState( content: string | LexicalState ): LexicalState | false {
107
-
108
- if (typeof content === 'string' && content.trim().startsWith('{')) {
109
- try {
110
- return JSON.parse(content) as LexicalState;
111
- } catch (error) {
112
- throw new Anomaly("Invalid JSON format for the given JSON RTE content.");
113
- }
114
- } else if (content && typeof content === 'object' && content.root)
115
- return content;
116
- else
117
- return false;
118
-
119
- }
120
-
121
- protected jsonToText(root: LexicalNode): string {
122
- let result = '';
123
-
124
- function traverse(node: LexicalNode) {
125
- switch (node.type) {
126
- case 'text':
127
- // Leaf text node
128
- result += node.text ?? '';
129
- break;
130
- case 'linebreak':
131
- // Explicit line break node
132
- result += '\n';
133
- break;
134
- default:
135
- // Container or block node: dive into children if any
136
- if (node.children) {
137
- node.children.forEach(traverse);
138
- }
139
- // After finishing a block-level node, append newline
140
- if (isBlockNode(node.type)) {
141
- result += '\n';
142
- }
143
- break;
144
- }
145
- }
146
-
147
- // Heuristic: treat these as blocks
148
- function isBlockNode(type: string): boolean {
149
- return [
150
- 'root',
151
- 'paragraph',
152
- 'heading',
153
- 'listitem',
154
- 'unorderedlist',
155
- 'orderedlist',
156
- 'quote',
157
- 'codeblock',
158
- 'table',
159
- ].includes(type);
160
- }
161
-
162
- traverse(root);
163
-
164
- // Trim trailing whitespace/newlines
165
- return result.replace(/\s+$/, '');
166
- }
167
-
168
- public abstract jsonToHtml( json: LexicalState, options: TRenderOptions ): Promise<string | null>;
169
-
170
- protected abstract processContent(
171
- node: LexicalNode,
172
- parent: LexicalNode | null,
173
- callback: (node: LexicalNode, parent: LexicalNode | null) => Promise<LexicalNode>
174
- ): Promise<LexicalNode>;
175
-
176
- protected abstract transformNode( node: LexicalNode, parent: LexicalNode | null, assets: TContentAssets, options: TRenderOptions ): Promise<LexicalNode>;
177
-
178
- protected abstract deleteUnusedFile(
179
- node: LexicalNode,
180
- assets: TContentAssets,
181
- options: NonNullable<TRenderOptions["attachements"]>
182
- ): Promise<LexicalNode>;
183
- }