odac 0.9.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 (213) hide show
  1. package/.editorconfig +21 -0
  2. package/.github/workflows/auto-pr-description.yml +49 -0
  3. package/.github/workflows/release.yml +32 -0
  4. package/.github/workflows/test-coverage.yml +58 -0
  5. package/.husky/pre-commit +2 -0
  6. package/.kiro/steering/code-style.md +56 -0
  7. package/.kiro/steering/product.md +20 -0
  8. package/.kiro/steering/structure.md +77 -0
  9. package/.kiro/steering/tech.md +87 -0
  10. package/.prettierrc +10 -0
  11. package/.releaserc.js +134 -0
  12. package/AGENTS.md +84 -0
  13. package/CHANGELOG.md +181 -0
  14. package/CODE_OF_CONDUCT.md +83 -0
  15. package/CONTRIBUTING.md +63 -0
  16. package/LICENSE +661 -0
  17. package/README.md +57 -0
  18. package/SECURITY.md +26 -0
  19. package/bin/candy +10 -0
  20. package/bin/candypack +10 -0
  21. package/cli/index.js +3 -0
  22. package/cli/src/Cli.js +348 -0
  23. package/cli/src/Connector.js +93 -0
  24. package/cli/src/Monitor.js +416 -0
  25. package/core/Candy.js +87 -0
  26. package/core/Commands.js +239 -0
  27. package/core/Config.js +1094 -0
  28. package/core/Lang.js +52 -0
  29. package/core/Log.js +43 -0
  30. package/core/Process.js +26 -0
  31. package/docs/backend/01-overview/01-whats-in-the-candy-box.md +9 -0
  32. package/docs/backend/01-overview/02-super-handy-helper-functions.md +9 -0
  33. package/docs/backend/01-overview/03-development-server.md +79 -0
  34. package/docs/backend/02-structure/01-typical-project-layout.md +39 -0
  35. package/docs/backend/03-config/00-configuration-overview.md +214 -0
  36. package/docs/backend/03-config/01-database-connection.md +60 -0
  37. package/docs/backend/03-config/02-static-route-mapping-optional.md +20 -0
  38. package/docs/backend/03-config/03-request-timeout.md +11 -0
  39. package/docs/backend/03-config/04-environment-variables.md +227 -0
  40. package/docs/backend/03-config/05-early-hints.md +352 -0
  41. package/docs/backend/04-routing/01-basic-page-routes.md +28 -0
  42. package/docs/backend/04-routing/02-controller-less-view-routes.md +43 -0
  43. package/docs/backend/04-routing/03-api-and-data-routes.md +20 -0
  44. package/docs/backend/04-routing/04-authentication-aware-routes.md +48 -0
  45. package/docs/backend/04-routing/05-advanced-routing.md +14 -0
  46. package/docs/backend/04-routing/06-error-pages.md +101 -0
  47. package/docs/backend/04-routing/07-cron-jobs.md +149 -0
  48. package/docs/backend/05-controllers/01-how-to-build-a-controller.md +17 -0
  49. package/docs/backend/05-controllers/02-your-trusty-candy-assistant.md +20 -0
  50. package/docs/backend/05-controllers/03-controller-classes.md +93 -0
  51. package/docs/backend/05-forms/01-custom-forms.md +395 -0
  52. package/docs/backend/05-forms/02-automatic-database-insert.md +297 -0
  53. package/docs/backend/06-request-and-response/01-the-request-object-what-is-the-user-asking-for.md +96 -0
  54. package/docs/backend/06-request-and-response/02-sending-a-response-replying-to-the-user.md +40 -0
  55. package/docs/backend/07-views/01-the-view-directory.md +73 -0
  56. package/docs/backend/07-views/02-rendering-a-view.md +179 -0
  57. package/docs/backend/07-views/03-template-syntax.md +181 -0
  58. package/docs/backend/07-views/03-variables.md +328 -0
  59. package/docs/backend/07-views/04-request-data.md +231 -0
  60. package/docs/backend/07-views/05-conditionals.md +290 -0
  61. package/docs/backend/07-views/06-loops.md +353 -0
  62. package/docs/backend/07-views/07-translations.md +358 -0
  63. package/docs/backend/07-views/08-backend-javascript.md +398 -0
  64. package/docs/backend/07-views/09-comments.md +297 -0
  65. package/docs/backend/08-database/01-database-connection.md +99 -0
  66. package/docs/backend/08-database/02-using-mysql.md +322 -0
  67. package/docs/backend/09-validation/01-the-validator-service.md +424 -0
  68. package/docs/backend/10-authentication/01-user-logins-with-authjs.md +53 -0
  69. package/docs/backend/10-authentication/02-foiling-villains-with-csrf-protection.md +55 -0
  70. package/docs/backend/10-authentication/03-register.md +134 -0
  71. package/docs/backend/10-authentication/04-candy-register-forms.md +676 -0
  72. package/docs/backend/10-authentication/05-session-management.md +159 -0
  73. package/docs/backend/10-authentication/06-candy-login-forms.md +596 -0
  74. package/docs/backend/11-mail/01-the-mail-service.md +42 -0
  75. package/docs/backend/12-streaming/01-streaming-overview.md +300 -0
  76. package/docs/backend/13-utilities/01-candy-var.md +504 -0
  77. package/docs/frontend/01-overview/01-introduction.md +146 -0
  78. package/docs/frontend/02-ajax-navigation/01-quick-start.md +608 -0
  79. package/docs/frontend/02-ajax-navigation/02-configuration.md +370 -0
  80. package/docs/frontend/02-ajax-navigation/03-advanced-usage.md +519 -0
  81. package/docs/frontend/03-forms/01-form-handling.md +420 -0
  82. package/docs/frontend/04-api-requests/01-get-post.md +443 -0
  83. package/docs/frontend/05-streaming/01-client-streaming.md +163 -0
  84. package/docs/index.json +452 -0
  85. package/docs/server/01-installation/01-quick-install.md +19 -0
  86. package/docs/server/01-installation/02-manual-installation-via-npm.md +9 -0
  87. package/docs/server/02-get-started/01-core-concepts.md +7 -0
  88. package/docs/server/02-get-started/02-basic-commands.md +57 -0
  89. package/docs/server/02-get-started/03-cli-reference.md +276 -0
  90. package/docs/server/02-get-started/04-cli-quick-reference.md +102 -0
  91. package/docs/server/03-service/01-start-a-new-service.md +57 -0
  92. package/docs/server/03-service/02-delete-a-service.md +48 -0
  93. package/docs/server/04-web/01-create-a-website.md +36 -0
  94. package/docs/server/04-web/02-list-websites.md +9 -0
  95. package/docs/server/04-web/03-delete-a-website.md +29 -0
  96. package/docs/server/05-subdomain/01-create-a-subdomain.md +32 -0
  97. package/docs/server/05-subdomain/02-list-subdomains.md +33 -0
  98. package/docs/server/05-subdomain/03-delete-a-subdomain.md +41 -0
  99. package/docs/server/06-ssl/01-renew-an-ssl-certificate.md +34 -0
  100. package/docs/server/07-mail/01-create-a-mail-account.md +23 -0
  101. package/docs/server/07-mail/02-delete-a-mail-account.md +20 -0
  102. package/docs/server/07-mail/03-list-mail-accounts.md +20 -0
  103. package/docs/server/07-mail/04-change-account-password.md +23 -0
  104. package/eslint.config.mjs +120 -0
  105. package/framework/index.js +4 -0
  106. package/framework/src/Auth.js +309 -0
  107. package/framework/src/Candy.js +81 -0
  108. package/framework/src/Config.js +79 -0
  109. package/framework/src/Env.js +60 -0
  110. package/framework/src/Lang.js +57 -0
  111. package/framework/src/Mail.js +83 -0
  112. package/framework/src/Mysql.js +575 -0
  113. package/framework/src/Request.js +301 -0
  114. package/framework/src/Route/Cron.js +128 -0
  115. package/framework/src/Route/Internal.js +439 -0
  116. package/framework/src/Route.js +455 -0
  117. package/framework/src/Server.js +15 -0
  118. package/framework/src/Stream.js +163 -0
  119. package/framework/src/Token.js +37 -0
  120. package/framework/src/Validator.js +271 -0
  121. package/framework/src/Var.js +211 -0
  122. package/framework/src/View/EarlyHints.js +190 -0
  123. package/framework/src/View/Form.js +600 -0
  124. package/framework/src/View.js +513 -0
  125. package/framework/web/candy.js +838 -0
  126. package/jest.config.js +22 -0
  127. package/locale/de-DE.json +80 -0
  128. package/locale/en-US.json +79 -0
  129. package/locale/es-ES.json +80 -0
  130. package/locale/fr-FR.json +80 -0
  131. package/locale/pt-BR.json +80 -0
  132. package/locale/ru-RU.json +80 -0
  133. package/locale/tr-TR.json +85 -0
  134. package/locale/zh-CN.json +80 -0
  135. package/package.json +86 -0
  136. package/server/index.js +5 -0
  137. package/server/src/Api.js +88 -0
  138. package/server/src/DNS.js +940 -0
  139. package/server/src/Hub.js +535 -0
  140. package/server/src/Mail.js +571 -0
  141. package/server/src/SSL.js +180 -0
  142. package/server/src/Server.js +27 -0
  143. package/server/src/Service.js +248 -0
  144. package/server/src/Subdomain.js +64 -0
  145. package/server/src/Web/Firewall.js +170 -0
  146. package/server/src/Web/Proxy.js +134 -0
  147. package/server/src/Web.js +451 -0
  148. package/server/src/mail/imap.js +1091 -0
  149. package/server/src/mail/server.js +32 -0
  150. package/server/src/mail/smtp.js +786 -0
  151. package/test/cli/Cli.test.js +36 -0
  152. package/test/core/Candy.test.js +234 -0
  153. package/test/core/Commands.test.js +538 -0
  154. package/test/core/Config.test.js +1435 -0
  155. package/test/core/Lang.test.js +250 -0
  156. package/test/core/Process.test.js +156 -0
  157. package/test/framework/Route.test.js +239 -0
  158. package/test/framework/View/EarlyHints.test.js +282 -0
  159. package/test/scripts/check-coverage.js +132 -0
  160. package/test/server/Api.test.js +647 -0
  161. package/test/server/Client.test.js +338 -0
  162. package/test/server/DNS.test.js +2050 -0
  163. package/test/server/DNS.test.js.bak +2084 -0
  164. package/test/server/Log.test.js +73 -0
  165. package/test/server/Mail.account.test_.js +460 -0
  166. package/test/server/Mail.init.test_.js +411 -0
  167. package/test/server/Mail.test_.js +1340 -0
  168. package/test/server/SSL.test_.js +1491 -0
  169. package/test/server/Server.test.js +765 -0
  170. package/test/server/Service.test_.js +1127 -0
  171. package/test/server/Subdomain.test.js +440 -0
  172. package/test/server/Web/Firewall.test.js +175 -0
  173. package/test/server/Web.test_.js +1562 -0
  174. package/test/server/__mocks__/acme-client.js +17 -0
  175. package/test/server/__mocks__/bcrypt.js +50 -0
  176. package/test/server/__mocks__/child_process.js +389 -0
  177. package/test/server/__mocks__/crypto.js +432 -0
  178. package/test/server/__mocks__/fs.js +450 -0
  179. package/test/server/__mocks__/globalCandy.js +227 -0
  180. package/test/server/__mocks__/http-proxy.js +105 -0
  181. package/test/server/__mocks__/http.js +575 -0
  182. package/test/server/__mocks__/https.js +272 -0
  183. package/test/server/__mocks__/index.js +249 -0
  184. package/test/server/__mocks__/mail/server.js +100 -0
  185. package/test/server/__mocks__/mail/smtp.js +31 -0
  186. package/test/server/__mocks__/mailparser.js +81 -0
  187. package/test/server/__mocks__/net.js +369 -0
  188. package/test/server/__mocks__/node-forge.js +328 -0
  189. package/test/server/__mocks__/os.js +320 -0
  190. package/test/server/__mocks__/path.js +291 -0
  191. package/test/server/__mocks__/selfsigned.js +8 -0
  192. package/test/server/__mocks__/server/src/mail/server.js +100 -0
  193. package/test/server/__mocks__/server/src/mail/smtp.js +31 -0
  194. package/test/server/__mocks__/smtp-server.js +106 -0
  195. package/test/server/__mocks__/sqlite3.js +394 -0
  196. package/test/server/__mocks__/testFactories.js +299 -0
  197. package/test/server/__mocks__/testHelpers.js +363 -0
  198. package/test/server/__mocks__/tls.js +229 -0
  199. package/watchdog/index.js +3 -0
  200. package/watchdog/src/Watchdog.js +156 -0
  201. package/web/config.json +5 -0
  202. package/web/controller/page/about.js +27 -0
  203. package/web/controller/page/index.js +34 -0
  204. package/web/package.json +18 -0
  205. package/web/public/assets/css/style.css +1835 -0
  206. package/web/public/assets/js/app.js +96 -0
  207. package/web/route/www.js +19 -0
  208. package/web/skeleton/main.html +22 -0
  209. package/web/view/content/about.html +65 -0
  210. package/web/view/content/home.html +205 -0
  211. package/web/view/footer/main.html +11 -0
  212. package/web/view/head/main.html +5 -0
  213. package/web/view/header/main.html +14 -0
