@venizia/ignis-docs 0.0.3 → 0.0.4-1

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 (131) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -2
  3. package/wiki/best-practices/api-usage-examples.md +591 -0
  4. package/wiki/best-practices/architectural-patterns.md +415 -0
  5. package/wiki/best-practices/architecture-decisions.md +488 -0
  6. package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
  7. package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
  8. package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
  9. package/wiki/best-practices/data-modeling.md +376 -0
  10. package/wiki/best-practices/deployment-strategies.md +698 -0
  11. package/wiki/best-practices/index.md +27 -0
  12. package/wiki/best-practices/performance-optimization.md +196 -0
  13. package/wiki/best-practices/security-guidelines.md +218 -0
  14. package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
  15. package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
  16. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
  17. package/wiki/changelogs/2025-12-17-refactor.md +1 -1
  18. package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
  19. package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
  20. package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
  21. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  22. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  23. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  24. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  25. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  26. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  27. package/wiki/changelogs/index.md +6 -0
  28. package/wiki/changelogs/planned-schema-migrator.md +0 -8
  29. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  30. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  31. package/wiki/guides/core-concepts/components-guide.md +509 -0
  32. package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
  33. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  34. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  35. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  36. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  37. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  38. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  39. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  40. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  41. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  42. package/wiki/guides/get-started/philosophy.md +682 -0
  43. package/wiki/guides/get-started/setup.md +157 -0
  44. package/wiki/guides/index.md +89 -0
  45. package/wiki/guides/reference/glossary.md +243 -0
  46. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  47. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  48. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  49. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  50. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  51. package/wiki/guides/tutorials/testing.md +723 -0
  52. package/wiki/index.md +176 -37
  53. package/wiki/references/base/application.md +27 -0
  54. package/wiki/references/base/bootstrapping.md +31 -26
  55. package/wiki/references/base/components.md +24 -7
  56. package/wiki/references/base/controllers.md +50 -20
  57. package/wiki/references/base/datasources.md +30 -0
  58. package/wiki/references/base/dependency-injection.md +39 -3
  59. package/wiki/references/base/filter-system/application-usage.md +224 -0
  60. package/wiki/references/base/filter-system/array-operators.md +132 -0
  61. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  62. package/wiki/references/base/filter-system/default-filter.md +428 -0
  63. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  64. package/wiki/references/base/filter-system/index.md +127 -0
  65. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  66. package/wiki/references/base/filter-system/list-operators.md +71 -0
  67. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  68. package/wiki/references/base/filter-system/null-operators.md +58 -0
  69. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  70. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  71. package/wiki/references/base/filter-system/range-operators.md +63 -0
  72. package/wiki/references/base/filter-system/tips.md +190 -0
  73. package/wiki/references/base/filter-system/use-cases.md +452 -0
  74. package/wiki/references/base/index.md +90 -0
  75. package/wiki/references/base/middlewares.md +604 -0
  76. package/wiki/references/base/models.md +215 -23
  77. package/wiki/references/base/providers.md +731 -0
  78. package/wiki/references/base/repositories/advanced.md +555 -0
  79. package/wiki/references/base/repositories/index.md +228 -0
  80. package/wiki/references/base/repositories/mixins.md +331 -0
  81. package/wiki/references/base/repositories/relations.md +486 -0
  82. package/wiki/references/base/repositories.md +40 -635
  83. package/wiki/references/base/services.md +28 -4
  84. package/wiki/references/components/authentication.md +22 -2
  85. package/wiki/references/components/health-check.md +12 -0
  86. package/wiki/references/components/index.md +23 -0
  87. package/wiki/references/components/mail.md +687 -0
  88. package/wiki/references/components/request-tracker.md +16 -0
  89. package/wiki/references/components/socket-io.md +18 -0
  90. package/wiki/references/components/static-asset.md +14 -26
  91. package/wiki/references/components/swagger.md +17 -0
  92. package/wiki/references/configuration/environment-variables.md +427 -0
  93. package/wiki/references/configuration/index.md +73 -0
  94. package/wiki/references/helpers/cron.md +14 -0
  95. package/wiki/references/helpers/crypto.md +15 -0
  96. package/wiki/references/helpers/env.md +16 -0
  97. package/wiki/references/helpers/error.md +17 -0
  98. package/wiki/references/helpers/index.md +14 -0
  99. package/wiki/references/helpers/inversion.md +24 -4
  100. package/wiki/references/helpers/logger.md +19 -0
  101. package/wiki/references/helpers/network.md +11 -0
  102. package/wiki/references/helpers/queue.md +19 -0
  103. package/wiki/references/helpers/redis.md +21 -0
  104. package/wiki/references/helpers/socket-io.md +24 -5
  105. package/wiki/references/helpers/storage.md +18 -10
  106. package/wiki/references/helpers/testing.md +18 -0
  107. package/wiki/references/helpers/types.md +16 -0
  108. package/wiki/references/helpers/uid.md +167 -0
  109. package/wiki/references/helpers/worker-thread.md +16 -0
  110. package/wiki/references/index.md +177 -0
  111. package/wiki/references/quick-reference.md +634 -0
  112. package/wiki/references/src-details/boot.md +3 -3
  113. package/wiki/references/src-details/dev-configs.md +0 -4
  114. package/wiki/references/src-details/docs.md +2 -2
  115. package/wiki/references/src-details/index.md +86 -0
  116. package/wiki/references/src-details/inversion.md +1 -6
  117. package/wiki/references/src-details/mcp-server.md +3 -15
  118. package/wiki/references/utilities/index.md +86 -10
  119. package/wiki/references/utilities/jsx.md +577 -0
  120. package/wiki/references/utilities/request.md +0 -2
  121. package/wiki/references/utilities/statuses.md +740 -0
  122. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  123. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  124. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  125. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  126. package/wiki/get-started/best-practices/performance-optimization.md +0 -97
  127. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  128. package/wiki/get-started/core-concepts/persistent.md +0 -539
  129. package/wiki/get-started/index.md +0 -65
  130. package/wiki/get-started/philosophy.md +0 -296
  131. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,577 @@
