odac 0.9.0 → 1.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 (208) hide show
  1. package/.github/workflows/auto-pr-description.yml +0 -2
  2. package/.github/workflows/codeql.yml +46 -0
  3. package/.github/workflows/release.yml +13 -6
  4. package/.github/workflows/test-coverage.yml +10 -9
  5. package/.releaserc.js +9 -6
  6. package/CHANGELOG.md +62 -150
  7. package/CODE_OF_CONDUCT.md +1 -1
  8. package/CONTRIBUTING.md +8 -8
  9. package/LICENSE +21 -661
  10. package/README.md +12 -12
  11. package/SECURITY.md +4 -4
  12. package/bin/odac.js +101 -0
  13. package/{framework/web/candy.js → client/odac.js} +310 -44
  14. package/docs/backend/01-overview/{01-whats-in-the-candy-box.md → 01-whats-in-the-odac-box.md} +4 -2
  15. package/docs/backend/01-overview/02-super-handy-helper-functions.md +29 -1
  16. package/docs/backend/01-overview/03-development-server.md +11 -11
  17. package/docs/backend/02-structure/01-typical-project-layout.md +4 -4
  18. package/docs/backend/03-config/00-configuration-overview.md +6 -6
  19. package/docs/backend/03-config/01-database-connection.md +1 -1
  20. package/docs/backend/03-config/02-static-route-mapping-optional.md +4 -4
  21. package/docs/backend/03-config/04-environment-variables.md +20 -20
  22. package/docs/backend/03-config/05-early-hints.md +4 -4
  23. package/docs/backend/04-routing/01-basic-page-routes.md +4 -4
  24. package/docs/backend/04-routing/02-controller-less-view-routes.md +5 -5
  25. package/docs/backend/04-routing/03-api-and-data-routes.md +3 -3
  26. package/docs/backend/04-routing/04-authentication-aware-routes.md +5 -5
  27. package/docs/backend/04-routing/05-advanced-routing.md +3 -3
  28. package/docs/backend/04-routing/06-error-pages.md +17 -17
  29. package/docs/backend/04-routing/07-cron-jobs.md +13 -13
  30. package/docs/backend/04-routing/08-middleware.md +214 -0
  31. package/docs/backend/04-routing/09-websocket-auth-middleware.md +292 -0
  32. package/docs/backend/04-routing/09-websocket-examples.md +381 -0
  33. package/docs/backend/04-routing/09-websocket-quick-reference.md +211 -0
  34. package/docs/backend/04-routing/09-websocket.md +298 -0
  35. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +3 -3
  36. package/docs/backend/05-controllers/02-your-trusty-odac-assistant.md +41 -0
  37. package/docs/backend/05-controllers/03-controller-classes.md +19 -19
  38. package/docs/backend/05-forms/01-custom-forms.md +114 -114
  39. package/docs/backend/05-forms/02-automatic-database-insert.md +82 -82
  40. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +26 -26
  41. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +10 -10
  42. package/docs/backend/07-views/01-the-view-directory.md +1 -1
  43. package/docs/backend/07-views/02-rendering-a-view.md +22 -22
  44. package/docs/backend/07-views/03-template-syntax.md +52 -52
  45. package/docs/backend/07-views/03-variables.md +84 -84
  46. package/docs/backend/07-views/04-request-data.md +57 -57
  47. package/docs/backend/07-views/05-conditionals.md +78 -78
  48. package/docs/backend/07-views/06-loops.md +114 -114
  49. package/docs/backend/07-views/07-translations.md +66 -66
  50. package/docs/backend/07-views/08-backend-javascript.md +103 -103
  51. package/docs/backend/07-views/09-comments.md +71 -71
  52. package/docs/backend/08-database/01-database-connection.md +8 -8
  53. package/docs/backend/08-database/02-using-mysql.md +49 -49
  54. package/docs/backend/09-validation/01-the-validator-service.md +38 -38
  55. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +15 -15
  56. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +10 -10
  57. package/docs/backend/10-authentication/03-register.md +12 -12
  58. package/docs/backend/10-authentication/{04-candy-register-forms.md → 04-odac-register-forms.md} +141 -141
  59. package/docs/backend/10-authentication/05-session-management.md +10 -10
  60. package/docs/backend/10-authentication/{06-candy-login-forms.md → 06-odac-login-forms.md} +125 -125
  61. package/docs/backend/11-mail/01-the-mail-service.md +5 -5
  62. package/docs/backend/12-streaming/01-streaming-overview.md +96 -54
  63. package/docs/backend/13-utilities/{01-candy-var.md → 01-odac-var.md} +109 -109
  64. package/docs/frontend/01-overview/01-introduction.md +30 -30
  65. package/docs/frontend/02-ajax-navigation/01-quick-start.md +45 -45
  66. package/docs/frontend/02-ajax-navigation/02-configuration.md +14 -14
  67. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +36 -36
  68. package/docs/frontend/03-forms/01-form-handling.md +32 -32
  69. package/docs/frontend/04-api-requests/01-get-post.md +33 -33
  70. package/docs/frontend/05-streaming/01-client-streaming.md +15 -15
  71. package/docs/frontend/06-websocket/00-overview.md +76 -0
  72. package/docs/frontend/06-websocket/01-websocket-client.md +139 -0
  73. package/docs/frontend/06-websocket/02-shared-websocket.md +149 -0
  74. package/docs/index.json +49 -11
  75. package/eslint.config.mjs +6 -6
  76. package/{framework/index.js → index.js} +1 -1
  77. package/package.json +14 -39
  78. package/{framework/src → src}/Auth.js +59 -59
  79. package/{framework/src → src}/Config.js +3 -3
  80. package/{framework/src → src}/Lang.js +7 -7
  81. package/{framework/src → src}/Mail.js +5 -5
  82. package/{framework/src → src}/Mysql.js +42 -42
  83. package/src/Odac.js +112 -0
  84. package/{framework/src → src}/Request.js +38 -36
  85. package/{framework/src → src}/Route/Internal.js +116 -116
  86. package/src/Route/Middleware.js +75 -0
  87. package/src/Route.js +621 -0
  88. package/src/Server.js +22 -0
  89. package/{framework/src → src}/Stream.js +11 -3
  90. package/{framework/src → src}/Validator.js +21 -21
  91. package/{framework/src → src}/Var.js +5 -5
  92. package/{framework/src → src}/View/EarlyHints.js +1 -1
  93. package/{framework/src → src}/View/Form.js +69 -69
  94. package/{framework/src → src}/View.js +78 -81
  95. package/src/WebSocket.js +403 -0
  96. package/template/config.json +5 -0
  97. package/{web → template}/controller/page/about.js +6 -6
  98. package/{web → template}/controller/page/index.js +9 -9
  99. package/{web → template}/package.json +4 -5
  100. package/{web → template}/public/assets/css/style.css +4 -4
  101. package/{web → template}/public/assets/js/app.js +6 -6
  102. package/{web → template}/route/www.js +6 -6
  103. package/{web → template}/skeleton/main.html +1 -1
  104. package/{web → template}/view/content/about.html +5 -5
  105. package/{web → template}/view/content/home.html +12 -12
  106. package/template/view/footer/main.html +11 -0
  107. package/{web → template}/view/head/main.html +1 -1
  108. package/{web → template}/view/header/main.html +2 -2
  109. package/test/core/Candy.test.js +58 -58
  110. package/test/core/Commands.test.js +7 -7
  111. package/test/core/Config.test.js +82 -85
  112. package/test/core/Lang.test.js +2 -2
  113. package/test/core/Process.test.js +6 -6
  114. package/test/framework/Route.test.js +56 -37
  115. package/test/framework/View/EarlyHints.test.js +2 -2
  116. package/test/framework/WebSocket.test.js +100 -0
  117. package/test/framework/middleware.test.js +85 -0
  118. package/test/server/Api.test.js +31 -31
  119. package/test/server/DNS.test.js +11 -11
  120. package/test/server/Hub.test.js +497 -0
  121. package/test/server/Mail.account.test_.js +3 -3
  122. package/test/server/Mail.init.test_.js +10 -10
  123. package/test/server/Mail.test_.js +20 -20
  124. package/test/server/SSL.test_.js +54 -54
  125. package/test/server/Server.test.js +39 -39
  126. package/test/server/Service.test_.js +7 -7
  127. package/test/server/Subdomain.test.js +7 -7
  128. package/test/server/Web/Firewall.test.js +87 -87
  129. package/test/server/Web/Proxy.test.js +397 -0
  130. package/test/server/{Web.test_.js → Web.test.js} +137 -205
  131. package/test/server/__mocks__/fs.js +2 -2
  132. package/test/server/__mocks__/{globalCandy.js → globalOdac.js} +5 -5
  133. package/test/server/__mocks__/index.js +6 -6
  134. package/test/server/__mocks__/testFactories.js +1 -1
  135. package/test/server/__mocks__/testHelpers.js +7 -7
  136. package/.husky/pre-commit +0 -2
  137. package/.kiro/steering/code-style.md +0 -56
  138. package/.kiro/steering/product.md +0 -20
  139. package/.kiro/steering/structure.md +0 -77
  140. package/.kiro/steering/tech.md +0 -87
  141. package/AGENTS.md +0 -84
  142. package/bin/candy +0 -10
  143. package/bin/candypack +0 -10
  144. package/cli/index.js +0 -3
  145. package/cli/src/Cli.js +0 -348
  146. package/cli/src/Connector.js +0 -93
  147. package/cli/src/Monitor.js +0 -416
  148. package/core/Candy.js +0 -87
  149. package/core/Commands.js +0 -239
  150. package/core/Config.js +0 -1094
  151. package/core/Lang.js +0 -52
  152. package/core/Log.js +0 -43
  153. package/core/Process.js +0 -26
  154. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +0 -20
  155. package/docs/server/01-installation/01-quick-install.md +0 -19
  156. package/docs/server/01-installation/02-manual-installation-via-npm.md +0 -9
  157. package/docs/server/02-get-started/01-core-concepts.md +0 -7
  158. package/docs/server/02-get-started/02-basic-commands.md +0 -57
  159. package/docs/server/02-get-started/03-cli-reference.md +0 -276
  160. package/docs/server/02-get-started/04-cli-quick-reference.md +0 -102
  161. package/docs/server/03-service/01-start-a-new-service.md +0 -57
  162. package/docs/server/03-service/02-delete-a-service.md +0 -48
  163. package/docs/server/04-web/01-create-a-website.md +0 -36
  164. package/docs/server/04-web/02-list-websites.md +0 -9
  165. package/docs/server/04-web/03-delete-a-website.md +0 -29
  166. package/docs/server/05-subdomain/01-create-a-subdomain.md +0 -32
  167. package/docs/server/05-subdomain/02-list-subdomains.md +0 -33
  168. package/docs/server/05-subdomain/03-delete-a-subdomain.md +0 -41
  169. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +0 -34
  170. package/docs/server/07-mail/01-create-a-mail-account.md +0 -23
  171. package/docs/server/07-mail/02-delete-a-mail-account.md +0 -20
  172. package/docs/server/07-mail/03-list-mail-accounts.md +0 -20
  173. package/docs/server/07-mail/04-change-account-password.md +0 -23
  174. package/framework/src/Candy.js +0 -81
  175. package/framework/src/Route.js +0 -455
  176. package/framework/src/Server.js +0 -15
  177. package/locale/de-DE.json +0 -80
  178. package/locale/en-US.json +0 -79
  179. package/locale/es-ES.json +0 -80
  180. package/locale/fr-FR.json +0 -80
  181. package/locale/pt-BR.json +0 -80
  182. package/locale/ru-RU.json +0 -80
  183. package/locale/tr-TR.json +0 -85
  184. package/locale/zh-CN.json +0 -80
  185. package/server/index.js +0 -5
  186. package/server/src/Api.js +0 -88
  187. package/server/src/DNS.js +0 -940
  188. package/server/src/Hub.js +0 -535
  189. package/server/src/Mail.js +0 -571
  190. package/server/src/SSL.js +0 -180
  191. package/server/src/Server.js +0 -27
  192. package/server/src/Service.js +0 -248
  193. package/server/src/Subdomain.js +0 -64
  194. package/server/src/Web/Firewall.js +0 -170
  195. package/server/src/Web/Proxy.js +0 -134
  196. package/server/src/Web.js +0 -451
  197. package/server/src/mail/imap.js +0 -1091
  198. package/server/src/mail/server.js +0 -32
  199. package/server/src/mail/smtp.js +0 -786
  200. package/test/server/Client.test.js +0 -338
  201. package/test/server/__mocks__/http-proxy.js +0 -105
  202. package/watchdog/index.js +0 -3
  203. package/watchdog/src/Watchdog.js +0 -156
  204. package/web/config.json +0 -5
  205. package/web/view/footer/main.html +0 -11
  206. /package/{framework/src → src}/Env.js +0 -0
  207. /package/{framework/src → src}/Route/Cron.js +0 -0
  208. /package/{framework/src → src}/Token.js +0 -0
