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
@@ -0,0 +1,298 @@
1
+ # WebSocket Routes
2
+
3
+ Odac provides built-in WebSocket support for real-time bidirectional communication.
4
+
5
+ ## Route Definition
6
+
7
+ WebSocket routes are defined in your route files (e.g., `route/www.js` or `route/websocket.js`) using `Odac.Route.ws()`:
8
+
9
+ ```javascript
10
+ // route/websocket.js
11
+ Odac.Route.ws('/chat', Odac => {
12
+ Odac.ws.send({type: 'welcome', message: 'Connected!'})
13
+
14
+ Odac.ws.on('message', data => {
15
+ console.log('Received:', data)
16
+ Odac.ws.send({type: 'echo', data})
17
+ })
18
+
19
+ Odac.ws.on('close', () => {
20
+ console.log('Client disconnected')
21
+ })
22
+ })
23
+ ```
24
+
25
+ **Handler Signature:**
26
+
27
+ The handler receives the `Odac` instance as the only parameter. The WebSocket client is accessible via `Odac.ws`, providing a consistent API with HTTP routes where everything is accessed through the `Odac` object.
28
+
29
+ **CSRF Token Protection:**
30
+
31
+ By default, WebSocket routes require a valid CSRF token (like `Route.get()` and `Route.post()`). The token is sent via the `Sec-WebSocket-Protocol` header during the initial handshake.
32
+
33
+ **Disable token requirement:**
34
+ ```javascript
35
+ Odac.Route.ws('/public', Odac => {
36
+ Odac.ws.send({type: 'public'})
37
+ }, {token: false})
38
+ ```
39
+
40
+ **Route File Structure:**
41
+ ```
42
+ web/
43
+ ├── route/
44
+ │ ├── www.js # HTTP routes
45
+ │ └── websocket.js # WebSocket routes (recommended)
46
+ ```
47
+
48
+ ## WebSocket Client API (Odac.ws)
49
+
50
+ The WebSocket client is accessible via `Odac.ws` in your handler, providing a consistent API pattern with HTTP routes.
51
+
52
+ ### Sending Messages
53
+
54
+ ```javascript
55
+ Odac.ws.send({type: 'message', text: 'Hello'}) // JSON object
56
+ Odac.ws.send('Plain text message') // String
57
+ Odac.ws.sendBinary(buffer) // Binary data
58
+ ```
59
+
60
+ ### Event Handlers
61
+
62
+ ```javascript
63
+ Odac.ws.on('message', data => {}) // Incoming message
64
+ Odac.ws.on('close', () => {}) // Connection closed
65
+ Odac.ws.on('error', err => {}) // Error occurred
66
+ ```
67
+
68
+ ### Connection Management
69
+
70
+ ```javascript
71
+ Odac.ws.close() // Close connection
72
+ Odac.ws.ping() // Send ping frame
73
+ Odac.ws.id // Unique client ID
74
+ ```
75
+
76
+ ## Rooms
77
+
78
+ Group clients into rooms for targeted broadcasting:
79
+
80
+ ```javascript
81
+ Odac.Route.ws('/game', Odac => {
82
+ const roomId = Odac.Request.data.url.room || 'lobby'
83
+
84
+ Odac.ws.join(roomId)
85
+
86
+ Odac.ws.on('message', data => {
87
+ Odac.ws.to(roomId).send({
88
+ type: 'chat',
89
+ message: data.message
90
+ })
91
+ })
92
+
93
+ Odac.ws.on('close', () => {
94
+ Odac.ws.leave(roomId)
95
+ })
96
+ })
97
+ ```
98
+
99
+ ## Broadcasting
100
+
101
+ ```javascript
102
+ // Send to all clients except sender
103
+ Odac.ws.broadcast({type: 'notification', text: 'New user joined'})
104
+
105
+ // Send to all clients in a room
106
+ Odac.ws.to('room-name').send({type: 'update', data: {}})
107
+ ```
108
+
109
+ ## URL Parameters
110
+
111
+ WebSocket routes support dynamic parameters:
112
+
113
+ ```javascript
114
+ Odac.Route.ws('/room/{roomId}/user/{userId}', Odac => {
115
+ const {roomId, userId} = Odac.Request.data.url
116
+
117
+ Odac.ws.join(roomId)
118
+ Odac.ws.data.userId = userId
119
+ })
120
+ ```
121
+
122
+ ## Authentication
123
+
124
+ ### Manual Authentication Check
125
+
126
+ ```javascript
127
+ Odac.Route.ws('/secure', async Odac => {
128
+ const isAuthenticated = await Odac.Auth.check()
129
+
130
+ if (!isAuthenticated) {
131
+ Odac.ws.close(4001, 'Unauthorized')
132
+ return
133
+ }
134
+
135
+ const user = await Odac.Auth.user()
136
+ Odac.ws.data.user = user
137
+ })
138
+ ```
139
+
140
+ ### Using auth.ws() (Recommended)
141
+
142
+ Automatically requires authentication (also requires token by default):
143
+
144
+ ```javascript
145
+ Odac.Route.auth.ws('/secure', async Odac => {
146
+ const user = await Odac.Auth.user()
147
+ Odac.ws.data.user = user
148
+
149
+ Odac.ws.send({
150
+ type: 'welcome',
151
+ user: user.name
152
+ })
153
+ })
154
+ ```
155
+
156
+ If the user is not authenticated, the connection is automatically closed with code `4001`.
157
+
158
+ ### Options
159
+
160
+ ```javascript
161
+ Odac.Route.ws('/path', handler, {
162
+ token: true // Require CSRF token (default: true)
163
+ })
164
+ ```
165
+
166
+ **Examples:**
167
+
168
+ ```javascript
169
+ // Public WebSocket (no token, no auth)
170
+ Odac.Route.ws('/public', handler, {token: false})
171
+
172
+ // Token required, no auth (default)
173
+ Odac.Route.ws('/chat', handler)
174
+
175
+ // Both token and auth required
176
+ Odac.Route.auth.ws('/secure', handler)
177
+ ```
178
+
179
+ ## Middleware
180
+
181
+ WebSocket routes support middleware just like HTTP routes:
182
+
183
+ ```javascript
184
+ // Define middleware
185
+ Odac.Route.use('auth-check', 'rate-limit').ws('/chat', Odac => {
186
+ Odac.ws.send({type: 'welcome'})
187
+ })
188
+ ```
189
+
190
+ **Middleware behavior:**
191
+ - If middleware returns `false`, connection closes with code `4003` (Forbidden)
192
+ - If middleware returns anything other than `true` or `undefined`, connection closes with code `4000`
193
+ - Middleware runs before the WebSocket handler
194
+
195
+ **Example with custom middleware:**
196
+
197
+ ```javascript
198
+ // middleware/websocket-auth.js
199
+ module.exports = async Odac => {
200
+ const token = Odac.Request.header('Authorization')
201
+ if (!token) return false
202
+
203
+ const user = await validateToken(token)
204
+ if (!user) return false
205
+
206
+ Odac.Auth.setUser(user)
207
+ return true
208
+ }
209
+
210
+ // route/websocket.js
211
+ Odac.Route.use('websocket-auth').ws('/secure', Odac => {
212
+ Odac.ws.send({type: 'authenticated'})
213
+ })
214
+ ```
215
+
216
+ ## Client Data Storage
217
+
218
+ Store per-connection data:
219
+
220
+ ```javascript
221
+ Odac.ws.data.username = 'john'
222
+ Odac.ws.data.joinedAt = Date.now()
223
+ ```
224
+
225
+ ## Intervals and Timeouts
226
+
227
+ Use `Odac.setInterval()` and `Odac.setTimeout()` instead of global functions. They are automatically cleaned up when the WebSocket connection closes:
228
+
229
+ ```javascript
230
+ Odac.Route.ws('/live-updates', Odac => {
231
+ Odac.setInterval(() => {
232
+ Odac.ws.send({
233
+ type: 'update',
234
+ timestamp: Date.now()
235
+ })
236
+ }, 1000)
237
+
238
+ Odac.setTimeout(() => {
239
+ Odac.ws.send({type: 'delayed-message'})
240
+ }, 5000)
241
+ })
242
+ ```
243
+
244
+ **Why use Odac.setInterval/setTimeout?**
245
+ - Prevents memory leaks by auto-cleanup on disconnect
246
+ - No need to manually track and clear intervals
247
+ - Works seamlessly with WebSocket lifecycle
248
+
249
+ **Manual cleanup (if needed):**
250
+
251
+ ```javascript
252
+ const intervalId = Odac.setInterval(() => {}, 1000)
253
+ Odac.clearInterval(intervalId)
254
+
255
+ const timeoutId = Odac.setTimeout(() => {}, 5000)
256
+ Odac.clearTimeout(timeoutId)
257
+ ```
258
+
259
+ ## Real-Time Notifications Example
260
+
261
+ ```javascript
262
+ Odac.Route.ws('/notifications', async Odac => {
263
+ const user = await Odac.Auth.user()
264
+ if (!user) {
265
+ Odac.ws.close(4001, 'Unauthorized')
266
+ return
267
+ }
268
+
269
+ Odac.ws.data.userId = user.id
270
+ Odac.ws.join(`user-${user.id}`)
271
+
272
+ Odac.ws.on('close', () => {
273
+ console.log(`User ${user.id} disconnected`)
274
+ })
275
+ })
276
+
277
+ // Send notification to specific user from anywhere in your app
278
+ function notifyUser(userId, message) {
279
+ const wsServer = Odac.Route.wsServer
280
+ wsServer.toRoom(`user-${userId}`, {
281
+ type: 'notification',
282
+ message
283
+ })
284
+ }
285
+ ```
286
+
287
+ ## Client-Side Usage
288
+
289
+ Frontend clients can use shared connections across tabs:
290
+
291
+ ```javascript
292
+ // All browser tabs share one connection
293
+ const ws = Odac.ws('/notifications', {shared: true})
294
+
295
+ ws.on('message', data => {
296
+ console.log('Notification:', data)
297
+ })
298
+ ```
@@ -1,6 +1,6 @@
1
1
  ## 🏗️ How to Build a Controller