1
+ ---
2
+ title: JSX/HTML Utilities Reference
3
+ description: Utilities for HTML and JSX responses in OpenAPI routes
4
+ difficulty: beginner
5
+ lastUpdated: 2026-01-03
6
+ ---
7
+
8
+ # JSX/HTML Utility
9
+
10
+ The JSX utility provides helper functions for defining HTML/JSX response schemas in OpenAPI routes. These utilities are companions to `jsonContent` and `jsonResponse` but for HTML content type.
11
+
12
+ **File:** `packages/core/src/utilities/jsx.utility.ts`
13
+
14
+ ## Quick Reference
15
+
16
+ | Function | Purpose | Returns |
17
+ |----------|---------|---------|
18
+ | `htmlContent()` | Create HTML content configuration | OpenAPI content object |
19
+ | `htmlResponse()` | Create HTML response with error handling | OpenAPI response object |
20
+
21
+ ## When to Use
22
+
23
+ Use these utilities when creating routes that:
24
+ - Render HTML pages using Hono JSX
25
+ - Return server-side rendered content
26
+ - Serve HTML documentation or views
27
+ - Generate HTML emails or reports
28
+
29
+ ## htmlContent()
30
+
31
+ Creates a standard OpenAPI content object for `text/html` responses.
32
+
33
+ ### Signature
34
+
35
+ ```typescript
36
+ function htmlContent(opts: {
37
+ description: string;
38
+ required?: boolean;
39
+ }): {
40
+ description: string;
41
+ content: {
42
+ 'text/html': {
43
+ schema: ZodString;
44
+ };
45
+ };
46
+ required: boolean;
47
+ }
48
+ ```
49
+
50
+ ### Parameters
51
+
52
+ | Parameter | Type | Required | Default | Description |
53
+ |-----------|------|----------|---------|-------------|
54
+ | `description` | `string` | Yes | - | Description of the HTML content |
55
+ | `required` | `boolean` | No | `false` | Whether the content is required |
56
+
57
+ ### Returns
58
+
59
+ Returns an OpenAPI content configuration object with:
60
+ - `description`: The provided description
61
+ - `content`: Content type configuration for `text/html`
62
+ - `required`: Whether the content is required
63
+
64
+ ### Example
65
+
66
+ ```typescript
67
+ import { htmlContent } from '@venizia/ignis';
68
+
69
+ const pageContent = htmlContent({
70
+ description: 'HTML page content',
71
+ required: true,
72
+ });
73
+
74
+ // Result:
75
+ // {
76
+ // description: 'HTML page content',
77
+ // content: {
78
+ // 'text/html': {
79
+ // schema: z.string().openapi({
80
+ // description: 'HTML content',
81
+ // example: '<!DOCTYPE html><html>...</html>',
82
+ // }),
83
+ // },
84
+ // },
85
+ // required: true,
86
+ // }
87
+ ```
88
+
89
+ ---
90
+
91
+ ## htmlResponse()
92
+
93
+ Creates a standard OpenAPI response object for HTML endpoints, including success (200 OK) HTML response and JSON error responses for 4xx/5xx status codes.
94
+
95
+ ### Signature
96
+
97
+ ```typescript
98
+ function htmlResponse(opts: {
99
+ description: string;
100
+ required?: boolean;
101
+ }): {
102
+ 200: typeof htmlContent;
103
+ '4xx | 5xx': {
104
+ description: 'Error Response';
105
+ content: {
106
+ 'application/json': {
107
+ schema: ErrorSchema;
108
+ };
109
+ };
110
+ };
111
+ }
112
+ ```
113
+
114
+ ### Parameters
115
+
116
+ | Parameter | Type | Required | Default | Description |
117
+ |-----------|------|----------|---------|-------------|
118
+ | `description` | `string` | Yes | - | Description of the successful HTML response |
119
+ | `required` | `boolean` | No | `false` | Whether the content is required |
120
+
121
+ ### Returns
122
+
123
+ Returns an OpenAPI responses object with:
124
+ - `200`: Success response with HTML content
125
+ - `4xx | 5xx`: Error responses with JSON error schema
126
+
127
+ ### Example
128
+
129
+ ```typescript
130
+ import { htmlResponse } from '@venizia/ignis';
131
+
132
+ this.defineRoute({
133
+ configs: {
134
+ path: '/dashboard',
135
+ method: 'get',
136
+ responses: htmlResponse({
137
+ description: 'Dashboard HTML page',
138
+ }),
139
+ },
140
+ handler: async (context) => {
141
+ return context.html(
142
+ <html>
143
+ <head>
144
+ <title>Dashboard</title>
145
+ </head>
146
+ <body>
147
+ <h1>Welcome to Dashboard</h1>
148
+ </body>
149
+ </html>
150
+ );
151
+ },
152
+ });
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Usage Examples
158
+
159
+ ### Basic HTML Route
160
+
161
+ ```typescript
162
+ import { BaseController, get, htmlResponse } from '@venizia/ignis';
163
+
164
+ export class PageController extends BaseController {
165
+ @get({
166
+ path: '/home',
167
+ responses: htmlResponse({
168
+ description: 'Home page HTML',
169
+ }),
170
+ })
171
+ async getHomePage() {
172
+ return this.context.html(
173
+ <html>
174
+ <head>
175
+ <title>Home</title>
176
+ </head>
177
+ <body>
178
+ <h1>Welcome Home</h1>
179
+ </body>
180
+ </html>
181
+ );
182
+ }
183
+ }
184
+ ```
185
+
186
+ ### HTML Email Preview
187
+
188
+ ```typescript
189
+ import { BaseController, get, htmlResponse, TRouteContext, HTTP, z } from '@venizia/ignis';
190
+
191
+ const EmailRoutes = {
192
+ PREVIEW: {
193
+ method: HTTP.Methods.GET,
194
+ path: '/preview/:templateId',
195
+ request: {
196
+ params: z.object({ templateId: z.string() }),
197
+ },
198
+ responses: htmlResponse({
199
+ description: 'Email template preview',
200
+ }),
201
+ },
202
+ } as const;
203
+
204
+ export class EmailController extends BaseController {
205
+ @get({ configs: EmailRoutes.PREVIEW })
206
+ async previewTemplate(c: TRouteContext<typeof EmailRoutes.PREVIEW>) {
207
+ const { templateId } = c.req.valid('param');
208
+ const template = await this.emailService.getTemplate(templateId);
209
+
210
+ return c.html(
211
+ <html>
212
+ <head>
213
+ <title>Email Preview: {template.subject}</title>
214
+ </head>
215
+ <body>
216
+ <div dangerouslySetInnerHTML={{ __html: template.html }} />
217
+ </body>
218
+ </html>
219
+ );
220
+ }
221
+ }
222
+ ```
223
+
224
+ ### Documentation Page
225
+
226
+ ```typescript
227
+ import { BaseController, get, htmlResponse, TRouteContext, HTTP, z } from '@venizia/ignis';
228
+
229
+ const DocsRoutes = {
230
+ GET_SECTION: {
231
+ method: HTTP.Methods.GET,
232
+ path: '/docs/:section',
233
+ request: {
234
+ params: z.object({ section: z.string() }),
235
+ },
236
+ responses: htmlResponse({
237
+ description: 'API documentation page',
238
+ }),
239
+ },
240
+ } as const;
241
+
242
+ export class DocsController extends BaseController {
243
+ @get({ configs: DocsRoutes.GET_SECTION })
244
+ async getDocumentation(c: TRouteContext<typeof DocsRoutes.GET_SECTION>) {
245
+ const { section } = c.req.valid('param');
246
+ const content = await this.docsService.getSection(section);
247
+
248
+ return c.html(
249
+ <html>
250
+ <head>
251
+ <title>Docs - {content.title}</title>
252
+ <link rel="stylesheet" href="/styles/docs.css" />
253
+ </head>
254
+ <body>
255
+ <nav>
256
+ <a href="/docs/getting-started">Getting Started</a>
257
+ <a href="/docs/api">API Reference</a>
258
+ </nav>
259
+ <main>
260
+ <h1>{content.title}</h1>
261
+ <div dangerouslySetInnerHTML={{ __html: content.html }} />
262
+ </main>
263
+ </body>
264
+ </html>
265
+ );
266
+ }
267
+ }
268
+ ```
269
+
270
+ ### Admin Dashboard
271
+
272
+ ```typescript
273
+ import { BaseController, get, htmlResponse } from '@venizia/ignis';
274
+ import { authenticate } from '../middleware/auth';
275
+
276
+ export class AdminController extends BaseController {
277
+ @get({
278
+ path: '/admin',
279
+ middleware: [authenticate({ role: 'admin' })],
280
+ responses: htmlResponse({
281
+ description: 'Admin dashboard',
282
+ }),
283
+ })
284
+ async getDashboard() {
285
+ const stats = await this.statsService.getAdminStats();
286
+
287
+ return this.context.html(
288
+ <html>
289
+ <head>
290
+ <title>Admin Dashboard</title>
291
+ <script src="/js/dashboard.js" defer />
292
+ </head>
293
+ <body>
294
+ <div class="dashboard">
295
+ <h1>Admin Dashboard</h1>
296
+ <div class="stats">
297
+ <div class="stat-card">
298
+ <h3>Total Users</h3>
299
+ <p>{stats.totalUsers}</p>
300
+ </div>
301
+ <div class="stat-card">
302
+ <h3>Active Sessions</h3>
303
+ <p>{stats.activeSessions}</p>
304
+ </div>
305
+ </div>
306
+ </div>
307
+ </body>
308
+ </html>
309
+ );
310
+ }
311
+ }
312
+ ```
313
+
314
+ ---
315
+
316
+ ## Comparison with JSON Utilities
317
+
318
+ ### htmlContent vs jsonContent
319
+
320
+ | Aspect | `htmlContent()` | `jsonContent()` |
321
+ |--------|----------------|-----------------|
322
+ | **Content-Type** | `text/html` | `application/json` |
323
+ | **Schema** | `z.string()` | Custom Zod schema |
324
+ | **Use Case** | HTML pages, JSX rendering | API responses, data |
325
+ | **Example** | HTML document string | JSON object |
326
+
327
+ ### htmlResponse vs jsonResponse
328
+
329
+ | Aspect | `htmlResponse()` | `jsonResponse()` |
330
+ |--------|------------------|------------------|
331
+ | **Success Type** | `text/html` (200) | `application/json` (200) |
332
+ | **Error Type** | `application/json` (4xx/5xx) | `application/json` (4xx/5xx) |
333
+ | **Use Case** | Web pages | REST APIs |
334
+
335
+ ---
336
+
337
+ ## Best Practices
338
+
339
+ ### 1. Use for Server-Side Rendering
340
+
341
+ ```typescript
342
+ // ✅ Good: Use htmlResponse for SSR routes
343
+ const ProfileConfig = {
344
+ method: HTTP.Methods.GET,
345
+ path: '/profile/:userId',
346
+ request: { params: z.object({ userId: z.string() }) },
347
+ responses: htmlResponse({ description: 'User profile page' }),
348
+ } as const;
349
+
350
+ @get({ configs: ProfileConfig })
351
+ async getUserProfile(c: TRouteContext<typeof ProfileConfig>) {
352
+ const { userId } = c.req.valid('param');
353
+ const user = await this.userService.getUser(userId);
354
+ return c.html(<UserProfile user={user} />);
355
+ }
356
+
357
+ // ❌ Bad: Don't use htmlResponse for API endpoints
358
+ const BadConfig = {
359
+ method: HTTP.Methods.GET,
360
+ path: '/api/users/:userId',
361
+ request: { params: z.object({ userId: z.string() }) },
362
+ responses: htmlResponse({ description: 'User data' }), // Wrong!
363
+ } as const;
364
+
365
+ @get({ configs: BadConfig })
366
+ async getUser(c: TRouteContext<typeof BadConfig>) {
367
+ const { userId } = c.req.valid('param');
368
+ return { id: userId, name: 'John' }; // Should use jsonResponse
369
+ }
370
+ ```
371
+
372
+ ### 2. Combine with Authentication
373
+
374
+ ```typescript
375
+ // ✅ Good: Protect HTML routes with auth
376
+ const SettingsConfig = {
377
+ method: HTTP.Methods.GET,
378
+ path: '/admin/settings',
379
+ authStrategies: [Authentication.STRATEGY_JWT],
380
+ responses: htmlResponse({ description: 'Settings page' }),
381
+ } as const;
382
+
383
+ @get({ configs: SettingsConfig })
384
+ async getSettings(c: TRouteContext<typeof SettingsConfig>) {
385
+ return c.html(<SettingsPage />);
386
+ }
387
+ ```
388
+
389
+ ### 3. Error Handling
390
+
391
+ HTML routes automatically return JSON errors for 4xx/5xx:
392
+
393
+ ```typescript
394
+ const ArticleConfig = {
395
+ method: HTTP.Methods.GET,
396
+ path: '/article/:id',
397
+ request: { params: z.object({ id: z.string() }) },
398
+ responses: htmlResponse({ description: 'Article page' }),
399
+ } as const;
400
+
401
+ @get({ configs: ArticleConfig })
402
+ async getArticle(c: TRouteContext<typeof ArticleConfig>) {
403
+ const { id } = c.req.valid('param');
404
+ const article = await this.articleService.findById(id);
405
+
406
+ if (!article) {
407
+ // Returns JSON error: { message: 'Not found', statusCode: 404 }
408
+ throw new NotFoundError('Article not found');
409
+ }
410
+
411
+ return c.html(<ArticlePage article={article} />);
412
+ }
413
+ ```
414
+
415
+ ### 4. SEO-Friendly Metadata
416
+
417
+ ```typescript
418
+ const BlogConfig = {
419
+ method: HTTP.Methods.GET,
420
+ path: '/blog/:slug',
421
+ request: { params: z.object({ slug: z.string() }) },
422
+ responses: htmlResponse({ description: 'Blog post page' }),
423
+ } as const;
424
+
425
+ @get({ configs: BlogConfig })
426
+ async getBlogPost(c: TRouteContext<typeof BlogConfig>) {
427
+ const { slug } = c.req.valid('param');
428
+ const post = await this.blogService.getBySlug(slug);
429
+
430
+ return c.html(
431
+ <html>
432
+ <head>
433
+ <title>{post.title} | My Blog</title>
434
+ <meta name="description" content={post.excerpt} />
435
+ <meta property="og:title" content={post.title} />
436
+ <meta property="og:description" content={post.excerpt} />
437
+ <meta property="og:image" content={post.coverImage} />
438
+ </head>
439
+ <body>
440
+ <article>
441
+ <h1>{post.title}</h1>
442
+ <div dangerouslySetInnerHTML={{ __html: post.content }} />
443
+ </article>
444
+ </body>
445
+ </html>
446
+ );
447
+ }
448
+ ```
449
+
450
+ ---
451
+
452
+ ## Integration with Hono JSX
453
+
454
+ IGNIS uses Hono's built-in JSX support. Make sure to configure your `tsconfig.json`:
455
+
456
+ ```json
457
+ {
458
+ "compilerOptions": {
459
+ "jsx": "react-jsx",
460
+ "jsxImportSource": "hono/jsx"
461
+ }
462
+ }
463
+ ```
464
+
465
+ ### JSX Components
466
+
467
+ ```typescript
468
+ // components/Layout.tsx
469
+ export const Layout = (props: { title: string; children: any }) => {
470
+ return (
471
+ <html>
472
+ <head>
473
+ <title>{props.title}</title>
474
+ <link rel="stylesheet" href="/styles/main.css" />
475
+ </head>
476
+ <body>
477
+ <header>
478
+ <nav>
479
+ <a href="/">Home</a>
480
+ <a href="/about">About</a>
481
+ </nav>
482
+ </header>
483
+ <main>{props.children}</main>
484
+ <footer>
485
+ <p>&copy; 2026 My App</p>
486
+ </footer>
487
+ </body>
488
+ </html>
489
+ );
490
+ };
491
+
492
+ // controller.ts
493
+ import { Layout } from './components/Layout';
494
+
495
+ @get({
496
+ path: '/',
497
+ responses: htmlResponse({ description: 'Home page' }),
498
+ })
499
+ async getHome() {
500
+ return this.context.html(
501
+ <Layout title="Home">
502
+ <h1>Welcome to My App</h1>
503
+ <p>This is the home page.</p>
504
+ </Layout>
505
+ );
506
+ }
507
+ ```
508
+
509
+ ---
510
+
511
+ ## Common Pitfalls
512
+
513
+ ### Pitfall 1: Missing HTML Wrapper
514
+
515
+ ```typescript
516
+ // ❌ Bad: Incomplete HTML
517
+ @get({
518
+ path: '/page',
519
+ responses: htmlResponse({ description: 'Page' }),
520
+ })
521
+ async getPage() {
522
+ return this.context.html(<div>Hello</div>); // Missing <html>, <head>, <body>
523
+ }
524
+
525
+ // ✅ Good: Complete HTML document
526
+ @get({
527
+ path: '/page',
528
+ responses: htmlResponse({ description: 'Page' }),
529
+ })
530
+ async getPage() {
531
+ return this.context.html(
532
+ <html>
533
+ <head><title>Page</title></head>
534
+ <body><div>Hello</div></body>
535
+ </html>
536
+ );
537
+ }
538
+ ```
539
+
540
+ ### Pitfall 2: Using htmlResponse for APIs
541
+
542
+ ```typescript
543
+ // ❌ Bad: HTML response for API
544
+ @get({
545
+ path: '/api/users',
546
+ responses: htmlResponse({ description: 'Users' }),
547
+ })
548
+ async getUsers() {
549
+ return { users: [...] }; // Should return HTML or use jsonResponse
550
+ }
551
+
552
+ // ✅ Good: Use jsonResponse for APIs
553
+ @get({
554
+ path: '/api/users',
555
+ responses: jsonResponse({
556
+ description: 'Users list',
557
+ schema: z.object({ users: z.array(UserSchema) }),
558
+ }),
559
+ })
560
+ async getUsers() {
561
+ return { users: await this.userService.findAll() };
562
+ }
563
+ ```
564
+
565
+ ---
566
+
567
+ ## See Also
568
+
569
+ - **Related References:**
570
+ - [Schema Utility](./schema.md) - JSON content and response helpers
571
+ - [Controllers](../base/controllers.md) - Defining routes and handlers
572
+ - [OpenAPI Component](../components/swagger.md) - API documentation
573
+
574
+ - **External Resources:**
575
+ - [Hono JSX Documentation](https://hono.dev/guides/jsx)
576
+ - [OpenAPI Specification](https://swagger.io/specification/)
577
+ - [React JSX (for reference)](https://react.dev/learn/writing-markup-with-jsx)
@@ -139,7 +139,6 @@ sanitizeFilename(''); // Returns: 'download'
139
139
  sanitizeFilename('..'); // Returns: 'download'
140
140
  ```
141
141
 
142
- ---
143
142
 
144
143
  ### `encodeRFC5987`
145
144
 
@@ -160,7 +159,6 @@ encodeRFC5987('my document.pdf'); // Returns: 'my%20document.pdf'
160
159
  encodeRFC5987('файл.txt'); // Returns: '%D1%84%D0%B0%D0%B9%D0%BB.txt'
161
160
  ```
162
161
 
163
- ---
164
162
 
165
163
  ## Complete File Download Example
166
164