@@ -20,7 +20,7 @@ class View {
20
20
  end: '*/ html += `'
21
21
  },
22
22
  '{{': {
23
- function: '${Candy.Var(await ',
23
+ function: '${Odac.Var(await ',
24
24
  close: '}}',
25
25
  end: ').html().replace(/\\n/g, "<br>")}'
26
26
  },
@@ -30,7 +30,7 @@ class View {
30
30
  },
31
31
  component: {
32
32
  // TODO: Implement component
33
- // <candy:component name="navbar" title="Dashboard"/>
33
+ // <odac:component name="navbar" title="Dashboard"/>
34
34
  },
35
35
  continue: {
36
36
  function: 'continue;',
@@ -50,7 +50,7 @@ class View {
50
50
  },
51
51
  fetch: {
52
52
  // TODO: Implement fetch
53
- // <candy:fetch fetch="/get/products" as="data" method="GET" headers="{}" body="null" refresh="false">
53
+ // <odac:fetch fetch="/get/products" as="data" method="GET" headers="{}" body="null" refresh="false">
54
54
  },
55
55
  for: {
56
56
  function: '{ let _arr = $constructor; for(let $key in _arr){ let $value = _arr[$key];',
@@ -68,16 +68,16 @@ class View {
68
68
  condition: true
69
69
  }
70
70
  },
71
- '<candy:js>': {
71
+ '<odac:js>': {
72
72
  end: ' html += `',
73
73
  function: '`; ',
74
- close: '</candy:js>'
74
+ close: '</odac:js>'
75
75
  },
76
76
  lazy: {
77
77
  // TODO: Implement lazy
78
- // <candy:lazy>
79
- // <candy:component name="profile-card" data="user"/>
80
- // </candy:lazy>
78
+ // <odac:lazy>
79
+ // <odac:component name="profile-card" data="user"/>
80
+ // </odac:lazy>
81
81
  },
82
82
  list: {
83
83
  arguments: {
@@ -98,21 +98,21 @@ class View {
98
98
  }
99
99
  }
100
100
  #part = {}
101
- #candy = null
101
+ #odac = null
102
102
 
103
- constructor(candy) {
104
- this.#candy = candy
103
+ constructor(odac) {
104
+ this.#odac = odac
105
105
 
106
- if (!global.Candy?.View?.EarlyHints) {
107
- const config = candy.Config?.earlyHints
106
+ if (!global.Odac?.View?.EarlyHints) {
107
+ const config = odac.Config?.earlyHints
108
108
  this.#earlyHints = new EarlyHints(config)
109
109
  this.#earlyHints.init()
110
110
 
111
- if (!global.Candy) global.Candy = {}
112
- if (!global.Candy.View) global.Candy.View = {}
113
- global.Candy.View.EarlyHints = this.#earlyHints
111
+ if (!global.Odac) global.Odac = {}
112
+ if (!global.Odac.View) global.Odac.View = {}
113
+ global.Odac.View.EarlyHints = this.#earlyHints
114
114
  } else {
115
- this.#earlyHints = global.Candy.View.EarlyHints
115
+ this.#earlyHints = global.Odac.View.EarlyHints
116
116
  }
117
117
  }
118
118
 
@@ -123,24 +123,24 @@ class View {
123
123
 
124
124
  // - PRINT VIEW
125
125
  async print() {
126
- if (this.#candy.Request.res.finished) return
126
+ if (this.#odac.Request.res.finished) return
127
127
 
128
- const routePath = this.#candy.Request.req.url.split('?')[0]
128
+ const routePath = this.#odac.Request.req.url.split('?')[0]
129
129
 
130
130
  // Handle AJAX load requests
131
- if (this.#candy.Request.isAjaxLoad === true && this.#candy.Request.ajaxLoad && this.#candy.Request.ajaxLoad.length > 0) {
131
+ if (this.#odac.Request.isAjaxLoad === true && this.#odac.Request.ajaxLoad && this.#odac.Request.ajaxLoad.length > 0) {
132
132
  let output = {}
133
133
  let variables = {}
134
134
 
135
135
  // Collect variables marked for AJAX
136
- for (let key in this.#candy.Request.variables) {
137
- if (this.#candy.Request.variables[key].ajax) {
138
- variables[key] = this.#candy.Request.variables[key].value
136
+ for (let key in this.#odac.Request.variables) {
137
+ if (this.#odac.Request.variables[key].ajax) {
138
+ variables[key] = this.#odac.Request.variables[key].value
139
139
  }
140
140
  }
141
141
 
142
142
  // Render requested elements
143
- for (let element of this.#candy.Request.ajaxLoad) {
143
+ for (let element of this.#odac.Request.ajaxLoad) {
144
144
  if (this.#part[element]) {
145
145
  let viewPath = this.#part[element]
146
146
  if (viewPath.includes('.')) viewPath = viewPath.replace(/\./g, '/')
@@ -151,14 +151,14 @@ class View {
151
151
  }
152
152
 
153
153
  const currentSkeleton = this.#part.skeleton || 'main'
154
- const clientSkeleton = this.#candy.Request.clientSkeleton
154
+ const clientSkeleton = this.#odac.Request.clientSkeleton
155
155
  const skeletonChanged = clientSkeleton && clientSkeleton !== currentSkeleton
156
156
 
157
- this.#candy.Request.header('Content-Type', 'application/json')
158
- this.#candy.Request.header('X-Candy-Page', this.#candy.Request.page || '')
159
- this.#candy.Request.header('Vary', 'X-Candy')
157
+ this.#odac.Request.header('Content-Type', 'application/json')
158
+ this.#odac.Request.header('X-Odac-Page', this.#odac.Request.page || '')
159
+ this.#odac.Request.header('Vary', 'X-Odac')
160
160
 
161
- this.#candy.Request.end({
161
+ this.#odac.Request.end({
162
162
  output: output,
163
163
  variables: variables,
164
164
  skeletonChanged: skeletonChanged
@@ -171,7 +171,7 @@ class View {
171
171
  if (this.#part.skeleton && fs.existsSync(`./skeleton/${this.#part.skeleton}.html`)) {
172
172
  result = fs.readFileSync(`./skeleton/${this.#part.skeleton}.html`, 'utf8')
173
173
 
174
- // Add data-candy-navigate to content wrapper for auto-navigation
174
+ // Add data-odac-navigate to content wrapper for auto-navigation
175
175
  result = this.#addNavigateAttribute(result)
176
176
 
177
177
  for (let key in this.#part) {
@@ -183,8 +183,8 @@ class View {
183
183
  }
184
184
  }
185
185
  if (this.#part.all) {
186
- let parts = result.match(/{{.*?}}/g).map(part => part.replace(/{{|}}/g, '').trim())
187
- if (parts)
186
+ let parts = (result.match(/{{.*?}}/g) || []).map(part => part.replace(/{{|}}/g, '').trim())
187
+ if (parts.length > 0)
188
188
  for (let part of parts) {
189
189
  part = part.trim()
190
190
  let file = this.#part.all.split('.')
@@ -198,7 +198,7 @@ class View {
198
198
  }
199
199
 
200
200
  if (result) {
201
- const hasEarlyHints = this.#candy.Request.hasEarlyHints()
201
+ const hasEarlyHints = this.#odac.Request.hasEarlyHints()
202
202
 
203
203
  if (!hasEarlyHints) {
204
204
  const detectedResources = this.#earlyHints.extractFromHtml(result)
@@ -209,27 +209,27 @@ class View {
209
209
  }
210
210
  }
211
211
 
212
- this.#candy.Request.header('Content-Type', 'text/html')
213
- this.#candy.Request.end(result)
212
+ this.#odac.Request.header('Content-Type', 'text/html')
213
+ this.#odac.Request.end(result)
214
214
  }
215
215
 
216
- #parseCandyTag(content) {
216
+ #parseOdacTag(content) {
217
217
  // Parse backend comments
218
- // Multi-line: <!--candy ... candy-->
219
- // Single-line: <!--candy ... -->
220
- content = content.replace(/<!--candy([\s\S]*?)(?:candy-->|-->)/g, () => {
218
+ // Multi-line: <!--odac ... odac-->
219
+ // Single-line: <!--odac ... -->
220
+ content = content.replace(/<!--odac([\s\S]*?)(?:odac-->|-->)/g, () => {
221
221
  return ''
222
222
  })
223
223
 
224
- // Parse <script:candy> tags (IDE-friendly JavaScript with backend execution)
225
- content = content.replace(/<script:candy([^>]*)>([\s\S]*?)<\/script:candy>/g, (fullMatch, attributes, jsContent) => {
226
- return `<candy:js>${jsContent}</candy:js>`
224
+ // Parse <script:odac> tags (IDE-friendly JavaScript with backend execution)
225
+ content = content.replace(/<script:odac([^>]*)>([\s\S]*?)<\/script:odac>/g, (fullMatch, attributes, jsContent) => {
226
+ return `<odac:js>${jsContent}</odac:js>`
227
227
  })
228
228
 
229
- content = content.replace(/<candy:else\s*\/>/g, '<candy:else>')
230
- content = content.replace(/<candy:elseif\s+([^>]*?)\/>/g, '<candy:elseif $1>')
229
+ content = content.replace(/<odac:else\s*\/>/g, '<odac:else>')
230
+ content = content.replace(/<odac:elseif\s+([^>]*?)\/>/g, '<odac:elseif $1>')
231
231
 
232
- content = content.replace(/<candy([^>]*?)\/>/g, (fullMatch, attributes) => {
232
+ content = content.replace(/<odac([^>]*?)\/>/g, (fullMatch, attributes) => {
233
233
  attributes = attributes.trim()
234
234
 
235
235
  const attrs = {}
@@ -255,9 +255,9 @@ class View {
255
255
 
256
256
  let depth = 0
257
257
  let maxDepth = 10
258
- while (depth < maxDepth && content.includes('<candy')) {
258
+ while (depth < maxDepth && content.includes('<odac')) {
259
259
  const before = content
260
- content = content.replace(/<candy([^>]*)>((?:(?!<candy)[\s\S])*?)<\/candy>/g, (fullMatch, attributes, innerContent) => {
260
+ content = content.replace(/<odac([^>]*)>((?:(?!<odac)[\s\S])*?)<\/odac>/g, (fullMatch, attributes, innerContent) => {
261
261
  attributes = attributes.trim()
262
262
  innerContent = innerContent.trim()
263
263
 
@@ -288,7 +288,7 @@ class View {
288
288
  if (variable.startsWith("'") && variable.endsWith("'")) {
289
289
  placeholders.push(variable)
290
290
  } else {
291
- placeholders.push(`Candy.Var(await ${variable}).html().replace(/\\n/g, "<br>")`)
291
+ placeholders.push(`Odac.Var(await ${variable}).html().replace(/\\n/g, "<br>")`)
292
292
  }
293
293
  return `%s${placeholderIndex++}`
294
294
  })
@@ -322,20 +322,20 @@ class View {
322
322
  let content = fs.readFileSync(file, 'utf8')
323
323
 
324
324
  if (this.#cache[file]?.mtime !== mtime) {
325
- content = Form.parse(content, this.#candy)
325
+ content = Form.parse(content, this.#odac)
326
326
 
327
327
  const jsBlocks = []
328
- content = content.replace(/<script:candy([^>]*)>([\s\S]*?)<\/script:candy>/g, (match, attrs, jsContent) => {
329
- const placeholder = `___CANDY_JS_BLOCK_${jsBlocks.length}___`
328
+ content = content.replace(/<script:odac([^>]*)>([\s\S]*?)<\/script:odac>/g, (match, attrs, jsContent) => {
329
+ const placeholder = `___ODAC_JS_BLOCK_${jsBlocks.length}___`
330
330
  jsBlocks.push(jsContent)
331
- return `<script:candy${attrs}>${placeholder}</script:candy>`
331
+ return `<script:odac${attrs}>${placeholder}</script:odac>`
332
332
  })
333
333
 
334
- content = this.#parseCandyTag(content)
335
- content = content.replace(/`/g, '\\`').replace(/\$\{/g, '\\${')
334
+ content = this.#parseOdacTag(content)
335
+ content = content.replace(/`/g, '\\\\`').replace(/\$\{/g, '\\\\${')
336
336
 
337
337
  jsBlocks.forEach((jsContent, index) => {
338
- content = content.replace(`___CANDY_JS_BLOCK_${index}___`, jsContent)
338
+ content = content.replace(`___ODAC_JS_BLOCK_${index}___`, jsContent)
339
339
  })
340
340
 
341
341
  let result = 'html += `\n' + content + '\n`'
@@ -345,11 +345,11 @@ class View {
345
345
  let func = this.#functions[key]
346
346
  let matches = func.close
347
347
  ? result.match(new RegExp(`${key}[\\s\\S]*?${func.close}`, 'g'))
348
- : result.match(new RegExp(`<candy:${key}(?:\\s+[^>]*?(?:"[^"]*"|'[^']*'|[^"'>])*)?>`, 'g'))
348
+ : result.match(new RegExp(`<odac:${key}(?:\\s+[^>]*?(?:"[^"]*"|'[^']*'|[^"'>])*)?>`, 'g'))
349
349
  if (!matches) continue
350
350
  for (let match of matches) {
351
351
  let matchForParsing = match
352
- if (!func.close) matchForParsing = matchForParsing.replace(/^<candy:/, '').replace(/>$/, '')
352
+ if (!func.close) matchForParsing = matchForParsing.replace(/^<odac:/, '').replace(/>$/, '')
353
353
  const attrRegex = /(\w+)(?:=(["'])((?:(?!\2).)*)\2|=([^\s>]+))?/g
354
354
  let attrMatch
355
355
  const args = []
@@ -401,10 +401,7 @@ class View {
401
401
  result = result.replace(match, fun + match.substring(key.length, match.length - func.close.length) + func.end)
402
402
  } else {
403
403
  result = result.replace(match, (func.replace ? `<${[func.replace, att].join(' ')}>` : '') + '`; ' + fun + ' html += `')
404
- result = result.replace(
405
- `</candy:${key}>`,
406
- '`; ' + (func.end ?? '}') + ' html += `' + (func.replace ? `</${func.replace}>` : '')
407
- )
404
+ result = result.replace(`</odac:${key}>`, '`; ' + (func.end ?? '}') + ' html += `' + (func.replace ? `</${func.replace}>` : ''))
408
405
  }
409
406
  }
410
407
  }
@@ -412,21 +409,21 @@ class View {
412
409
  if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, {recursive: true})
413
410
  fs.writeFileSync(
414
411
  `${CACHE_DIR}/${cache}`,
415
- `module.exports = async (Candy, get, __) => {\nlet html = '';\n${result}\nreturn html.trim()\n}`
412
+ `module.exports = async (Odac, get, __) => {\nlet html = '';\n${result}\nreturn html.trim()\n}`
416
413
  )
417
414
  delete require.cache[require.resolve(`${__dir}/${CACHE_DIR}/${cache}`)]
418
- if (!Candy.View) Candy.View = {}
419
- if (!Candy.View.cache) Candy.View.cache = {}
420
- Candy.View.cache[file] = {
415
+ if (!Odac.View) Odac.View = {}
416
+ if (!Odac.View.cache) Odac.View.cache = {}
417
+ Odac.View.cache[file] = {
421
418
  mtime: mtime,
422
419
  cache: cache
423
420
  }
424
421
  }
425
422
  try {
426
- return await require(`${__dir}/${CACHE_DIR}/${Candy.View.cache[file].cache}`)(
427
- this.#candy,
428
- key => this.#candy.Request.get(key),
429
- (...args) => this.#candy.Lang.get(...args)
423
+ return await require(`${__dir}/${CACHE_DIR}/${Odac.View.cache[file].cache}`)(
424
+ this.#odac,
425
+ key => this.#odac.Request.get(key),
426
+ (...args) => this.#odac.Lang.get(...args)
430
427
  )
431
428
  } catch (e) {
432
429
  let stackLine = e.stack.split('\n')[1].match(/:(\d+):\d+/)
@@ -441,8 +438,8 @@ class View {
441
438
  if (args.length === 1 && typeof args[0] === 'object') for (let key in args[0]) this.#part[key] = args[0][key]
442
439
  else if (args.length === 2) this.#part[args[0]] = args[1]
443
440
 
444
- if (!this.#candy.Request.page) {
445
- this.#candy.Request.page = this.#part.content || this.#part.all || ''
441
+ if (!this.#odac.Request.page) {
442
+ this.#odac.Request.page = this.#part.content || this.#part.all || ''
446
443
  }
447
444
 
448
445
  this.#sendEarlyHintsIfAvailable()
@@ -457,21 +454,21 @@ class View {
457
454
 
458
455
  #addNavigateAttribute(skeleton) {
459
456
  skeleton = skeleton.replace(/(<[^>]+>)(\s*\{\{\s*CONTENT\s*\}\})/, (match, openTag, content) => {
460
- if (openTag.includes('data-candy-navigate')) return match
461
- const tagWithAttr = openTag.slice(0, -1) + ' data-candy-navigate="content">'
457
+ if (openTag.includes('data-odac-navigate')) return match
458
+ const tagWithAttr = openTag.slice(0, -1) + ' data-odac-navigate="content">'
462
459
  return tagWithAttr + content
463
460
  })
464
461
 
465
462
  const skeletonName = this.#part.skeleton || 'main'
466
- const pageName = this.#candy.Request.page || ''
463
+ const pageName = this.#odac.Request.page || ''
467
464
 
468
465
  skeleton = skeleton.replace(/<html([^>]*)>/, (match, attrs) => {
469
466
  const updates = []
470
- if (!attrs.includes('data-candy-skeleton')) {
471
- updates.push(`data-candy-skeleton="${skeletonName}"`)
467
+ if (!attrs.includes('data-odac-skeleton')) {
468
+ updates.push(`data-odac-skeleton="${skeletonName}"`)
472
469
  }
473
- if (!attrs.includes('data-candy-page')) {
474
- updates.push(`data-candy-page="${pageName}"`)
470
+ if (!attrs.includes('data-odac-page')) {
471
+ updates.push(`data-odac-page="${pageName}"`)
475
472
  }
476
473
  if (updates.length === 0) return match
477
474
  return `<html${attrs} ${updates.join(' ')}>`
@@ -481,9 +478,9 @@ class View {
481
478
  }
482
479
 
483
480
  #sendEarlyHintsIfAvailable() {
484
- if (this.#candy.Request.res.headersSent) return
481
+ if (this.#odac.Request.res.headersSent) return
485
482
 
486
- const routePath = this.#candy.Request.req.url.split('?')[0]
483
+ const routePath = this.#odac.Request.req.url.split('?')[0]
487
484
  const viewPaths = []
488
485
 
489
486
  if (this.#part.skeleton) {
@@ -505,7 +502,7 @@ class View {
505
502
  }
506
503
 
507
504
  if (hints && hints.length > 0) {
508
- this.#candy.Request.setEarlyHints(hints)
505
+ this.#odac.Request.setEarlyHints(hints)
509
506
  }
510
507
  }
511
508
  }