2
2
 
3
- A controller is just a JavaScript module that exports a function. This function automatically gets the magical `Candy` context object we talked about in the overview.
3
+ A controller is just a JavaScript module that exports a function. This function automatically gets the magical `Odac` context object we talked about in the overview.
4
4
 
5
5
  #### A Simple "Hello World" Controller
6
6
 
@@ -8,9 +8,9 @@ Check out this basic example from `controller/page/index.js`:
8
8
 
9
9
  ```javascript
10
10
  // This function is our controller!
11
- module.exports = function (Candy) {
11
+ module.exports = function (Odac) {
12
12
  // It simply returns a string.
13
- return 'Welcome to my awesome CandyPack server!'
13
+ return 'Welcome to my awesome Odac server!'
14
14
  }
15
15
  ```
16
16
 
@@ -0,0 +1,41 @@
1
+ ## 🤝 Your trusty `Odac` Assistant
2
+
3
+ Remember the `Odac` object? It's your best friend inside a controller. It's passed to your controller function and gives you all the tools you need for the current request.
4
+
5
+ #### Awesome Services at Your Fingertips
6
+
7
+ * `Odac.Request`: Info about the user's request.
8
+ * `Odac.View`: Renders your HTML pages.
9
+ * `Odac.Auth`: Manages user logins.
10
+ * `Odac.Token`: Protects your forms.
11
+ * `Odac.Lang`: Helps with different languages.
12
+
13
+ #### Handy Helper Functions
14
+
15
+ * `Odac.return(data)`: Send back a response.
16
+ * `Odac.direct(url)`: Redirect the user to a new page.
17
+ * `Odac.cookie(key, value)`: Set a browser cookie.
18
+ * `Odac.validator()`: Check user input easily.
19
+ * `Odac.setInterval(callback, delay)`: Schedule repeating tasks (auto-cleanup).
20
+ * `Odac.setTimeout(callback, delay)`: Schedule one-time tasks (auto-cleanup).
21
+ * `Odac.stream(input)`: Create streaming responses (SSE).
22
+
23
+ #### Memory-Safe Timers
24
+
25
+ Always use `Odac.setInterval()` and `Odac.setTimeout()` instead of global functions:
26
+
27
+ ```javascript
28
+ module.exports = async (Odac) => {
29
+ // ✅ Good - automatically cleaned up
30
+ Odac.setInterval(() => {
31
+ // This stops when request ends
32
+ }, 1000)
33
+
34
+ // ❌ Bad - memory leak!
35
+ setInterval(() => {
36
+ // This runs forever
37
+ }, 1000)
38
+ }
39
+ ```
40
+
41
+ With controllers and the `Odac` object, you have everything you need to start building powerful application logic!
@@ -4,25 +4,25 @@ While simple function exports work great for basic controllers, you can also org
4
4
 
