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,519 @@
1
+ # Advanced AJAX Navigation
2
+
3
+ Advanced techniques and patterns for AJAX navigation in CandyPack.
4
+
5
+ **Note:** All examples assume you have properly configured your skeleton template and controller. See the [Quick Start guide](01-quick-start.md#skeleton-structure-required) for setup instructions.
6
+
7
+ ## Programmatic Navigation
8
+
9
+ ### Using Candy.load()
10
+
11
+ Navigate programmatically from your code:
12
+
13
+ ```javascript
14
+ // Basic usage
15
+ Candy.load('/about')
16
+
17
+ // With callback
18
+ Candy.load('/about', function(page, variables) {
19
+ console.log('Loaded:', page)
20
+ console.log('Data:', variables)
21
+ })
22
+
23
+ // Without updating history
24
+ Candy.load('/about', callback, false)
25
+ ```
26
+
27
+ ### Use Cases
28
+
29
+ **Redirect after form submission:**
30
+ ```javascript
31
+ Candy.form('#my-form', function(data) {
32
+ if (data.result.success) {
33
+ Candy.load('/success')
34
+ }
35
+ })
36
+ ```
37
+
38
+ **Conditional navigation:**
39
+ ```javascript
40
+ if (user.isLoggedIn) {
41
+ Candy.load('/dashboard')
42
+ } else {
43
+ Candy.load('/login')
44
+ }
45
+ ```
46
+
47
+ **Timed navigation:**
48
+ ```javascript
49
+ setTimeout(() => {
50
+ Candy.load('/next-page')
51
+ }, 3000)
52
+ ```
53
+
54
+ ## Multi-Section Updates
55
+
56
+ ### Updating Multiple Areas
57
+
58
+ Update different parts of your page simultaneously:
59
+
60
+ ```javascript
61
+ Candy.action({
62
+ navigate: {
63
+ update: {
64
+ content: 'main',
65
+ sidebar: '#sidebar',
66
+ breadcrumb: '.breadcrumb',
67
+ notifications: '#notifications'
68
+ }
69
+ }
70
+ })
71
+ ```
72
+
73
+ ### Controller Setup
74
+
75
+ Define all parts in your controller:
76
+
77
+ ```javascript
78
+ module.exports = function(Candy) {
79
+ Candy.View.skeleton('main')
80
+ Candy.View.set({
81
+ header: 'main',
82
+ content: 'dashboard',
83
+ sidebar: 'dashboard',
84
+ breadcrumb: 'dashboard',
85
+ footer: 'main'
86
+ })
87
+ }
88
+ ```
89
+
90
+ ## State Management
91
+
92
+ ### Preserving State
93
+
94
+ Maintain application state across navigations:
95
+
96
+ ```javascript
97
+ let appState = {
98
+ user: null,
99
+ cart: [],
100
+ filters: {}
101
+ }
102
+
103
+ Candy.action({
104
+ navigate: {
105
+ update: 'main',
106
+ on: function(page, variables) {
107
+ // Update state from server
108
+ if (variables.user) {
109
+ appState.user = variables.user
110
+ }
111
+
112
+ // Restore UI state
113
+ restoreFilters(appState.filters)
114
+ }
115
+ }
116
+ })
117
+ ```
118
+
119
+ ### Session Storage
120
+
121
+ Persist state across page reloads:
122
+
123
+ ```javascript
124
+ Candy.action({
125
+ navigate: {
126
+ update: 'main',
127
+ on: function(page, variables) {
128
+ // Save to session storage
129
+ sessionStorage.setItem('lastPage', page)
130
+ sessionStorage.setItem('pageData', JSON.stringify(variables))
131
+ }
132
+ },
133
+
134
+ load: function() {
135
+ // Restore on app load
136
+ const lastPage = sessionStorage.getItem('lastPage')
137
+ if (lastPage) {
138
+ console.log('Last visited:', lastPage)
139
+ }
140
+ }
141
+ })
142
+ ```
143
+
144
+ ## Animation & Transitions
145
+
146
+ ### Custom Transitions
147
+
148
+ Add custom animations:
149
+
150
+ ```javascript
151
+ Candy.action({
152
+ navigate: {
153
+ update: 'main',
154
+ on: function(page, variables) {
155
+ const main = document.querySelector('main')
156
+
157
+ // Fade in animation
158
+ main.style.opacity = '0'
159
+ setTimeout(() => {
160
+ main.style.transition = 'opacity 0.3s'
161
+ main.style.opacity = '1'
162
+ }, 10)
163
+ }
164
+ }
165
+ })
166
+ ```
167
+
168
+ ### Page Transitions
169
+
170
+ Smooth page transitions:
171
+
172
+ ```javascript
173
+ Candy.action({
174
+ navigate: {
175
+ update: 'main',
176
+ on: function(page, variables) {
177
+ const main = document.querySelector('main')
178
+ main.classList.add('page-enter')
179
+
180
+ setTimeout(() => {
181
+ main.classList.remove('page-enter')
182
+ main.classList.add('page-enter-active')
183
+ }, 10)
184
+ }
185
+ }
186
+ })
187
+ ```
188
+
189
+ ```css
190
+ main {
191
+ transition: transform 0.3s, opacity 0.3s;
192
+ }
193
+
194
+ main.page-enter {
195
+ opacity: 0;
196
+ transform: translateY(20px);
197
+ }
198
+
199
+ main.page-enter-active {
200
+ opacity: 1;
201
+ transform: translateY(0);
202
+ }
203
+ ```
204
+
205
+ ## Scroll Management
206
+
207
+ ### Scroll to Top
208
+
209
+ Automatically scroll to top on navigation:
210
+
211
+ ```javascript
212
+ Candy.action({
213
+ navigate: {
214
+ update: 'main',
215
+ on: function(page, variables) {
216
+ window.scrollTo({
217
+ top: 0,
218
+ behavior: 'smooth'
219
+ })
220
+ }
221
+ }
222
+ })
223
+ ```
224
+
225
+ ### Preserve Scroll Position
226
+
227
+ Remember scroll position:
228
+
229
+ ```javascript
230
+ let scrollPositions = {}
231
+
232
+ Candy.action({
233
+ navigate: {
234
+ update: 'main',
235
+ on: function(page, variables) {
236
+ // Save current scroll position
237
+ const currentPath = window.location.pathname
238
+ scrollPositions[currentPath] = window.scrollY
239
+
240
+ // Restore scroll position for this page
241
+ const savedPosition = scrollPositions[page]
242
+ if (savedPosition !== undefined) {
243
+ setTimeout(() => {
244
+ window.scrollTo(0, savedPosition)
245
+ }, 100)
246
+ } else {
247
+ window.scrollTo(0, 0)
248
+ }
249
+ }
250
+ }
251
+ })
252
+ ```
253
+
254
+ ### Scroll to Element
255
+
256
+ Scroll to specific element after navigation:
257
+
258
+ ```javascript
259
+ Candy.action({
260
+ navigate: {
261
+ update: 'main',
262
+ on: function(page, variables) {
263
+ // Check for hash in URL
264
+ const hash = window.location.hash
265
+ if (hash) {
266
+ setTimeout(() => {
267
+ const element = document.querySelector(hash)
268
+ if (element) {
269
+ element.scrollIntoView({behavior: 'smooth'})
270
+ }
271
+ }, 100)
272
+ }
273
+ }
274
+ }
275
+ })
276
+ ```
277
+
278
+ ## Error Handling
279
+
280
+ ### Handling Failed Requests
281
+
282
+ Gracefully handle navigation errors:
283
+
284
+ ```javascript
285
+ // Override Candy.load to add error handling
286
+ const originalLoad = Candy.load.bind(Candy)
287
+
288
+ Candy.load = function(url, callback, push) {
289
+ try {
290
+ originalLoad(url, function(page, variables) {
291
+ if (callback) callback(page, variables)
292
+ }, push)
293
+ } catch (error) {
294
+ console.error('Navigation failed:', error)
295
+ // Fallback to normal navigation
296
+ window.location.href = url
297
+ }
298
+ }
299
+ ```
300
+
301
+ ### Retry Logic
302
+
303
+ Retry failed requests:
304
+
305
+ ```javascript
306
+ function loadWithRetry(url, maxRetries = 3) {
307
+ let attempts = 0
308
+
309
+ function attempt() {
310
+ attempts++
311
+ Candy.load(url,
312
+ (page, vars) => {
313
+ console.log('Success after', attempts, 'attempts')
314
+ },
315
+ true
316
+ )
317
+ }
318
+
319
+ // Initial attempt
320
+ attempt()
321
+
322
+ // Retry on error (implement error detection)
323
+ // This is a simplified example
324
+ }
325
+ ```
326
+
327
+ ## Performance Optimization
328
+
329
+ ### Debouncing Navigation
330
+
331
+ Prevent rapid navigation:
332
+
333
+ ```javascript
334
+ let navigationTimeout
335
+
336
+ Candy.action({
337
+ click: {
338
+ 'a[href^="/"]': function(e) {
339
+ clearTimeout(navigationTimeout)
340
+
341
+ navigationTimeout = setTimeout(() => {
342
+ // Navigation happens here
343
+ }, 100)
344
+ }
345
+ }
346
+ })
347
+ ```
348
+
349
+ ### Prefetching
350
+
351
+ Prefetch pages on hover:
352
+
353
+ ```javascript
354
+ let prefetchCache = {}
355
+
356
+ Candy.action({
357
+ mouseover: {
358
+ 'a[href^="/"]': function() {
359
+ const url = this.getAttribute('href')
360
+
361
+ if (!prefetchCache[url]) {
362
+ // Prefetch the page
363
+ fetch(url, {
364
+ headers: {
365
+ 'X-Candy': 'prefetch'
366
+ }
367
+ }).then(response => response.text())
368
+ .then(html => {
369
+ prefetchCache[url] = html
370
+ })
371
+ }
372
+ }
373
+ }
374
+ })
375
+ ```
376
+
377
+ ## Integration with Other Libraries
378
+
379
+ ### With Analytics
380
+
381
+ Track page views:
382
+
383
+ ```javascript
384
+ Candy.action({
385
+ navigate: {
386
+ update: 'main',
387
+ on: function(page, variables) {
388
+ // Google Analytics
389
+ if (window.gtag) {
390
+ gtag('config', 'GA_ID', {
391
+ page_path: window.location.pathname,
392
+ page_title: variables.title || page
393
+ })
394
+ }
395
+
396
+ // Plausible
397
+ if (window.plausible) {
398
+ plausible('pageview')
399
+ }
400
+ }
401
+ }
402
+ })
403
+ ```
404
+
405
+ ### With State Management
406
+
407
+ Redux/Vuex integration:
408
+
409
+ ```javascript
410
+ Candy.action({
411
+ navigate: {
412
+ update: 'main',
413
+ on: function(page, variables) {
414
+ // Dispatch to Redux
415
+ store.dispatch({
416
+ type: 'PAGE_CHANGED',
417
+ payload: {page, variables}
418
+ })
419
+ }
420
+ }
421
+ })
422
+ ```
423
+
424
+ ## Testing
425
+
426
+ ### Unit Testing
427
+
428
+ Test navigation logic:
429
+
430
+ ```javascript
431
+ describe('Navigation', () => {
432
+ it('should update active nav on page change', () => {
433
+ Candy.action({
434
+ navigate: {
435
+ update: 'main',
436
+ on: (page) => {
437
+ updateActiveNav(page)
438
+ }
439
+ }
440
+ })
441
+
442
+ // Simulate navigation
443
+ Candy.load('/about')
444
+
445
+ // Assert
446
+ expect(document.querySelector('.nav-link.active').href)
447
+ .toContain('/about')
448
+ })
449
+ })
450
+ ```
451
+
452
+ ## Best Practices
453
+
454
+ 1. **Error Handling**: Always handle failed requests
455
+ 2. **Loading States**: Show visual feedback during navigation
456
+ 3. **Accessibility**: Announce page changes to screen readers
457
+ 4. **Performance**: Minimize DOM updates
458
+ 5. **SEO**: Ensure content is crawlable
459
+ 6. **Progressive Enhancement**: Work without JavaScript
460
+
461
+ ## Common Patterns
462
+
463
+ ### Dashboard Navigation
464
+
465
+ ```javascript
466
+ Candy.action({
467
+ navigate: {
468
+ links: '.sidebar a, .breadcrumb a',
469
+ update: {
470
+ content: '#dashboard-content',
471
+ breadcrumb: '.breadcrumb',
472
+ stats: '#stats-widget'
473
+ },
474
+ on: (page, vars) => {
475
+ updateSidebar(page)
476
+ updateStats(vars.stats)
477
+ }
478
+ }
479
+ })
480
+ ```
481
+
482
+ ### E-commerce
483
+
484
+ ```javascript
485
+ Candy.action({
486
+ navigate: {
487
+ update: {
488
+ content: 'main',
489
+ cart: '#cart-widget'
490
+ },
491
+ on: (page, vars) => {
492
+ updateCartCount(vars.cartItems)
493
+ trackPageView(page)
494
+ }
495
+ }
496
+ })
497
+ ```
498
+
499
+ ### Blog
500
+
501
+ ```javascript
502
+ Candy.action({
503
+ navigate: {
504
+ update: 'main',
505
+ on: (page, vars) => {
506
+ if (page === 'post') {
507
+ initComments()
508
+ highlightCode()
509
+ }
510
+ }
511
+ }
512
+ })
513
+ ```
514
+
515
+ ## Next Steps
516
+
517
+ - Learn about [Form Handling](../03-forms/01-form-handling.md)
518
+ - Explore [API Requests](../04-api-requests/01-get-post.md)
519
+ - Check [candy.js Overview](../01-overview/01-introduction.md)