odac 0.9.0 → 1.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 (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 +72 -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 +81 -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
@@ -5,21 +5,21 @@ Backend JavaScript allows you to execute JavaScript code during template renderi
5
5
  ### Basic Usage
6
6
 
7
7
  ```html
8
- <script:candy>
8
+ <script:odac>
9
9
  // This runs on the SERVER during template rendering
10
10
  let total = 0;
11
11
  for (let item of cart) {
12
12
  total += item.price * item.quantity;
13
13
  }
14
- </script:candy>
14
+ </script:odac>
15
15
 
16
- <p>Total: $<candy var="total" /></p>
16
+ <p>Total: $<odac var="total" /></p>
17
17
  ```
18
18
 
19
19
  ### Key Characteristics
20
20
 
21
21
  - ✅ Runs on the **server** during template rendering
22
- - ✅ Has access to all backend variables and Candy object
22
+ - ✅ Has access to all backend variables and Odac object
23
23
  - ✅ Perfect for calculations, data manipulation, filtering
24
24
  - ✅ Full IDE syntax highlighting and autocomplete
25
25
  - ❌ Does NOT run in the browser
@@ -39,19 +39,19 @@ You have access to all variables set in the controller:
39
39
 
40
40
  ```javascript
41
41
  // Controller
42
- module.exports = async function(Candy) {
43
- Candy.set('products', [
42
+ module.exports = async function(Odac) {
43
+ Odac.set('products', [
44
44
  { name: 'Laptop', price: 999, quantity: 2 },
45
45
  { name: 'Mouse', price: 29, quantity: 5 }
46
46
  ])
47
47
 
48
- Candy.View.skeleton('main').set('content', 'cart')
48
+ Odac.View.skeleton('main').set('content', 'cart')
49
49
  }
50
50
  ```
51
51
 
52
52
  ```html
53
53
  <!-- View -->
54
- <script:candy>
54
+ <script:odac>
55
55
  let total = 0;
56
56
  let itemCount = 0;
57
57
 
@@ -61,30 +61,30 @@ module.exports = async function(Candy) {
61
61
  }
62
62
 
63
63
  const avgPrice = total / itemCount;
64
- </script:candy>
64
+ </script:odac>
65
65
 
66
66
  <div class="cart-summary">
67
- <p>Total Items: <candy var="itemCount" /></p>
68
- <p>Total Price: $<candy var="total" /></p>
69
- <p>Average Price: $<candy var="avgPrice.toFixed(2)" /></p>
67
+ <p>Total Items: <odac var="itemCount" /></p>
68
+ <p>Total Price: $<odac var="total" /></p>
69
+ <p>Average Price: $<odac var="avgPrice.toFixed(2)" /></p>
70
70
  </div>
71
71
  ```
72
72
 
73
- ### Accessing the Candy Object
73
+ ### Accessing the Odac Object
74
74
 
75
- Full access to the Candy object and all its methods:
75
+ Full access to the Odac object and all its methods:
76
76
 
77
77
  ```html
78
- <script:candy>
79
- const isLoggedIn = Candy.Auth.check();
80
- const currentUser = isLoggedIn ? Candy.Auth.user() : null;
81
- const requestMethod = Candy.Request.method;
82
- const currentUrl = Candy.Request.url;
83
- </script:candy>
84
-
85
- <candy:if condition="isLoggedIn">
86
- <p>Welcome, <candy var="currentUser.name" />!</p>
87
- </candy:if>
78
+ <script:odac>
79
+ const isLoggedIn = Odac.Auth.check();
80
+ const currentUser = isLoggedIn ? Odac.Auth.user() : null;
81
+ const requestMethod = Odac.Request.method;
82
+ const currentUrl = Odac.Request.url;
83
+ </script:odac>
84
+
85
+ <odac:if condition="isLoggedIn">
86
+ <p>Welcome, <odac var="currentUser.name" />!</p>
87
+ </odac:if>
88
88
  ```
89
89
 
90
90
  ### Practical Examples
@@ -92,7 +92,7 @@ Full access to the Candy object and all its methods:
92
92
  #### Shopping Cart Calculations
93
93
 
94
94
  ```html
95
- <script:candy>
95
+ <script:odac>
96
96
  let subtotal = 0;
97
97
  let totalItems = 0;
98
98
 
@@ -104,28 +104,28 @@ Full access to the Candy object and all its methods:
104
104
  const tax = subtotal * 0.18; // 18% tax
105
105
  const shipping = subtotal > 100 ? 0 : 10;
106
106
  const total = subtotal + tax + shipping;
107
- </script:candy>
107
+ </script:odac>
108
108
 
109
109
  <div class="cart-summary">
110
110
  <h3>Order Summary</h3>
111
- <p>Items (<candy var="totalItems" />): $<candy var="subtotal.toFixed(2)" /></p>
112
- <p>Tax (18%): $<candy var="tax.toFixed(2)" /></p>
111
+ <p>Items (<odac var="totalItems" />): $<odac var="subtotal.toFixed(2)" /></p>
112
+ <p>Tax (18%): $<odac var="tax.toFixed(2)" /></p>
113
113
  <p>Shipping:
114
- <candy:if condition="shipping === 0">
114
+ <odac:if condition="shipping === 0">
115
115
  <span class="free">FREE</span>
116
- <candy:else>
117
- $<candy var="shipping.toFixed(2)" />
118
- </candy:if>
116
+ <odac:else>
117
+ $<odac var="shipping.toFixed(2)" />
118
+ </odac:if>
119
119
  </p>
120
120
  <hr>
121
- <p class="total">Total: $<candy var="total.toFixed(2)" /></p>
121
+ <p class="total">Total: $<odac var="total.toFixed(2)" /></p>
122
122
  </div>
123
123
  ```
124
124
 
125
125
  #### Filtering and Sorting
126
126
 
127
127
  ```html
128
- <script:candy>
128
+ <script:odac>
129
129
  // Filter active products
130
130
  const activeProducts = products.filter(p => p.isActive && p.stock > 0);
131
131
 
@@ -139,30 +139,30 @@ Full access to the Candy object and all its methods:
139
139
  const avgPrice = activeProducts.reduce((sum, p) => sum + p.price, 0) / activeProducts.length;
140
140
  const maxPrice = Math.max(...activeProducts.map(p => p.price));
141
141
  const minPrice = Math.min(...activeProducts.map(p => p.price));
142
- </script:candy>
142
+ </script:odac>
143
143
 
144
144
  <div class="products-section">
145
145
  <h2>Featured Products</h2>
146
- <p>Showing <candy var="featured.length" /> of <candy var="activeProducts.length" /> products</p>
147
- <p>Price range: $<candy var="minPrice" /> - $<candy var="maxPrice" /></p>
146
+ <p>Showing <odac var="featured.length" /> of <odac var="activeProducts.length" /> products</p>
147
+ <p>Price range: $<odac var="minPrice" /> - $<odac var="maxPrice" /></p>
148
148
 
149
- <candy:for in="featured" value="product">
149
+ <odac:for in="featured" value="product">
150
150
  <div class="product">
151
- <h3><candy var="product.name" /></h3>
152
- <p>$<candy var="product.price" /></p>
151
+ <h3><odac var="product.name" /></h3>
152
+ <p>$<odac var="product.price" /></p>
153
153
 
154
- <candy:if condition="product.price < avgPrice">
154
+ <odac:if condition="product.price < avgPrice">
155
155
  <span class="badge">Great Deal!</span>
156
- </candy:if>
156
+ </odac:if>
157
157
  </div>
158
- </candy:for>
158
+ </odac:for>
159
159
  </div>
160
160
  ```
161
161
 
162
162
  #### Date and Time Formatting
163
163
 
164
164
  ```html
165
- <script:candy>
165
+ <script:odac>
166
166
  const now = new Date();
167
167
  const postDate = new Date(post.createdAt);
168
168
 
@@ -188,12 +188,12 @@ Full access to the Candy object and all its methods:
188
188
  month: 'long',
189
189
  day: 'numeric'
190
190
  });
191
- </script:candy>
191
+ </script:odac>
192
192
 
193
193
  <div class="post">
194
- <h2><candy var="post.title" /></h2>
194
+ <h2><odac var="post.title" /></h2>
195
195
  <p class="meta">
196
- Posted <candy var="timeAgo" /> (<candy var="formattedDate" />)
196
+ Posted <odac var="timeAgo" /> (<odac var="formattedDate" />)
197
197
  </p>
198
198
  </div>
199
199
  ```
@@ -201,7 +201,7 @@ Full access to the Candy object and all its methods:
201
201
  #### Grouping Data
202
202
 
203
203
  ```html
204
- <script:candy>
204
+ <script:odac>
205
205
  // Group products by category
206
206
  const grouped = {};
207
207
  for (let product of products) {
@@ -213,31 +213,31 @@ Full access to the Candy object and all its methods:
213
213
 
214
214
  // Sort categories
215
215
  const categories = Object.keys(grouped).sort();
216
- </script:candy>
216
+ </script:odac>
217
217
 
218
218
  <div class="products-by-category">
219
- <candy:for in="categories" value="category">
219
+ <odac:for in="categories" value="category">
220
220
  <div class="category-section">
221
- <h2><candy var="category" /></h2>
222
- <p><candy var="grouped[category].length" /> products</p>
221
+ <h2><odac var="category" /></h2>
222
+ <p><odac var="grouped[category].length" /> products</p>
223
223
 
224
- <candy:for in="grouped[category]" value="product">
224
+ <odac:for in="grouped[category]" value="product">
225
225
  <div class="product">
226
- <h3><candy var="product.name" /></h3>
227
- <p>$<candy var="product.price" /></p>
226
+ <h3><odac var="product.name" /></h3>
227
+ <p>$<odac var="product.price" /></p>
228
228
  </div>
229
- </candy:for>
229
+ </odac:for>
230
230
  </div>
231
- </candy:for>
231
+ </odac:for>
232
232
  </div>
233
233
  ```
234
234
 
235
235
  #### Pagination Logic
236
236
 
237
237
  ```html
238
- <script:candy>
238
+ <script:odac>
239
239
  const itemsPerPage = 10;
240
- const currentPage = parseInt(Candy.Request.get('page')) || 1;
240
+ const currentPage = parseInt(Odac.Request.get('page')) || 1;
241
241
  const totalItems = products.length;
242
242
  const totalPages = Math.ceil(totalItems / itemsPerPage);
243
243
 
@@ -247,34 +247,34 @@ Full access to the Candy object and all its methods:
247
247
 
248
248
  const hasPrevious = currentPage > 1;
249
249
  const hasNext = currentPage < totalPages;
250
- </script:candy>
250
+ </script:odac>
251
251
 
252
252
  <div class="products">
253
- <candy:for in="currentItems" value="product">
253
+ <odac:for in="currentItems" value="product">
254
254
  <div class="product">
255
- <h3><candy var="product.name" /></h3>
255
+ <h3><odac var="product.name" /></h3>
256
256
  </div>
257
- </candy:for>
257
+ </odac:for>
258
258
  </div>
259
259
 
260
260
  <div class="pagination">
261
- <candy:if condition="hasPrevious">
262
- <a href="?page=<candy var="currentPage - 1" />">Previous</a>
263
- </candy:if>
261
+ <odac:if condition="hasPrevious">
262
+ <a href="?page=<odac var="currentPage - 1" />">Previous</a>
263
+ </odac:if>
264
264
 
265
- <span>Page <candy var="currentPage" /> of <candy var="totalPages" /></span>
265
+ <span>Page <odac var="currentPage" /> of <odac var="totalPages" /></span>
266
266
 
267
- <candy:if condition="hasNext">
268
- <a href="?page=<candy var="currentPage + 1" />">Next</a>
269
- </candy:if>
267
+ <odac:if condition="hasNext">
268
+ <a href="?page=<odac var="currentPage + 1" />">Next</a>
269
+ </odac:if>
270
270
  </div>
271
271
  ```
272
272
 
273
273
  #### Complex Conditional Logic
274
274
 
275
275
  ```html
276
- <script:candy>
277
- const user = Candy.Auth.check() ? Candy.Auth.user() : null;
276
+ <script:odac>
277
+ const user = Odac.Auth.check() ? Odac.Auth.user() : null;
278
278
 
279
279
  const canEdit = user && (
280
280
  user.role === 'admin' ||
@@ -286,59 +286,59 @@ Full access to the Candy object and all its methods:
286
286
  const canComment = user && !user.isBanned && post.commentsEnabled;
287
287
 
288
288
  const showActions = canEdit || canDelete || canComment;
289
- </script:candy>
289
+ </script:odac>
290
290
 
291
291
  <div class="post">
292
- <h2><candy var="post.title" /></h2>
293
- <p><candy var="post.content" /></p>
292
+ <h2><odac var="post.title" /></h2>
293
+ <p><odac var="post.content" /></p>
294
294
 
295
- <candy:if condition="showActions">
295
+ <odac:if condition="showActions">
296
296
  <div class="actions">
297
- <candy:if condition="canEdit">
298
- <a href="/posts/<candy var="post.id" />/edit">Edit</a>
299
- </candy:if>
297
+ <odac:if condition="canEdit">
298
+ <a href="/posts/<odac var="post.id" />/edit">Edit</a>
299
+ </odac:if>
300
300
 
301
- <candy:if condition="canDelete">
302
- <a href="/posts/<candy var="post.id" />/delete">Delete</a>
303
- </candy:if>
301
+ <odac:if condition="canDelete">
302
+ <a href="/posts/<odac var="post.id" />/delete">Delete</a>
303
+ </odac:if>
304
304
 
305
- <candy:if condition="canComment">
305
+ <odac:if condition="canComment">
306
306
  <a href="#comments">Add Comment</a>
307
- </candy:if>
307
+ </odac:if>
308
308
  </div>
309
- </candy:if>
309
+ </odac:if>
310
310
  </div>
311
311
  ```
312
312
 
313
313
  ### Multiple Script Blocks
314
314
 
315
- You can use multiple `<script:candy>` blocks in the same view:
315
+ You can use multiple `<script:odac>` blocks in the same view:
316
316
 
317
317
  ```html
318
- <script:candy>
318
+ <script:odac>
319
319
  let total = 0;
320
- </script:candy>
320
+ </script:odac>
321
321
 
322
- <candy:for in="items" value="item">
323
- <div><candy var="item.name" /></div>
322
+ <odac:for in="items" value="item">
323
+ <div><odac var="item.name" /></div>
324
324
 
325
- <script:candy>
325
+ <script:odac>
326
326
  total += item.price;
327
- </script:candy>
328
- </candy:for>
327
+ </script:odac>
328
+ </odac:for>
329
329
 
330
- <p>Total: $<candy var="total" /></p>
330
+ <p>Total: $<odac var="total" /></p>
331
331
  ```
332
332
 
333
333
  ### Comparison with Client-Side JavaScript
334
334
 
335
- **Backend JavaScript (`<script:candy>`):**
335
+ **Backend JavaScript (`<script:odac>`):**
336
336
  ```html
337
- <script:candy>
337
+ <script:odac>
338
338
  // Runs on SERVER during rendering
339
339
  const total = products.reduce((sum, p) => sum + p.price, 0);
340
- </script:candy>
341
- <p>Total: $<candy var="total" /></p>
340
+ </script:odac>
341
+ <p>Total: $<odac var="total" /></p>
342
342
  ```
343
343
 
344
344
  **Client-Side JavaScript (`<script>`):**
@@ -369,19 +369,19 @@ You can use multiple `<script:candy>` blocks in the same view:
369
369
 
370
370
  **Good:**
371
371
  ```html
372
- <script:candy>
372
+ <script:odac>
373
373
  const discountedPrice = product.price * (1 - product.discount / 100);
374
374
  const savings = product.price - discountedPrice;
375
- </script:candy>
375
+ </script:odac>
376
376
  ```
377
377
 
378
378
  **Avoid:**
379
379
  ```html
380
- <script:candy>
380
+ <script:odac>
381
381
  // Don't do this - should be in controller
382
- const users = await Candy.Mysql.query('SELECT * FROM users');
382
+ const users = await Odac.Mysql.query('SELECT * FROM users');
383
383
  const apiData = await fetch('https://api.example.com/data');
384
- </script:candy>
384
+ </script:odac>
385
385
  ```
386
386
 
387
387
  ### Common Use Cases