5
5
  #### Creating a Controller Class
6
6
 
7
- A controller class receives the `Candy` object in its constructor, giving you access to all services throughout your class methods:
7
+ A controller class receives the `Odac` object in its constructor, giving you access to all services throughout your class methods:
8
8
 
9
9
  ```javascript
10
10
  // controller/User.js
11
11
  class User {
12
- constructor(Candy) {
13
- this.Candy = Candy
12
+ constructor(Odac) {
13
+ this.Odac = Odac
14
14
  }
15
15
 
16
16
  async getProfile() {
17
- const user = await this.Candy.Auth.user()
18
- return this.Candy.return({
17
+ const user = await this.Odac.Auth.user()
18
+ return this.Odac.return({
19
19
  success: true,
20
20
  user: user
21
21
  })
22
22
  }
23
23
 
24
24
  async updateProfile() {
25
- const validator = this.Candy.validator()
25
+ const validator = this.Odac.validator()
26
26
  validator.post('name').required().min(3)
27
27
  validator.post('email').required().email()
28
28
 
@@ -30,17 +30,17 @@ class User {
30
30
  return validator.result()
31
31
  }
32
32
 
33
- const name = await this.Candy.request('name')
34
- const email = await this.Candy.request('email')
33
+ const name = await this.Odac.request('name')
34
+ const email = await this.Odac.request('email')
35
35
 
36
36
  // Update user in database
37
- await this.Candy.Mysql.query('UPDATE users SET name = ?, email = ? WHERE id = ?', [
37
+ await this.Odac.Mysql.query('UPDATE users SET name = ?, email = ? WHERE id = ?', [
38
38
  name,
39
39
  email,
40
- this.Candy.Auth.user().id
40
+ this.Odac.Auth.user().id
41
41
  ])
42
42
 
43
- return this.Candy.return({
43
+ return this.Odac.return({
44
44
  success: true,
45
45
  message: 'Profile updated successfully'
46
46
  })
@@ -56,23 +56,23 @@ Once you've created a controller class, you can use it in your routes just like
56
56
 
57
57
  ```javascript
