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,504 @@
1
+ ## 🔧 Candy.Var - String Manipulation & Validation
2
+
3
+ `Candy.Var` is a powerful utility class for string manipulation, validation, encryption, and formatting. It provides a chainable, fluent interface for common string operations.
4
+
5
+ ### Basic Usage
6
+
7
+ ```javascript
8
+ // Create a Var instance
9
+ const result = Candy.Var('hello world').slug()
10
+ // Returns: 'hello-world'
11
+
12
+ // Chain multiple operations
13
+ const email = Candy.Var(' USER@EXAMPLE.COM ').trim().toLowerCase()
14
+ ```
15
+
16
+ ### String Validation
17
+
18
+ Check if a string matches specific patterns:
19
+
20
+ #### is() - Single Validation
21
+
22
+ ```javascript
23
+ // Email validation
24
+ Candy.Var('user@example.com').is('email') // true
25
+ Candy.Var('invalid-email').is('email') // false
26
+
27
+ // Numeric validation
28
+ Candy.Var('12345').is('numeric') // true
29
+ Candy.Var('abc123').is('numeric') // false
30
+
31
+ // Multiple conditions (AND logic)
32
+ Candy.Var('abc123').is('alphanumeric') // true
33
+ ```
34
+
35
+ #### isAny() - Multiple Validation (OR logic)
36
+
37
+ ```javascript
38
+ // Check if value matches ANY of the conditions
39
+ Candy.Var('user@example.com').isAny('email', 'domain') // true
40
+ Candy.Var('example.com').isAny('email', 'domain') // true
41
+ ```
42
+
43
+ #### Available Validation Types
44
+
45
+ ```javascript
46
+ 'alpha' // Only letters (A-Z, a-z)
47
+ 'alphaspace' // Letters and spaces
48
+ 'alphanumeric' // Letters and numbers
49
+ 'alphanumericspace' // Letters, numbers, and spaces
50
+ 'bcrypt' // BCrypt hash format
51
+ 'date' // Valid date string
52
+ 'domain' // Valid domain name (example.com)
53
+ 'email' // Valid email address
54
+ 'float' // Floating point number
55
+ 'host' // IP address
56
+ 'ip' // IP address
57
+ 'json' // Valid JSON string
58
+ 'mac' // MAC address
59
+ 'md5' // MD5 hash
60
+ 'numeric' // Numbers only
61
+ 'url' // Valid URL
62
+ 'emoji' // Contains emoji
63
+ 'xss' // XSS-safe (no HTML tags)
64
+ ```
65
+
66
+ #### Practical Examples
67
+
68
+ ```javascript
69
+ // Controller validation
70
+ module.exports = async function(Candy) {
71
+ const email = Candy.Request.post('email')
72
+
73
+ if (!Candy.Var(email).is('email')) {
74
+ return Candy.return({
75
+ success: false,
76
+ message: 'Invalid email address'
77
+ })
78
+ }
79
+
80
+ // Continue with valid email
81
+ }
82
+ ```
83
+
84
+ ### String Checking
85
+
86
+ #### contains() - Check if string contains values
87
+
88
+ ```javascript
89
+ // Single value
90
+ Candy.Var('hello world').contains('world') // true
91
+ Candy.Var('hello world').contains('foo') // false
92
+
93
+ // Multiple values (AND logic - must contain all)
94
+ Candy.Var('hello world').contains('hello', 'world') // true
95
+ Candy.Var('hello world').contains('hello', 'foo') // false
96
+ ```
97
+
98
+ #### containsAny() - Check if string contains any value
99
+
100
+ ```javascript
101
+ // Check if contains ANY of the values (OR logic)
102
+ Candy.Var('hello world').containsAny('foo', 'world') // true
103
+ Candy.Var('hello world').containsAny('foo', 'bar') // false
104
+ ```
105
+
106
+ #### isBegin() - Check if string starts with value
107
+
108
+ ```javascript
109
+ Candy.Var('hello world').isBegin('hello') // true
110
+ Candy.Var('hello world').isBegin('world') // false
111
+
112
+ // Multiple options
113
+ Candy.Var('https://example.com').isBegin('http://', 'https://') // true
114
+ ```
115
+
116
+ #### isEnd() - Check if string ends with value
117
+
118
+ ```javascript
119
+ Candy.Var('hello world').isEnd('world') // true
120
+ Candy.Var('hello world').isEnd('hello') // false
121
+
122
+ // Multiple options
123
+ Candy.Var('image.jpg').isEnd('.jpg', '.png', '.gif') // true
124
+ ```
125
+
126
+ ### String Manipulation
127
+
128
+ #### replace() - Replace text
129
+
130
+ ```javascript
131
+ // Simple replacement
132
+ Candy.Var('hello world').replace('world', 'universe')
133
+ // Returns: 'hello universe'
134
+
135
+ // Multiple replacements with object
136
+ Candy.Var('Hello {{name}}, welcome to {{site}}').replace({
137
+ '{{name}}': 'John',
138
+ '{{site}}': 'CandyPack'
139
+ })
140
+ // Returns: 'Hello John, welcome to CandyPack'
141
+
142
+ // Works with arrays/objects recursively
143
+ const data = {
144
+ title: 'Welcome {{name}}',
145
+ message: 'Hello {{name}}'
146
+ }
147
+ Candy.Var(data).replace({'{{name}}': 'John'})
148
+ // Returns: { title: 'Welcome John', message: 'Hello John' }
149
+ ```
150
+
151
+ #### clear() - Remove specific strings
152
+
153
+ ```javascript
154
+ Candy.Var('hello-world-test').clear('-')
155
+ // Returns: 'helloworldtest'
156
+
157
+ // Remove multiple strings
158
+ Candy.Var('a1b2c3').clear('1', '2', '3')
159
+ // Returns: 'abc'
160
+ ```
161
+
162
+ #### slug() - Create URL-friendly slug
163
+
164
+ ```javascript
165
+ Candy.Var('Hello World!').slug()
166
+ // Returns: 'hello-world'
167
+
168
+ Candy.Var('Product Name 2024').slug()
169
+ // Returns: 'product-name-2024'
170
+
171
+ // Custom separator
172
+ Candy.Var('Hello World').slug('_')
173
+ // Returns: 'hello_world'
174
+ ```
175
+
176
+ #### format() - Format string with pattern
177
+
178
+ ```javascript
179
+ // ? = single character, * = rest of string
180
+ Candy.Var('1234567890').format('(???) ???-????')
181
+ // Returns: '(123) 456-7890'
182
+
183
+ Candy.Var('TR1234567890').format('?? *')
184
+ // Returns: 'TR 1234567890'
185
+ ```
186
+
187
+ #### html() - Escape HTML
188
+
189
+ ```javascript
190
+ Candy.Var('<script>alert("xss")</script>').html()
191
+ // Returns: '&lt;script&gt;alert("xss")&lt;/script&gt;'
192
+ ```
193
+
194
+ ### Encryption & Hashing
195
+
196
+ #### hash() - BCrypt password hashing
197
+
198
+ ```javascript
199
+ // Hash a password
200
+ const hashedPassword = Candy.Var('mypassword').hash()
201
+ // Returns: '$2b$10$...' (BCrypt hash)
202
+
203
+ // Custom salt rounds
204
+ const hashedPassword = Candy.Var('mypassword').hash(12)
205
+ ```
206
+
207
+ #### hashCheck() - Verify BCrypt hash
208
+
209
+ ```javascript
210
+ const hashedPassword = '$2b$10$...'
211
+ const isValid = Candy.Var(hashedPassword).hashCheck('mypassword')
212
+ // Returns: true or false
213
+ ```
214
+
215
+ #### md5() - MD5 hash
216
+
217
+ ```javascript
218
+ Candy.Var('hello').md5()
219
+ // Returns: '5d41402abc4b2a76b9719d911017c592'
220
+ ```
221
+
222
+ #### encrypt() - AES-256 encryption
223
+
224
+ ```javascript
225
+ // Uses key from Candy.Config.encrypt.key
226
+ const encrypted = Candy.Var('secret data').encrypt()
227
+
228
+ // Custom encryption key
229
+ const encrypted = Candy.Var('secret data').encrypt('my-32-character-encryption-key')
230
+ ```
231
+
232
+ #### decrypt() - AES-256 decryption
233
+
234
+ ```javascript
235
+ // Uses key from Candy.Config.encrypt.key
236
+ const decrypted = Candy.Var(encryptedData).decrypt()
237
+
238
+ // Custom decryption key
239
+ const decrypted = Candy.Var(encryptedData).decrypt('my-32-character-encryption-key')
240
+ ```
241
+
242
+ ### Date Formatting
243
+
244
+ #### date() - Format date strings
245
+
246
+ ```javascript
247
+ const timestamp = '2024-03-15 14:30:45'
248
+
249
+ Candy.Var(timestamp).date('Y-m-d')
250
+ // Returns: '2024-03-15'
251
+
252
+ Candy.Var(timestamp).date('d/m/Y')
253
+ // Returns: '15/03/2024'
254
+
255
+ Candy.Var(timestamp).date('H:i:s')
256
+ // Returns: '14:30:45'
257
+
258
+ Candy.Var(timestamp).date('Y-m-d H:i')
259
+ // Returns: '2024-03-15 14:30'
260
+ ```
261
+
262
+ **Format tokens:**
263
+ - `Y` - 4-digit year (2024)
264
+ - `y` - 2-digit year (24)
265
+ - `m` - Month with leading zero (01-12)
266
+ - `d` - Day with leading zero (01-31)
267
+ - `H` - Hour with leading zero (00-23)
268
+ - `i` - Minute with leading zero (00-59)
269
+ - `s` - Second with leading zero (00-59)
270
+
271
+ ### File Operations
272
+
273
+ #### save() - Save string to file
274
+
275
+ ```javascript
276
+ // Save content to file
277
+ Candy.Var('Hello World').save('/path/to/file.txt')
278
+
279
+ // Automatically creates directories if needed
280
+ Candy.Var(jsonData).save('/path/to/nested/dir/data.json')
281
+ ```
282
+
283
+ ### Practical Examples
284
+
285
+ #### User Registration with Validation
286
+
287
+ ```javascript
288
+ module.exports = async function(Candy) {
289
+ const email = Candy.Request.post('email')
290
+ const password = Candy.Request.post('password')
291
+ const username = Candy.Request.post('username')
292
+
293
+ // Validate email
294
+ if (!Candy.Var(email).is('email')) {
295
+ return Candy.return({
296
+ success: false,
297
+ message: 'Invalid email address'
298
+ })
299
+ }
300
+
301
+ // Validate username (alphanumeric only)
302
+ if (!Candy.Var(username).is('alphanumeric')) {
303
+ return Candy.return({
304
+ success: false,
305
+ message: 'Username must be alphanumeric'
306
+ })
307
+ }
308
+
309
+ // Hash password
310
+ const hashedPassword = Candy.Var(password).hash()
311
+
312
+ // Create slug for profile URL
313
+ const profileSlug = Candy.Var(username).slug()
314
+
315
+ // Save user
316
+ await Candy.Mysql.table('users').insert({
317
+ email: email,
318
+ username: username,
319
+ password: hashedPassword,
320
+ slug: profileSlug
321
+ })
322
+
323
+ return Candy.return({success: true})
324
+ }
325
+ ```
326
+
327
+ #### Login with Password Verification
328
+
329
+ ```javascript
330
+ module.exports = async function(Candy) {
331
+ const email = Candy.Request.post('email')
332
+ const password = Candy.Request.post('password')
333
+
334
+ // Find user
335
+ const user = await Candy.Mysql.table('users')
336
+ .where('email', email)
337
+ .first()
338
+
339
+ if (!user) {
340
+ return Candy.return({
341
+ success: false,
342
+ message: 'User not found'
343
+ })
344
+ }
345
+
346
+ // Verify password
347
+ const isValid = Candy.Var(user.password).hashCheck(password)
348
+
349
+ if (!isValid) {
350
+ return Candy.return({
351
+ success: false,
352
+ message: 'Invalid password'
353
+ })
354
+ }
355
+
356
+ // Login successful
357
+ Candy.Auth.login(user.id)
358
+ return Candy.return({success: true})
359
+ }
360
+ ```
361
+
362
+ #### URL Slug Generation
363
+
364
+ ```javascript
365
+ module.exports = async function(Candy) {
366
+ const title = Candy.Request.post('title')
367
+
368
+ // Create URL-friendly slug
369
+ const slug = Candy.Var(title).slug()
370
+
371
+ // Check if slug exists
372
+ const exists = await Candy.Mysql.table('posts')
373
+ .where('slug', slug)
374
+ .first()
375
+
376
+ if (exists) {
377
+ // Add timestamp to make unique
378
+ const uniqueSlug = `${slug}-${Date.now()}`
379
+ await Candy.Mysql.table('posts').insert({
380
+ title: title,
381
+ slug: uniqueSlug
382
+ })
383
+ } else {
384
+ await Candy.Mysql.table('posts').insert({
385
+ title: title,
386
+ slug: slug
387
+ })
388
+ }
389
+
390
+ return Candy.return({success: true})
391
+ }
392
+ ```
393
+
394
+ #### Template Variable Replacement
395
+
396
+ ```javascript
397
+ module.exports = async function(Candy) {
398
+ const user = await Candy.Auth.user()
399
+
400
+ // Email template
401
+ const template = `
402
+ Hello {{name}},
403
+
404
+ Your account {{email}} has been verified.
405
+ You can now access your dashboard at {{url}}.
406
+
407
+ Thanks,
408
+ {{site}}
409
+ `
410
+
411
+ // Replace variables
412
+ const emailContent = Candy.Var(template).replace({
413
+ '{{name}}': user.name,
414
+ '{{email}}': user.email,
415
+ '{{url}}': 'https://example.com/dashboard',
416
+ '{{site}}': 'CandyPack'
417
+ })
418
+
419
+ // Send email
420
+ await Candy.Mail.send({
421
+ to: user.email,
422
+ subject: 'Account Verified',
423
+ body: emailContent
424
+ })
425
+
426
+ return Candy.return({success: true})
427
+ }
428
+ ```
429
+
430
+ #### Phone Number Formatting
431
+
432
+ ```javascript
433
+ module.exports = async function(Candy) {
434
+ const phone = Candy.Request.post('phone')
435
+
436
+ // Remove all non-numeric characters
437
+ const cleanPhone = Candy.Var(phone).clear('-', ' ', '(', ')', '+')
438
+
439
+ // Validate it's numeric
440
+ if (!Candy.Var(cleanPhone).is('numeric')) {
441
+ return Candy.return({
442
+ success: false,
443
+ message: 'Invalid phone number'
444
+ })
445
+ }
446
+
447
+ // Format for display
448
+ const formattedPhone = Candy.Var(cleanPhone).format('(???) ???-????')
449
+
450
+ return Candy.return({
451
+ success: true,
452
+ phone: formattedPhone
453
+ })
454
+ }
455
+ ```
456
+
457
+ #### Data Encryption for Storage
458
+
459
+ ```javascript
460
+ module.exports = async function(Candy) {
461
+ const creditCard = Candy.Request.post('credit_card')
462
+
463
+ // Encrypt sensitive data
464
+ const encryptedCard = Candy.Var(creditCard).encrypt()
465
+
466
+ // Save encrypted data
467
+ await Candy.Mysql.table('payments').insert({
468
+ user_id: Candy.Auth.id(),
469
+ card: encryptedCard
470
+ })
471
+
472
+ return Candy.return({success: true})
473
+ }
474
+
475
+ // Later, to retrieve and decrypt
476
+ module.exports = async function(Candy) {
477
+ const payment = await Candy.Mysql.table('payments')
478
+ .where('user_id', Candy.Auth.id())
479
+ .first()
480
+
481
+ // Decrypt data
482
+ const creditCard = Candy.Var(payment.card).decrypt()
483
+
484
+ return Candy.return({
485
+ card: creditCard
486
+ })
487
+ }
488
+ ```
489
+
490
+ ### Best Practices
491
+
492
+ 1. **Always validate user input** before processing
493
+ 2. **Use hash() for passwords**, never store plain text
494
+ 3. **Use encrypt() for sensitive data** like credit cards, SSNs
495
+ 4. **Create slugs for URLs** to make them SEO-friendly
496
+ 5. **Sanitize HTML** with html() to prevent XSS attacks
497
+ 6. **Use isBegin/isEnd** for protocol or file extension checks
498
+
499
+ ### Notes
500
+
501
+ - `Candy.Var()` returns the processed string value, not a Var instance (except for chaining)
502
+ - Encryption uses AES-256-CBC with a fixed IV
503
+ - BCrypt hashing is one-way and cannot be decrypted
504
+ - Date formatting works with any valid JavaScript date string
@@ -0,0 +1,146 @@
1
+ # Frontend Javascript Framework: candy.js
2
+
3
+ `candy.js` is a lightweight frontend JavaScript framework designed to simplify interactions with the backend, handle forms, and manage page-specific logic within the CandyPack ecosystem. It provides a set of tools for event handling, AJAX requests, and more, all accessible through the global `Candy` object.
4
+
5
+ ## The Global `Candy` Object
6
+
7
+ After including `candy.js` in your page, you will have access to a global `Candy` object. This object is the main entry point for all the features of the framework.
8
+
9
+ ## Core Concepts
10
+
11
+ ### Actions
12
+
13
+ Actions are the fundamental building block of `candy.js`. An action is a collection of event handlers and lifecycle callbacks that define the behavior of a page or a component.
14
+
15
+ ### Pages
16
+
17
+ `candy.js` has a concept of "pages", which allows you to scope your JavaScript to a specific page. The current page identifier is determined by the backend based on:
18
+ - **Controller name** when using controller files (e.g., `'user'` for `controller/page/user.js`)
19
+ - **View name** when using view objects (e.g., `'dashboard'` from `{content: 'dashboard'}`)
20
+ - This identifier is accessible via `Candy.page()` and stored in `data-candy-page` attribute on the `<html>` element.
21
+
22
+ ### Lifecycle Events
23
+
24
+ `candy.js` provides several lifecycle events that you can hook into:
25
+ - `start`: Fired once when the script is initialized.
26
+ - `load`: Fired on every page load, after the DOM is ready.
27
+ - `page`: Fired on a specific page, after the DOM is ready.
28
+ - `interval`: Fired repeatedly at a specified interval.
29
+
30
+ ## Event Handling with `Candy.action()`
31
+
32
+ The `Candy.action()` method is the most important method in the framework. It allows you to register event handlers and lifecycle callbacks.
33
+
34
+ ```javascript
35
+ Candy.action({
36
+ // Fired once on DOMContentLoaded
37
+ start: function() {
38
+ console.log('Candy.js started!');
39
+ },
40
+
41
+ // Fired on every page load
42
+ load: function() {
43
+ console.log('Page loaded!');
44
+ },
45
+
46
+ // Fired only on the 'home' page
47
+ page: {
48
+ home: function() {
49
+ console.log('Welcome to the home page!');
50
+ }
51
+ },
52
+
53
+ // Fired every 2 seconds on the 'dashboard' page
54
+ interval: {
55
+ myInterval: {
56
+ interval: 2000,
57
+ page: 'dashboard',
58
+ function: function() {
59
+ console.log('Dashboard is refreshing...');
60
+ }
61
+ }
62
+ },
63
+
64
+ // Event handlers
65
+ click: {
66
+ '#my-button': function() {
67
+ alert('Button clicked!');
68
+ }
69
+ },
70
+
71
+ // You can also define functions and reference them
72
+ fn: {
73
+ myFunction: function() {
74
+ alert('This is my function!');
75
+ }
76
+ },
77
+
78
+ // And then use them in your event handlers
79
+ mouseover: {
80
+ '#my-element': 'fn.myFunction'
81
+ }
82
+ });
83
+ ```
84
+
85
+ ## Working with Forms using `Candy.form()`
86
+
87
+ `Candy.form()` simplifies AJAX form submissions. It handles serialization, validation feedback, success messages, and file uploads automatically.
88
+
89
+ ```javascript
90
+ // Basic usage
91
+ Candy.form('#my-form', function(data) {
92
+ // This callback is executed on success
93
+ console.log('Form submitted successfully!', data);
94
+ });
95
+
96
+ // With options
97
+ Candy.form({
98
+ form: '#my-form',
99
+ messages: ['success', 'error'], // Show both success and error messages
100
+ loading: function(percent) {
101
+ console.log('Upload progress:', percent + '%');
102
+ }
103
+ }, function(data) {
104
+ // Success callback
105
+ if (data.result.success) {
106
+ window.location.href = '/thank-you';
107
+ }
108
+ });
109
+ ```
110
+ To display validation errors, you can add elements with the `candy-form-error` attribute to your form. The value of the attribute should be the `name` of the input field.
111
+
112
+ ```html
113
+ <input type="text" name="email">
114
+ <span candy-form-error="email"></span>
115
+ ```
116
+
117
+ ## Making AJAX requests with `Candy.get()`
118
+
119
+ For simple GET requests, you can use the `Candy.get()` method.
120
+
121
+ ```javascript
122
+ Candy.get('/api/users', function(data) {
123
+ console.log('Users:', data);
124
+ });
125
+ ```
126
+
127
+ ## Managing CSRF tokens with `Candy.token()`
128
+
129
+ `candy.js` automatically manages CSRF tokens for you. The `Candy.token()` method will return a valid token for your requests. The `Candy.form()` and `Candy.get()` methods use this automatically, so you usually don't need to call it yourself.
130
+
131
+ ## Other Utility Functions
132
+
133
+ - **`Candy.client()`**: Returns a unique client identifier from a cookie.
134
+ - **`Candy.data()`**: Returns data from the `candy_data` cookie, which is set by the backend. This data includes the current page and the initial CSRF token.
135
+ - **`Candy.page()`**: Returns the identifier of the current page. This is the controller name (e.g., `'user'`) or view name (e.g., `'dashboard'`) set by the backend. Use this to conditionally run code for specific pages.
136
+ - **`Candy.storage()`**: A wrapper for `localStorage`.
137
+ ```javascript
138
+ // Set a value
139
+ Candy.storage('my-key', 'my-value');
140
+
141
+ // Get a value
142
+ let value = Candy.storage('my-key');
143
+
144
+ // Remove a value
145
+ Candy.storage('my-key', null);
146
+ ```