@@ -0,0 +1,443 @@
1
+ # API Requests with candy.js
2
+
3
+ Learn how to make GET and POST requests to your API endpoints using candy.js.
4
+
5
+ ## GET Requests
6
+
7
+ ### Basic GET Request
8
+
9
+ ```javascript
10
+ Candy.get('/api/data', function(response) {
11
+ console.log('Data:', response)
12
+ })
13
+ ```
14
+
15
+ ### With Parameters
16
+
17
+ ```javascript
18
+ const userId = 123
19
+ Candy.get(`/api/users/${userId}`, function(user) {
20
+ console.log('User:', user.name)
21
+ console.log('Email:', user.email)
22
+ })
23
+ ```
24
+
25
+ ### Error Handling
26
+
27
+ ```javascript
28
+ Candy.get('/api/data', function(response) {
29
+ if (response.error) {
30
+ console.error('Error:', response.error)
31
+ return
32
+ }
33
+
34
+ // Process data
35
+ console.log('Success:', response)
36
+ })
37
+ ```
38
+
39
+ ## POST Requests
40
+
41
+ ### Using Forms
42
+
43
+ The recommended way to make POST requests is using `Candy.form()`:
44
+
45
+ ```javascript
46
+ Candy.form('#my-form', function(data) {
47
+ if (data.result.success) {
48
+ console.log('Success!')
49
+ }
50
+ })
51
+ ```
52
+
53
+ See [Form Handling](../03-forms/01-form-handling.md) for details.
54
+
55
+ ### Manual POST (Advanced)
56
+
57
+ For custom POST requests without forms, use the internal AJAX method:
58
+
59
+ ```javascript
60
+ // Note: This is an advanced pattern
61
+ // For most cases, use Candy.form() instead
62
+
63
+ const formData = new FormData()
64
+ formData.append('name', 'John')
65
+ formData.append('email', 'john@example.com')
66
+ formData.append('_token', Candy.token())
67
+
68
+ fetch('/api/submit', {
69
+ method: 'POST',
70
+ body: formData
71
+ })
72
+ .then(response => response.json())
73
+ .then(data => {
74
+ console.log('Success:', data)
75
+ })
76
+ ```
77
+
78
+ ## CSRF Protection
79
+
80
+ ### Automatic Token Management
81
+
82
+ candy.js automatically handles CSRF tokens:
83
+
84
+ ```javascript
85
+ // Token is automatically included
86
+ Candy.get('/api/data', function(response) {
87
+ // ...
88
+ })
89
+
90
+ // Token is automatically included in forms
91
+ Candy.form('#my-form', function(data) {
92
+ // ...
93
+ })
94
+ ```
95
+
96
+ ### Manual Token Access
97
+
98
+ If you need the token manually:
99
+
100
+ ```javascript
101
+ const token = Candy.token()
102
+ console.log('Current token:', token)
103
+ ```
104
+
105
+ ## Common Patterns
106
+
107
+ ### Fetching Data
108
+
109
+ ```javascript
110
+ // Get user profile
111
+ Candy.get('/api/profile', function(profile) {
112
+ document.querySelector('#username').textContent = profile.name
113
+ document.querySelector('#email').textContent = profile.email
114
+ })
115
+ ```
116
+
117
+ ### Loading List Data
118
+
119
+ ```javascript
120
+ // Get products
121
+ Candy.get('/api/products', function(products) {
122
+ const container = document.querySelector('#products')
123
+
124
+ products.forEach(product => {
125
+ const html = `
126
+ <div class="product">
127
+ <h3>${product.name}</h3>
128
+ <p>${product.price}</p>
129
+ </div>
130
+ `
131
+ container.insertAdjacentHTML('beforeend', html)
132
+ })
133
+ })
134
+ ```
135
+
136
+ ### Search
137
+
138
+ ```javascript
139
+ const searchInput = document.querySelector('#search')
140
+
141
+ searchInput.addEventListener('input', function() {
142
+ const query = this.value
143
+
144
+ if (query.length < 3) return
145
+
146
+ Candy.get(`/api/search?q=${encodeURIComponent(query)}`, function(results) {
147
+ displayResults(results)
148
+ })
149
+ })
150
+
151
+ function displayResults(results) {
152
+ const container = document.querySelector('#results')
153
+ container.innerHTML = ''
154
+
155
+ results.forEach(result => {
156
+ const html = `<div class="result">${result.title}</div>`
157
+ container.insertAdjacentHTML('beforeend', html)
158
+ })
159
+ }
160
+ ```
161
+
162
+ ### Autocomplete
163
+
164
+ ```javascript
165
+ let debounceTimer
166
+
167
+ document.querySelector('#autocomplete').addEventListener('input', function() {
168
+ clearTimeout(debounceTimer)
169
+
170
+ const value = this.value
171
+
172
+ debounceTimer = setTimeout(() => {
173
+ if (value.length < 2) return
174
+
175
+ Candy.get(`/api/autocomplete?q=${value}`, function(suggestions) {
176
+ showSuggestions(suggestions)
177
+ })
178
+ }, 300)
179
+ })
180
+ ```
181
+
182
+ ### Infinite Scroll
183
+
184
+ ```javascript
185
+ let page = 1
186
+ let loading = false
187
+
188
+ window.addEventListener('scroll', function() {
189
+ if (loading) return
190
+
191
+ const scrollPosition = window.innerHeight + window.scrollY
192
+ const threshold = document.body.offsetHeight - 500
193
+
194
+ if (scrollPosition >= threshold) {
195
+ loadMore()
196
+ }
197
+ })
198
+
199
+ function loadMore() {
200
+ loading = true
201
+ page++
202
+
203
+ Candy.get(`/api/posts?page=${page}`, function(posts) {
204
+ posts.forEach(post => {
205
+ appendPost(post)
206
+ })
207
+ loading = false
208
+ })
209
+ }
210
+ ```
211
+
212
+ ### Real-time Updates
213
+
214
+ ```javascript
215
+ // Poll for updates every 30 seconds
216
+ setInterval(function() {
217
+ Candy.get('/api/notifications', function(notifications) {
218
+ updateNotificationBadge(notifications.count)
219
+ })
220
+ }, 30000)
221
+ ```
222
+
223
+ ## Server-Side Setup
224
+
225
+ ### GET Endpoint
226
+
227
+ ```javascript
228
+ // controller/get/users.js
229
+ module.exports = function(Candy) {
230
+ // Get all users
231
+ const users = [
232
+ {id: 1, name: 'John', email: 'john@example.com'},
233
+ {id: 2, name: 'Jane', email: 'jane@example.com'}
234
+ ]
235
+
236
+ return users
237
+ }
238
+ ```
239
+
240
+ ```javascript
241
+ // route/www.js
242
+ Candy.Route.get('/api/users', 'users')
243
+ ```
244
+
245
+ ### GET with Parameters
246
+
247
+ ```javascript
248
+ // controller/get/user.js
249
+ module.exports = async function(Candy) {
250
+ const userId = Candy.Request.data.url.id
251
+
252
+ // Fetch user from database
253
+ const user = await getUserById(userId)
254
+
255
+ if (!user) {
256
+ Candy.Request.status(404)
257
+ return {error: 'User not found'}
258
+ }
259
+
260
+ return user
261
+ }
262
+ ```
263
+
264
+ ```javascript
265
+ // route/www.js
266
+ Candy.Route.get('/api/users/{id}', 'user')
267
+ ```
268
+
269
+ ### POST Endpoint
270
+
271
+ ```javascript
272
+ // controller/post/create.js
273
+ module.exports = async function(Candy) {
274
+ const name = await Candy.Request.request('name')
275
+ const email = await Candy.Request.request('email')
276
+
277
+ // Validation
278
+ if (!name || !email) {
279
+ return {
280
+ result: {success: false},
281
+ errors: {
282
+ name: !name ? 'Name is required' : null,
283
+ email: !email ? 'Email is required' : null
284
+ }
285
+ }
286
+ }
287
+
288
+ // Create user
289
+ const user = await createUser({name, email})
290
+
291
+ return {
292
+ result: {
293
+ success: true,
294
+ message: 'User created successfully'
295
+ },
296
+ user: user
297
+ }
298
+ }
299
+ ```
300
+
301
+ ```javascript
302
+ // route/www.js
303
+ Candy.Route.post('/api/users/create', 'create')
304
+ ```
305
+
306
+ ## Response Format
307
+
308
+ ### Success Response
309
+
310
+ ```json
311
+ {
312
+ "result": {
313
+ "success": true,
314
+ "message": "Operation successful"
315
+ },
316
+ "data": {
317
+ "id": 123,
318
+ "name": "John"
319
+ }
320
+ }
321
+ ```
322
+
323
+ ### Error Response
324
+
325
+ ```json
326
+ {
327
+ "result": {
328
+ "success": false,
329
+ "message": "Validation failed"
330
+ },
331
+ "errors": {
332
+ "email": "Email is required",
333
+ "password": "Password must be at least 8 characters"
334
+ }
335
+ }
336
+ ```
337
+
338
+ ## Best Practices
339
+
340
+ 1. **Use Appropriate Methods**: GET for reading, POST for writing
341
+ 2. **Validate Input**: Always validate on the server
342
+ 3. **Handle Errors**: Provide meaningful error messages
343
+ 4. **Use HTTPS**: Always use HTTPS for sensitive data
344
+ 5. **Rate Limiting**: Implement rate limiting on the server
345
+ 6. **Debounce Requests**: Debounce rapid requests (search, autocomplete)
346
+
347
+ ## Advanced Techniques
348
+
349
+ ### Request Caching
350
+
351
+ ```javascript
352
+ const cache = {}
353
+
354
+ function getCached(url, callback) {
355
+ if (cache[url]) {
356
+ callback(cache[url])
357
+ return
358
+ }
359
+
360
+ Candy.get(url, function(data) {
361
+ cache[url] = data
362
+ callback(data)
363
+ })
364
+ }
365
+
366
+ // Usage
367
+ getCached('/api/data', function(data) {
368
+ console.log('Data:', data)
369
+ })
370
+ ```
371
+
372
+ ### Request Queue
373
+
374
+ ```javascript
375
+ const requestQueue = []
376
+ let processing = false
377
+
378
+ function queueRequest(url, callback) {
379
+ requestQueue.push({url, callback})
380
+ processQueue()
381
+ }
382
+
383
+ function processQueue() {
384
+ if (processing || requestQueue.length === 0) return
385
+
386
+ processing = true
387
+ const {url, callback} = requestQueue.shift()
388
+
389
+ Candy.get(url, function(data) {
390
+ callback(data)
391
+ processing = false
392
+ processQueue()
393
+ })
394
+ }
395
+ ```
396
+
397
+ ### Retry Logic
398
+
399
+ ```javascript
400
+ function getWithRetry(url, callback, maxRetries = 3) {
401
+ let attempts = 0
402
+
403
+ function attempt() {
404
+ attempts++
405
+
406
+ Candy.get(url, function(data) {
407
+ if (data.error && attempts < maxRetries) {
408
+ setTimeout(attempt, 1000 * attempts)
409
+ } else {
410
+ callback(data)
411
+ }
412
+ })
413
+ }
414
+
415
+ attempt()
416
+ }
417
+ ```
418
+
419
+ ## Troubleshooting
420
+
421
+ ### Request Not Working
422
+
423
+ - Check the URL is correct
424
+ - Verify the endpoint exists in your routes
425
+ - Check browser console for errors
426
+ - Ensure CSRF token is valid
427
+
428
+ ### CORS Errors
429
+
430
+ - CORS is not an issue for same-origin requests
431
+ - For cross-origin requests, configure server CORS headers
432
+
433
+ ### Token Errors
434
+
435
+ - Tokens are managed automatically
436
+ - If you get token errors, check server configuration
437
+ - Ensure cookies are enabled
438
+
439
+ ## Next Steps
440
+
441
+ - Learn about [Form Handling](../03-forms/01-form-handling.md)
442
+ - Explore [AJAX Navigation](../02-ajax-navigation/01-quick-start.md)
443
+ - Check [candy.js Overview](../01-overview/01-introduction.md)
@@ -0,0 +1,163 @@
1
+ # Client-Side Streaming
2
+
3
+ CandyPack provides a simple client-side API for consuming Server-Sent Events (SSE) streams.
4
+
5
+ ### Basic Usage
6
+
7
+ ```javascript
8
+ // Simple callback
9
+ Candy.listen('/events', (data) => {
10
+ console.log('Received:', data)
11
+ })
12
+ ```
13
+
14
+ ### With Options
15
+
16
+ ```javascript
17
+ const stream = Candy.listen('/events',
18
+ (data) => {
19
+ console.log('Message:', data)
20
+ },
21
+ {
22
+ onOpen: () => console.log('Connected'),
23
+ onError: (err) => console.error('Error:', err),
24
+ autoReconnect: true,
25
+ reconnectDelay: 3000
26
+ }
27
+ )
28
+
29
+ // Close connection manually
30
+ stream.close()
31
+ ```
32
+
33
+ ### Auto JSON Parsing
34
+
35
+ ```javascript
36
+ // Server sends: data: {"message": "Hello"}\n\n
37
+ Candy.listen('/events', (data) => {
38
+ console.log(data.message) // "Hello"
39
+ })
40
+ ```
41
+
42
+
43
+
44
+ ## Real-World Examples
45
+
46
+ ### Live Dashboard
47
+
48
+ ```javascript
49
+ // Server
50
+ Candy.Route.get('/dashboard/stats', async (Candy) => {
51
+ Candy.stream((send) => {
52
+ const interval = setInterval(async () => {
53
+ const stats = await getServerStats()
54
+ send(stats)
55
+ }, 1000)
56
+
57
+ // Cleanup function
58
+ return () => {
59
+ clearInterval(interval)
60
+ }
61
+ })
62
+ })
63
+
64
+ // Client
65
+ Candy.listen('/dashboard/stats', (stats) => {
66
+ document.getElementById('cpu').textContent = stats.cpu + '%'
67
+ document.getElementById('memory').textContent = stats.memory + 'MB'
68
+ })
69
+ ```
70
+
71
+ ### Real-time Notifications
72
+
73
+ ```javascript
74
+ // Server
75
+ Candy.Route.get('/notifications', async (Candy) => {
76
+ const userId = await Candy.request('userId')
77
+
78
+ Candy.stream((send) => {
79
+ global.notificationStreams[userId] = { send }
80
+ })
81
+ })
82
+
83
+ // Client
84
+ Candy.listen('/notifications', (notification) => {
85
+ showToast(notification.message)
86
+ })
87
+ ```
88
+
89
+ ### Build Logs
90
+
91
+ ```javascript
92
+ // Server
93
+ Candy.Route.get('/build/logs', async (Candy) => {
94
+ Candy.stream(async function* () {
95
+ for await (const log of getBuildLogs()) {
96
+ yield { timestamp: Date.now(), message: log }
97
+ }
98
+ })
99
+ })
100
+
101
+ // Client
102
+ const logs = []
103
+ Candy.listen('/build/logs', (log) => {
104
+ logs.push(log)
105
+ updateLogsUI(logs)
106
+ })
107
+ ```
108
+
109
+ ## API Reference
110
+
111
+ ### Candy.listen(url, onMessage, options)
112
+
113
+ **Parameters:**
114
+ - `url` (string): Stream endpoint URL
115
+ - `onMessage` (function): Callback for each message
116
+ - `options` (object): Optional configuration
117
+
118
+ **Options:**
119
+ - `onOpen` (function): Called when connection opens
120
+ - `onError` (function): Called on error
121
+ - `autoReconnect` (boolean): Auto-reconnect on disconnect (default: true)
122
+ - `reconnectDelay` (number): Delay before reconnect in ms (default: 3000)
123
+
124
+ **Returns:**
125
+ - `close()` (function): Close the connection
126
+
127
+
128
+
129
+ ## Best Practices
130
+
131
+ 1. **Always cleanup:** Close streams when component unmounts
132
+ 2. **Handle errors:** Provide error UI feedback
133
+ 3. **Show connection status:** Let users know when disconnected
134
+ 4. **Limit data:** Don't store unlimited messages in state
135
+ 5. **Throttle updates:** Use debounce for frequent updates
136
+
137
+ ## Troubleshooting
138
+
139
+ **Connection keeps dropping:**
140
+ - Check server heartbeat (should be < 60s)
141
+ - Verify network stability
142
+ - Check browser console for errors
143
+
144
+ **Messages not received:**
145
+ - Verify JSON format from server
146
+ - Check CORS headers
147
+ - Ensure EventSource is supported
148
+
149
+ **Memory leaks:**
150
+ - Always call `close()` or use hooks
151
+ - Limit stored messages
152
+ - Clear intervals/timers
153
+
154
+ ## Browser Support
155
+
156
+ - ✅ Chrome 6+
157
+ - ✅ Firefox 6+
158
+ - ✅ Safari 5+
159
+ - ✅ Edge (all versions)
160
+ - ✅ Opera 11+
161
+ - ❌ IE (not supported)
162
+
163
+ For IE support, use a polyfill like `event-source-polyfill`.