58
58
  // route/www.js
59
- Candy.Route.buff = 'www'
59
+ Odac.Route.buff = 'www'
60
60
 
61
61
  // Access class methods using dot notation
62
- Candy.Route.get('/profile', 'User.getProfile')
63
- Candy.Route.post('/profile/update', 'User.updateProfile')
62
+ Odac.Route.get('/profile', 'User.getProfile')
63
+ Odac.Route.post('/profile/update', 'User.updateProfile')
64
64
  ```
65
65
 
66
66
  #### Accessing Classes in Controllers
67
67
 
68
- Controller classes are automatically instantiated for each request and attached to the `Candy` object. You can access them from any controller:
68
+ Controller classes are automatically instantiated for each request and attached to the `Odac` object. You can access them from any controller:
69
69
 
70
70
  ```javascript
71
- module.exports = async function (Candy) {
71
+ module.exports = async function (Odac) {
72
72
  // Access your User class
73
- const profile = await Candy.User.getProfile()
73
+ const profile = await Odac.User.getProfile()
74
74
 
75
- return Candy.return(profile)
75
+ return Odac.return(profile)
76
76
  }
77
77
  ```
78
78
 
@@ -81,7 +81,7 @@ module.exports = async function (Candy) {
81
81
  - **Organization**: Group related methods together
82
82
  - **Reusability**: Share logic between different routes
83
83
  - **Maintainability**: Easier to manage complex controllers
84
- - **Context**: The `Candy` object is always available via `this.Candy`
84
+ - **Context**: The `Odac` object is always available via `this.Odac`
85
85
 
86
86
  #### Class vs Function Controllers
87
87