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,96 @@
1
+ ## 📥 The Request Object
2
+
3
+ The `Candy.Request` object contains information about the user's incoming request.
4
+
5
+ ### Getting Request Parameters
6
+
7
+ #### Using Candy.request() (Recommended)
8
+
9
+ The easiest way to get request parameters is using `Candy.request()`:
10
+
11
+ ```javascript
12
+ module.exports = async function (Candy) {
13
+ // Get parameter from GET or POST automatically
14
+ const userName = await Candy.request('name')
15
+ const userId = await Candy.request('id')
16
+
17
+ return `Hello ${userName}!`
18
+ }
19
+ ```
20
+
21
+ **Specify Method (Optional):**
22
+
23
+ ```javascript
24
+ module.exports = async function (Candy) {
25
+ // Get from GET parameters only
26
+ const searchQuery = await Candy.request('q', 'GET')
27
+
28
+ // Get from POST parameters only
29
+ const formName = await Candy.request('name', 'POST')
30
+
31
+ return `Searching for: ${searchQuery}`
32
+ }
33
+ ```
34
+
35
+ #### Direct Access
36
+
37
+ You can also access request data directly:
38
+
39
+ ```javascript
40
+ module.exports = function (Candy) {
41
+ // GET parameters (URL query string like ?id=123)
42
+ const userId = Candy.Request.get('id')
43
+
44
+ // POST parameters (form data)
45
+ const userName = Candy.Request.post('name')
46
+
47
+ return `User: ${userName}`
48
+ }
49
+ ```
50
+
51
+ ### Request Properties
52
+
53
+ * `Candy.Request.method` - HTTP method ('GET', 'POST', etc.)
54
+ * `Candy.Request.url` - Full URL the user visited
55
+ * `Candy.Request.host` - Website's hostname
56
+ * `Candy.Request.ip` - User's IP address
57
+ * `Candy.Request.ssl` - Whether connection is SSL/HTTPS
58
+
59
+ ### Request Headers
60
+
61
+ ```javascript
62
+ module.exports = function (Candy) {
63
+ const userAgent = Candy.Request.header('user-agent')
64
+ const contentType = Candy.Request.header('content-type')
65
+
66
+ return `Browser: ${userAgent}`
67
+ }
68
+ ```
69
+
70
+ ### Complete Example
71
+
72
+ ```javascript
73
+ module.exports = async function (Candy) {
74
+ // Get request parameters
75
+ const productId = await Candy.request('id')
76
+ const quantity = await Candy.request('quantity') || 1
77
+
78
+ // Check request method
79
+ if (Candy.Request.method === 'POST') {
80
+ // Handle form submission
81
+ const result = await processOrder(productId, quantity)
82
+ return { success: true, orderId: result.id }
83
+ }
84
+
85
+ // Show product page
86
+ Candy.set({
87
+ productId: productId,
88
+ quantity: quantity
89
+ })
90
+
91
+ Candy.View.set({
92
+ skeleton: 'main',
93
+ content: 'product.detail'
94
+ })
95
+ }
96
+ ```
@@ -0,0 +1,40 @@
1
+ ## 📤 Sending a Response: Replying to the User
2
+
3
+ Once you've processed the request, it's time to send something back. You've got a few options.
4
+
5
+ #### The Simple Way: Just Return It!
6
+
7
+ For many cases, you can just `return` a value from your controller. CandyPack is smart enough to figure out what to do.
8
+
9
+ ```javascript
10
+ // Return some HTML
11
+ module.exports = function (Candy) {
12
+ return '<h1>Welcome to the site!</h1>';
13
+ }
14
+
15
+ // Return some JSON for an API
16
+ module.exports = function (Candy) {
17
+ return { status: 'success', message: 'Your data was saved!' };
18
+ }
19
+ ```
20
+
21
+ #### The Helper Functions: More Control
22
+
23
+ Need a bit more control? The `Candy` object has your back.
24
+
25
+ * `Candy.return(data)`: Does the same thing as a direct return, but you can call it from anywhere in your function. It stops everything and sends the response immediately.
26
+ * `Candy.direct(url)`: Need to send the user to a different page? This function performs a redirect, telling the user's browser to go to a new URL.
27
+
28
+ **Example:**
29
+ ```javascript
30
+ module.exports = function (Candy) {
31
+ // If the user isn't logged in...
32
+ if (!Candy.Auth.isLogin()) {
33
+ // ...send them to the login page!
34
+ return Candy.direct('/login');
35
+ }
36
+
37
+ // Otherwise, give them their data.
38
+ Candy.return({ data: 'here is your secret stuff' });
39
+ }
40
+ ```
@@ -0,0 +1,73 @@
1
+ ## 📁 View System Overview
2
+
3
+ CandyPack's view system creates dynamic HTML pages by combining skeleton (layout) and view (content) files. This system provides a modular structure by keeping page layout and content separate.
4
+
5
+ ### Directory Structure
6
+
7
+ Your project uses two main directories:
8
+
9
+ - `skeleton/` - Main page skeletons (layout files)
10
+ - `view/` - Page contents and components
11
+
12
+ ### Skeleton Files
13
+
14
+ Skeleton files define the overall structure of your page. They contain the basic HTML structure including head and body, and host placeholders for content.
15
+
16
+ Example: `skeleton/main.html`
17
+
18
+ ```html
19
+ <!DOCTYPE html>
20
+ <html lang="en">
21
+ <head>
22
+ <meta charset="UTF-8">
23
+ <title>My Website</title>
24
+ <meta name="description" content="Welcome to my website">
25
+ <link rel="stylesheet" href="/assets/css/style.css">
26
+ </head>
27
+ <body>
28
+ <header>
29
+ {{ HEADER }}
30
+ </header>
31
+
32
+ <main>
33
+ {{ CONTENT }}
34
+ </main>
35
+
36
+ <footer>
37
+ {{ FOOTER }}
38
+ </footer>
39
+ </body>
40
+ </html>
41
+ ```
42
+
43
+ **Important Rules for Placeholders:**
44
+
45
+ 1. **Each placeholder must be wrapped in HTML tags** - This allows AJAX to identify and update specific sections
46
+ 2. **Never place placeholders directly next to each other** - Bad: `{{ HEADER }}{{ CONTENT }}`, Good: `<header>{{ HEADER }}</header><main>{{ CONTENT }}</main>`
47
+ 3. **Placeholders are uppercase** - `{{ HEADER }}`, `{{ CONTENT }}`, `{{ FOOTER }}`
48
+ 4. **Use semantic HTML tags** - `<header>`, `<main>`, `<footer>`, `<aside>`, `<nav>`, etc.
49
+
50
+ **Why wrap in tags?**
51
+ When using AJAX navigation, the system needs to identify which part of the page to update. HTML tags provide clear boundaries for each section.
52
+
53
+ **Note:** Skeleton files currently support only view part placeholders (uppercase). For dynamic content like page titles, use a view part for the `<head>` section or set them in individual view files.
54
+
55
+ ### View Files
56
+
57
+ View files contain the content that will be placed into the placeholders within the skeleton. They are organized under the `view/` directory.
58
+
59
+ Example directory structure:
60
+
61
+ ```
62
+ view/
63
+ ├── header/
64
+ │ ├── main.html
65
+ │ └── dashboard.html
66
+ ├── content/
67
+ │ ├── home.html
68
+ │ └── about.html
69
+ └── footer/
70
+ └── main.html
71
+ ```
72
+
73
+
@@ -0,0 +1,179 @@
1
+ ## 🎨 Rendering Views
2
+
3
+ In CandyPack, you use the `Candy.View` object to render views. There are two main approaches:
4
+
5
+ ### 1. Combining Skeleton and View Parts
6
+
7
+ The most common usage is to select a skeleton and place view parts into it.
8
+
9
+ ```javascript
10
+ module.exports = function (Candy) {
11
+ Candy.View
12
+ .skeleton('main') // Use skeleton/main.html
13
+ .set('header', 'main') // Place view/header/main.html into {{ HEADER }}
14
+ .set('content', 'home') // Place view/content/home.html into {{ CONTENT }}
15
+ .set('footer', 'main') // Place view/footer/main.html into {{ FOOTER }}
16
+ }
17
+ ```
18
+
19
+ ### 2. Bulk Setting with Object
20
+
21
+ You can set all view parts at once:
22
+
23
+ ```javascript
24
+ module.exports = function (Candy) {
25
+ Candy.View.set({
26
+ skeleton: 'main',
27
+ header: 'main',
28
+ content: 'home',
29
+ footer: 'main'
30
+ })
31
+ }
32
+ ```
33
+
34
+ ### 3. Subdirectories with Dot Notation
35
+
36
+ View files can be organized in subdirectories. You can access them using dot notation:
37
+
38
+ ```javascript
39
+ Candy.View.set({
40
+ skeleton: 'dashboard',
41
+ header: 'dashboard.main', // view/header/dashboard/main.html
42
+ sidebar: 'dashboard.menu', // view/sidebar/dashboard/menu.html
43
+ content: 'user.profile' // view/content/user/profile.html
44
+ })
45
+ ```
46
+
47
+ ### 4. Direct View Rendering from Routes
48
+
49
+ You can render views directly from route files without using a controller:
50
+
51
+ ```javascript
52
+ // route/www.js
53
+ Candy.Route.page('/about').view({
54
+ skeleton: 'main',
55
+ header: 'main',
56
+ content: 'about',
57
+ footer: 'main'
58
+ })
59
+ ```
60
+
61
+ ### 5. All Feature
62
+
63
+ If you're using the same directory structure for all placeholders, you can use the `all()` method:
64
+
65
+ ```javascript
66
+ Candy.View
67
+ .skeleton('main')
68
+ .all('home') // view/home/header.html, view/home/content.html, view/home/footer.html
69
+ ```
70
+
71
+ In this case, placeholders like `{{ HEADER }}`, `{{ CONTENT }}`, `{{ FOOTER }}` in the skeleton are automatically matched with `view/home/header.html`, `view/home/content.html`, `view/home/footer.html` files.
72
+
73
+ ### Setting Dynamic Page Titles and Meta Tags
74
+
75
+ Since skeleton files only support view part placeholders, you have two approaches for dynamic titles:
76
+
77
+ #### Approach 1: Include Head as a View Part
78
+
79
+ Create a separate view part for the `<head>` section:
80
+
81
+ **Skeleton (skeleton/main.html):**
82
+ ```html
83
+ <!DOCTYPE html>
84
+ <html lang="en">
85
+ <div id="head">
86
+ {{ HEAD }}
87
+ </div>
88
+ <body>
89
+ <header>
90
+ {{ HEADER }}
91
+ </header>
92
+
93
+ <main>
94
+ {{ CONTENT }}
95
+ </main>
96
+
97
+ <footer>
98
+ {{ FOOTER }}
99
+ </footer>
100
+ </body>
101
+ </html>
102
+ ```
103
+
104
+ **Note:** Each placeholder is wrapped in an HTML tag so AJAX can identify and update specific sections.
105
+
106
+ **Head View (view/head/main.html):**
107
+ ```html
108
+ <head>
109
+ <meta charset="UTF-8">
110
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
111
+ <title>{{ Candy.pageTitle }}</title>
112
+ <meta name="description" content="{{ Candy.pageDescription }}">
113
+ <link rel="stylesheet" href="/assets/css/style.css">
114
+ </head>
115
+ ```
116
+
117
+ **Controller:**
118
+ ```javascript
119
+ module.exports = async function (Candy) {
120
+ const productId = Candy.Request.get('id')
121
+ const product = await Candy.Mysql.table('products')
122
+ .where('id', productId)
123
+ .first()
124
+
125
+ // Set dynamic title and description
126
+ Candy.pageTitle = product ? `${product.name} - My Store` : 'Product Not Found'
127
+ Candy.pageDescription = product ? product.short_description : ''
128
+
129
+ Candy.product = product
130
+
131
+ Candy.View.set({
132
+ skeleton: 'main',
133
+ head: 'main', // Include dynamic head
134
+ header: 'main',
135
+ content: 'product.detail',
136
+ footer: 'main'
137
+ })
138
+ }
139
+ ```
140
+
141
+ #### Approach 2: Set Title in Content View
142
+
143
+ Include the title tag in your content view:
144
+
145
+ **Skeleton (skeleton/simple.html):**
146
+ ```html
147
+ <!DOCTYPE html>
148
+ <html lang="en">
149
+ <head>
150
+ <meta charset="UTF-8">
151
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
152
+ <link rel="stylesheet" href="/assets/css/style.css">
153
+ </head>
154
+ <body>
155
+ {{ CONTENT }}
156
+ </body>
157
+ </html>
158
+ ```
159
+
160
+ **Content View (view/content/product.html):**
161
+ ```html
162
+ <title>{{ Candy.product.name }} - My Store</title>
163
+
164
+ <div class="product">
165
+ <h1>{{ Candy.product.name }}</h1>
166
+ <p>{{ Candy.product.description }}</p>
167
+ </div>
168
+ ```
169
+
170
+ **Note:** This approach is less clean but works for simple cases.
171
+
172
+ ### Important Notes
173
+
174
+ - View files must have the `.html` extension
175
+ - Skeleton files should be in the `skeleton/` directory, view files in the `view/` directory
176
+ - Placeholders for view parts are written in uppercase: `{{ HEADER }}`, `{{ CONTENT }}`, etc.
177
+ - View part names are specified in lowercase: `header`, `content`, etc.
178
+ - Variables in skeleton/views are accessed via `Candy` object: `{{ Candy.variableName }}`
179
+ - You don't need to use `return` from the controller, `Candy.View.set()` automatically initiates the rendering process
@@ -0,0 +1,181 @@
1
+ ## 🔧 Template Syntax Overview
2
+
3
+ CandyPack uses a powerful template engine to create dynamic content in view files. The engine provides a clean, HTML-like syntax for displaying variables, conditionals, loops, translations, and more.
4
+
5
+ > **Note:** CandyPack also supports legacy syntax (`{{ }}`, `{!! !!}`, `{{-- --}}`) for backward compatibility, but the new `<candy>` tag syntax is recommended for all new projects.
6
+
7
+ ### Quick Reference
8
+
9
+ This page provides a quick overview of all available template features. For detailed documentation and examples, see the dedicated pages for each feature.
10
+
11
+ ### Variables (Controller Data)
12
+
13
+ Display data passed from controllers using `Candy.set()`:
14
+
15
+ ```html
16
+ <!-- HTML-safe output -->
17
+ <candy var="username" />
18
+
19
+ <!-- Raw HTML output -->
20
+ <candy var="htmlContent" raw />
21
+
22
+ <!-- String literals -->
23
+ <candy>Hello World</candy>
24
+ ```
25
+
26
+ **[→ Learn more about Variables](./03-variables.md)**
27
+
28
+ ### Request Data (Query Parameters)
29
+
30
+ Access URL query parameters directly:
31
+
32
+ ```html
33
+ <!-- Get query parameter from URL -->
34
+ <!-- URL: /search?q=laptop -->
35
+ <candy get="q" />
36
+ <!-- Output: laptop -->
37
+ ```
38
+
39
+ **Note:** `<candy get>` is for URL parameters. For controller data, use `<candy var>`.
40
+
41
+ **[→ Learn more about Request Data](./04-request-data.md)**
42
+
43
+ ### Translations (i18n)
44
+
45
+ Create multi-language applications:
46
+
47
+ ```html
48
+ <!-- Basic translation -->
49
+ <candy translate>Welcome</candy>
50
+
51
+ <!-- With placeholders -->
52
+ <candy translate>Hello <candy var="user.name" /></candy>
53
+
54
+ <!-- With HTML preserved -->
55
+ <candy translate raw>Click <a href="/help">here</a></candy>
56
+ ```
57
+
58
+ **[→ Learn more about Translations](./07-translations.md)**
59
+
60
+ ### Comments
61
+
62
+ Two types of comments for different purposes:
63
+
64
+ ```html
65
+ <!--candy Backend comment (not rendered) -->
66
+
67
+ <!--candy
68
+ Multi-line backend comment
69
+ Won't appear in output
70
+ candy-->
71
+
72
+ <!-- Regular HTML comment (rendered) -->
73
+ ```
74
+
75
+ **[→ Learn more about Comments](./09-comments.md)**
76
+
77
+ ### Conditionals
78
+
79
+ Show or hide content based on conditions:
80
+
81
+ ```html
82
+ <candy:if condition="user.isAdmin">
83
+ <p>Admin panel</p>
84
+ <candy:elseif condition="user.isModerator">
85
+ <p>Moderator panel</p>
86
+ <candy:else>
87
+ <p>User panel</p>
88
+ </candy:if>
89
+ ```
90
+
91
+ **[→ Learn more about Conditionals](./05-conditionals.md)**
92
+
93
+ ### Loops
94
+
95
+ Iterate over arrays and objects:
96
+
97
+ ```html
98
+ <!-- For loop -->
99
+ <candy:for in="users" key="index" value="user">
100
+ <div><candy var="user.name" /></div>
101
+ </candy:for>
102
+
103
+ <!-- While loop -->
104
+ <candy:while condition="counter < 10">
105
+ <p><candy var="counter" /></p>
106
+ </candy:while>
107
+
108
+ <!-- Loop control -->
109
+ <candy:break />
110
+ <candy:continue />
111
+ ```
112
+
113
+ **[→ Learn more about Loops](./06-loops.md)**
114
+
115
+ ### Backend JavaScript
116
+
117
+ Execute JavaScript on the server during template rendering:
118
+
119
+ ```html
120
+ <script:candy>
121
+ // Runs on SERVER before HTML is sent
122
+ let total = 0;
123
+ for (let item of cart) {
124
+ total += item.price * item.quantity;
125
+ }
126
+ </script:candy>
127
+
128
+ <p>Total: $<candy var="total" /></p>
129
+ ```
130
+
131
+ **[→ Learn more about Backend JavaScript](./08-backend-javascript.md)**
132
+
133
+ ### Accessing the Candy Object
134
+
135
+ Full access to the Candy object in templates:
136
+
137
+ ```html
138
+ <candy:if condition="Candy.Auth.check()">
139
+ <p>User: <candy var="Candy.Auth.user().name" /></p>
140
+ </candy:if>
141
+
142
+ <p>URL: <candy var="Candy.Request.url" /></p>
143
+ ```
144
+
145
+ ### Complete Syntax Reference
146
+
147
+ | Feature | Syntax | Documentation |
148
+ |---------|--------|---------------|
149
+ | Variable (Controller) | `<candy var="x" />` | [Variables](./03-variables.md) |
150
+ | Raw HTML | `<candy var="x" raw />` | [Variables](./03-variables.md) |
151
+ | String | `<candy>text</candy>` | [Variables](./03-variables.md) |
152
+ | Query Parameter | `<candy get="key" />` | [Request Data](./04-request-data.md) |
153
+ | Translation | `<candy translate>key</candy>` | [Translations](./07-translations.md) |
154
+ | Translation Raw | `<candy translate raw>key</candy>` | [Translations](./07-translations.md) |
155
+ | If | `<candy:if condition="x">` | [Conditionals](./05-conditionals.md) |
156
+ | Elseif | `<candy:elseif condition="x">` | [Conditionals](./05-conditionals.md) |
157
+ | Else | `<candy:else>` | [Conditionals](./05-conditionals.md) |
158
+ | For | `<candy:for in="x" value="item">` | [Loops](./06-loops.md) |
159
+ | While | `<candy:while condition="x">` | [Loops](./06-loops.md) |
160
+ | Break | `<candy:break />` | [Loops](./06-loops.md) |
161
+ | Continue | `<candy:continue />` | [Loops](./06-loops.md) |
162
+ | JavaScript | `<script:candy>...</script:candy>` | [Backend JavaScript](./08-backend-javascript.md) |
163
+ | Comment | `<!--candy ... candy-->` | [Comments](./09-comments.md) |
164
+
165
+ ### Legacy Syntax
166
+
167
+ CandyPack also supports legacy syntax for backward compatibility:
168
+
169
+ ```html
170
+ <!-- Variable output -->
171
+ {{ username }}
172
+
173
+ <!-- Raw HTML -->
174
+ {!! htmlContent !!}
175
+
176
+ <!-- Comments -->
177
+ {{-- This is a comment --}}
178
+ ```
179
+
180
+ **Note:** The new `<candy>` tag syntax is recommended for all new projects.
181
+