@venizia/ignis-docs 0.0.7 → 0.0.8-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 (158) hide show
  1. package/dist/mcp-server/common/paths.d.ts +4 -2
  2. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  3. package/dist/mcp-server/common/paths.js +8 -6
  4. package/dist/mcp-server/common/paths.js.map +1 -1
  5. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts +1 -1
  6. package/dist/mcp-server/tools/docs/get-document-content.tool.d.ts.map +1 -1
  7. package/dist/mcp-server/tools/docs/get-document-content.tool.js +7 -7
  8. package/dist/mcp-server/tools/docs/get-document-metadata.tool.js +3 -3
  9. package/dist/mcp-server/tools/docs/get-package-overview.tool.d.ts +1 -1
  10. package/dist/mcp-server/tools/docs/get-package-overview.tool.js +1 -1
  11. package/package.json +1 -1
  12. package/wiki/best-practices/api-usage-examples.md +9 -9
  13. package/wiki/best-practices/architectural-patterns.md +19 -3
  14. package/wiki/best-practices/architecture-decisions.md +6 -6
  15. package/wiki/best-practices/code-style-standards/advanced-patterns.md +1 -1
  16. package/wiki/best-practices/code-style-standards/control-flow.md +1 -1
  17. package/wiki/best-practices/code-style-standards/function-patterns.md +2 -2
  18. package/wiki/best-practices/code-style-standards/index.md +2 -2
  19. package/wiki/best-practices/code-style-standards/naming-conventions.md +1 -1
  20. package/wiki/best-practices/code-style-standards/route-definitions.md +4 -4
  21. package/wiki/best-practices/data-modeling.md +1 -1
  22. package/wiki/best-practices/deployment-strategies.md +1 -1
  23. package/wiki/best-practices/error-handling.md +2 -2
  24. package/wiki/best-practices/performance-optimization.md +3 -3
  25. package/wiki/best-practices/security-guidelines.md +2 -2
  26. package/wiki/best-practices/troubleshooting-tips.md +1 -1
  27. package/wiki/{references → extensions}/components/authentication/api.md +12 -20
  28. package/wiki/{references → extensions}/components/authentication/errors.md +1 -1
  29. package/wiki/{references → extensions}/components/authentication/index.md +5 -8
  30. package/wiki/{references → extensions}/components/authentication/usage.md +20 -36
  31. package/wiki/{references → extensions}/components/authorization/api.md +62 -13
  32. package/wiki/{references → extensions}/components/authorization/errors.md +12 -7
  33. package/wiki/{references → extensions}/components/authorization/index.md +93 -6
  34. package/wiki/{references → extensions}/components/authorization/usage.md +42 -4
  35. package/wiki/{references → extensions}/components/health-check.md +5 -4
  36. package/wiki/{references → extensions}/components/index.md +2 -0
  37. package/wiki/{references → extensions}/components/mail/index.md +1 -1
  38. package/wiki/{references → extensions}/components/request-tracker.md +1 -1
  39. package/wiki/{references → extensions}/components/socket-io/api.md +2 -2
  40. package/wiki/{references → extensions}/components/socket-io/errors.md +2 -0
  41. package/wiki/{references → extensions}/components/socket-io/index.md +24 -20
  42. package/wiki/{references → extensions}/components/socket-io/usage.md +2 -2
  43. package/wiki/{references → extensions}/components/static-asset/api.md +14 -15
  44. package/wiki/{references → extensions}/components/static-asset/errors.md +3 -1
  45. package/wiki/{references → extensions}/components/static-asset/index.md +158 -89
  46. package/wiki/{references → extensions}/components/static-asset/usage.md +8 -5
  47. package/wiki/{references → extensions}/components/swagger.md +3 -3
  48. package/wiki/{references → extensions}/components/template/index.md +4 -4
  49. package/wiki/{references → extensions}/components/template/setup-page.md +1 -1
  50. package/wiki/{references → extensions}/components/template/single-page.md +1 -1
  51. package/wiki/{references → extensions}/components/websocket/api.md +7 -6
  52. package/wiki/{references → extensions}/components/websocket/errors.md +17 -3
  53. package/wiki/{references → extensions}/components/websocket/index.md +17 -11
  54. package/wiki/{references → extensions}/components/websocket/usage.md +2 -2
  55. package/wiki/{references → extensions}/helpers/crypto/index.md +1 -1
  56. package/wiki/{references → extensions}/helpers/env/index.md +9 -5
  57. package/wiki/{references → extensions}/helpers/error/index.md +2 -7
  58. package/wiki/{references → extensions}/helpers/index.md +18 -6
  59. package/wiki/{references → extensions}/helpers/kafka/admin.md +13 -1
  60. package/wiki/{references → extensions}/helpers/kafka/consumer.md +28 -28
  61. package/wiki/{references → extensions}/helpers/kafka/examples.md +19 -19
  62. package/wiki/{references → extensions}/helpers/kafka/index.md +51 -48
  63. package/wiki/{references → extensions}/helpers/kafka/producer.md +18 -18
  64. package/wiki/{references → extensions}/helpers/kafka/schema-registry.md +25 -25
  65. package/wiki/{references → extensions}/helpers/logger/index.md +2 -2
  66. package/wiki/{references → extensions}/helpers/queue/index.md +400 -4
  67. package/wiki/{references → extensions}/helpers/storage/api.md +170 -10
  68. package/wiki/{references → extensions}/helpers/storage/index.md +44 -8
  69. package/wiki/{references → extensions}/helpers/template/index.md +1 -1
  70. package/wiki/{references → extensions}/helpers/testing/index.md +4 -4
  71. package/wiki/{references → extensions}/helpers/types/index.md +63 -16
  72. package/wiki/{references → extensions}/helpers/websocket/index.md +1 -1
  73. package/wiki/extensions/index.md +48 -0
  74. package/wiki/guides/core-concepts/application/bootstrapping.md +55 -37
  75. package/wiki/guides/core-concepts/application/index.md +95 -35
  76. package/wiki/guides/core-concepts/components-guide.md +23 -19
  77. package/wiki/guides/core-concepts/components.md +34 -10
  78. package/wiki/guides/core-concepts/dependency-injection.md +99 -34
  79. package/wiki/guides/core-concepts/grpc-controllers.md +295 -0
  80. package/wiki/guides/core-concepts/persistent/datasources.md +27 -8
  81. package/wiki/guides/core-concepts/persistent/models.md +43 -1
  82. package/wiki/guides/core-concepts/persistent/repositories.md +75 -8
  83. package/wiki/guides/core-concepts/persistent/transactions.md +38 -8
  84. package/wiki/guides/core-concepts/{controllers.md → rest-controllers.md} +30 -33
  85. package/wiki/guides/core-concepts/services.md +19 -5
  86. package/wiki/guides/get-started/5-minute-quickstart.md +6 -7
  87. package/wiki/guides/get-started/philosophy.md +1 -1
  88. package/wiki/guides/index.md +2 -2
  89. package/wiki/guides/reference/glossary.md +7 -7
  90. package/wiki/guides/reference/mcp-docs-server.md +1 -1
  91. package/wiki/guides/tutorials/building-a-crud-api.md +2 -2
  92. package/wiki/guides/tutorials/complete-installation.md +17 -14
  93. package/wiki/guides/tutorials/ecommerce-api.md +18 -18
  94. package/wiki/guides/tutorials/realtime-chat.md +8 -8
  95. package/wiki/guides/tutorials/testing.md +2 -2
  96. package/wiki/index.md +4 -3
  97. package/wiki/references/base/application.md +341 -21
  98. package/wiki/references/base/bootstrapping.md +43 -13
  99. package/wiki/references/base/components.md +259 -8
  100. package/wiki/references/base/controllers.md +556 -253
  101. package/wiki/references/base/datasources.md +159 -79
  102. package/wiki/references/base/dependency-injection.md +299 -48
  103. package/wiki/references/base/filter-system/application-usage.md +18 -2
  104. package/wiki/references/base/filter-system/array-operators.md +14 -6
  105. package/wiki/references/base/filter-system/comparison-operators.md +9 -3
  106. package/wiki/references/base/filter-system/default-filter.md +28 -3
  107. package/wiki/references/base/filter-system/fields-order-pagination.md +17 -13
  108. package/wiki/references/base/filter-system/index.md +169 -11
  109. package/wiki/references/base/filter-system/json-filtering.md +51 -18
  110. package/wiki/references/base/filter-system/list-operators.md +4 -3
  111. package/wiki/references/base/filter-system/logical-operators.md +7 -2
  112. package/wiki/references/base/filter-system/null-operators.md +50 -0
  113. package/wiki/references/base/filter-system/quick-reference.md +82 -243
  114. package/wiki/references/base/filter-system/range-operators.md +7 -1
  115. package/wiki/references/base/filter-system/tips.md +34 -7
  116. package/wiki/references/base/filter-system/use-cases.md +6 -5
  117. package/wiki/references/base/grpc-controllers.md +984 -0
  118. package/wiki/references/base/index.md +32 -24
  119. package/wiki/references/base/middleware.md +347 -0
  120. package/wiki/references/base/models.md +390 -46
  121. package/wiki/references/base/providers.md +14 -14
  122. package/wiki/references/base/repositories/advanced.md +84 -69
  123. package/wiki/references/base/repositories/index.md +447 -12
  124. package/wiki/references/base/repositories/mixins.md +103 -98
  125. package/wiki/references/base/repositories/relations.md +129 -45
  126. package/wiki/references/base/repositories/soft-deletable.md +104 -23
  127. package/wiki/references/base/services.md +94 -14
  128. package/wiki/references/index.md +12 -10
  129. package/wiki/references/quick-reference.md +98 -65
  130. package/wiki/references/utilities/crypto.md +21 -4
  131. package/wiki/references/utilities/date.md +25 -7
  132. package/wiki/references/utilities/index.md +26 -24
  133. package/wiki/references/utilities/jsx.md +54 -54
  134. package/wiki/references/utilities/module.md +8 -6
  135. package/wiki/references/utilities/parse.md +16 -9
  136. package/wiki/references/utilities/performance.md +22 -7
  137. package/wiki/references/utilities/promise.md +19 -16
  138. package/wiki/references/utilities/request.md +48 -26
  139. package/wiki/references/utilities/schema.md +69 -6
  140. package/wiki/references/utilities/statuses.md +131 -140
  141. /package/wiki/{references → extensions}/components/mail/api.md +0 -0
  142. /package/wiki/{references → extensions}/components/mail/errors.md +0 -0
  143. /package/wiki/{references → extensions}/components/mail/usage.md +0 -0
  144. /package/wiki/{references → extensions}/components/template/api-page.md +0 -0
  145. /package/wiki/{references → extensions}/components/template/errors-page.md +0 -0
  146. /package/wiki/{references → extensions}/components/template/usage-page.md +0 -0
  147. /package/wiki/{references → extensions}/helpers/cron/index.md +0 -0
  148. /package/wiki/{references → extensions}/helpers/inversion/index.md +0 -0
  149. /package/wiki/{references → extensions}/helpers/network/api.md +0 -0
  150. /package/wiki/{references → extensions}/helpers/network/index.md +0 -0
  151. /package/wiki/{references → extensions}/helpers/redis/index.md +0 -0
  152. /package/wiki/{references → extensions}/helpers/socket-io/api.md +0 -0
  153. /package/wiki/{references → extensions}/helpers/socket-io/index.md +0 -0
  154. /package/wiki/{references → extensions}/helpers/template/single-page.md +0 -0
  155. /package/wiki/{references → extensions}/helpers/uid/index.md +0 -0
  156. /package/wiki/{references → extensions}/helpers/websocket/api.md +0 -0
  157. /package/wiki/{references → extensions}/helpers/worker-thread/index.md +0 -0
  158. /package/wiki/{references → extensions}/src-details/mcp-server.md +0 -0
@@ -89,7 +89,7 @@ const pageContent = htmlContent({
89
89
 
90
90
  ## htmlResponse()
91
91
 
92
- Creates a standard OpenAPI response object for HTML endpoints, including success (200 OK) HTML response and JSON error responses for 4xx/5xx status codes.
92
+ Creates a standard OpenAPI response object for HTML endpoints, including a success (200 OK) HTML response and a JSON error response for `4xx | 5xx` status codes using the `ErrorSchema`.
93
93
 
94
94
  ### Signature
95
95
 
@@ -120,7 +120,7 @@ function htmlResponse(opts: {
120
120
  ### Returns
121
121
 
122
122
  Returns an OpenAPI responses object with:
123
- - `200`: Success response with HTML content
123
+ - `200`: Success response with HTML content (via `htmlContent()`)
124
124
  - `4xx | 5xx`: Error responses with JSON error schema
125
125
 
126
126
  ### Example
@@ -157,14 +157,16 @@ this.defineRoute({
157
157
  ### Basic HTML Route
158
158
 
159
159
  ```typescript
160
- import { BaseController, get, htmlResponse } from '@venizia/ignis';
160
+ import { BaseRestController, get, htmlResponse } from '@venizia/ignis';
161
161
 
162
- export class PageController extends BaseController {
162
+ export class PageController extends BaseRestController {
163
163
  @get({
164
- path: '/home',
165
- responses: htmlResponse({
166
- description: 'Home page HTML',
167
- }),
164
+ configs: {
165
+ path: '/home',
166
+ responses: htmlResponse({
167
+ description: 'Home page HTML',
168
+ }),
169
+ },
168
170
  })
169
171
  async getHomePage() {
170
172
  return this.context.html(
@@ -184,7 +186,7 @@ export class PageController extends BaseController {
184
186
  ### HTML Email Preview
185
187
 
186
188
  ```typescript
187
- import { BaseController, get, htmlResponse, TRouteContext, z } from '@venizia/ignis';
189
+ import { BaseRestController, get, htmlResponse, TRouteContext, z } from '@venizia/ignis';
188
190
  import { HTTP } from '@venizia/ignis-helpers';
189
191
 
190
192
  const EmailRoutes = {
@@ -200,7 +202,7 @@ const EmailRoutes = {
200
202
  },
201
203
  } as const;
202
204
 
203
- export class EmailController extends BaseController {
205
+ export class EmailController extends BaseRestController {
204
206
  @get({ configs: EmailRoutes.PREVIEW })
205
207
  async previewTemplate(c: TRouteContext) {
206
208
  const { templateId } = c.req.valid<{ templateId: string }>('param');
@@ -223,7 +225,7 @@ export class EmailController extends BaseController {
223
225
  ### Documentation Page
224
226
 
225
227
  ```typescript
226
- import { BaseController, get, htmlResponse, TRouteContext, z } from '@venizia/ignis';
228
+ import { BaseRestController, get, htmlResponse, TRouteContext, z } from '@venizia/ignis';
227
229
  import { HTTP } from '@venizia/ignis-helpers';
228
230
 
229
231
  const DocsRoutes = {
@@ -239,7 +241,7 @@ const DocsRoutes = {
239
241
  },
240
242
  } as const;
241
243
 
242
- export class DocsController extends BaseController {
244
+ export class DocsController extends BaseRestController {
243
245
  @get({ configs: DocsRoutes.GET_SECTION })
244
246
  async getDocumentation(c: TRouteContext) {
245
247
  const { section } = c.req.valid<{ section: string }>('param');
@@ -270,16 +272,17 @@ export class DocsController extends BaseController {
270
272
  ### Admin Dashboard
271
273
 
272
274
  ```typescript
273
- import { BaseController, get, htmlResponse } from '@venizia/ignis';
274
- import { authenticate } from '../middleware/auth';
275
+ import { BaseRestController, get, htmlResponse } from '@venizia/ignis';
275
276
 
276
- export class AdminController extends BaseController {
277
+ export class AdminController extends BaseRestController {
277
278
  @get({
278
- path: '/admin',
279
- middleware: [authenticate({ role: 'admin' })],
280
- responses: htmlResponse({
281
- description: 'Admin dashboard',
282
- }),
279
+ configs: {
280
+ path: '/admin',
281
+ middleware: [authenticate({ role: 'admin' })],
282
+ responses: htmlResponse({
283
+ description: 'Admin dashboard',
284
+ }),
285
+ },
283
286
  })
284
287
  async getDashboard() {
285
288
  const stats = await this.statsService.getAdminStats();
@@ -337,7 +340,7 @@ export class AdminController extends BaseController {
337
340
  ### 1. Use for Server-Side Rendering
338
341
 
339
342
  ```typescript
340
- // Good: Use htmlResponse for SSR routes
343
+ // Good: Use htmlResponse for SSR routes
341
344
  const ProfileConfig = {
342
345
  method: HTTP.Methods.GET,
343
346
  path: '/profile/:userId',
@@ -352,29 +355,16 @@ async getUserProfile(c: TRouteContext) {
352
355
  return c.html(<UserProfile user={user} />);
353
356
  }
354
357
 
355
- // Bad: Don't use htmlResponse for API endpoints
356
- const BadConfig = {
357
- method: HTTP.Methods.GET,
358
- path: '/api/users/:userId',
359
- request: { params: z.object({ userId: z.string() }) },
360
- responses: htmlResponse({ description: 'User data' }), // Wrong!
361
- } as const;
362
-
363
- @get({ configs: BadConfig })
364
- async getUser(c: TRouteContext) {
365
- const { userId } = c.req.valid<{ userId: string }>('param');
366
- return { id: userId, name: 'John' }; // Should use jsonResponse
367
- }
358
+ // Bad: Don't use htmlResponse for API endpoints — use jsonResponse instead
368
359
  ```
369
360
 
370
361
  ### 2. Combine with Authentication
371
362
 
372
363
  ```typescript
373
- // ✅ Good: Protect HTML routes with auth
374
364
  const SettingsConfig = {
375
365
  method: HTTP.Methods.GET,
376
366
  path: '/admin/settings',
377
- authStrategies: [Authentication.STRATEGY_JWT],
367
+ authenticate: { strategies: ['jwt'] },
378
368
  responses: htmlResponse({ description: 'Settings page' }),
379
369
  } as const;
380
370
 
@@ -490,8 +480,10 @@ export const Layout = (props: { title: string; children: any }) => {
490
480
  import { Layout } from './components/Layout';
491
481
 
492
482
  @get({
493
- path: '/',
494
- responses: htmlResponse({ description: 'Home page' }),
483
+ configs: {
484
+ path: '/',
485
+ responses: htmlResponse({ description: 'Home page' }),
486
+ },
495
487
  })
496
488
  async getHome() {
497
489
  return this.context.html(
@@ -509,19 +501,23 @@ async getHome() {
509
501
  ### Pitfall 1: Missing HTML Wrapper
510
502
 
511
503
  ```typescript
512
- // Bad: Incomplete HTML
504
+ // Bad: Incomplete HTML
513
505
  @get({
514
- path: '/page',
515
- responses: htmlResponse({ description: 'Page' }),
506
+ configs: {
507
+ path: '/page',
508
+ responses: htmlResponse({ description: 'Page' }),
509
+ },
516
510
  })
517
511
  async getPage() {
518
512
  return this.context.html(<div>Hello</div>); // Missing <html>, <head>, <body>
519
513
  }
520
514
 
521
- // Good: Complete HTML document
515
+ // Good: Complete HTML document
522
516
  @get({
523
- path: '/page',
524
- responses: htmlResponse({ description: 'Page' }),
517
+ configs: {
518
+ path: '/page',
519
+ responses: htmlResponse({ description: 'Page' }),
520
+ },
525
521
  })
526
522
  async getPage() {
527
523
  return this.context.html(
@@ -536,22 +532,26 @@ async getPage() {
536
532
  ### Pitfall 2: Using htmlResponse for APIs
537
533
 
538
534
  ```typescript
539
- // Bad: HTML response for API
535
+ // Bad: HTML response for API
540
536
  @get({
541
- path: '/api/users',
542
- responses: htmlResponse({ description: 'Users' }),
537
+ configs: {
538
+ path: '/api/users',
539
+ responses: htmlResponse({ description: 'Users' }),
540
+ },
543
541
  })
544
542
  async getUsers() {
545
543
  return { users: [...] }; // Should return HTML or use jsonResponse
546
544
  }
547
545
 
548
- // Good: Use jsonResponse for APIs
546
+ // Good: Use jsonResponse for APIs
549
547
  @get({
550
- path: '/api/users',
551
- responses: jsonResponse({
552
- description: 'Users list',
553
- schema: z.object({ users: z.array(UserSchema) }),
554
- }),
548
+ configs: {
549
+ path: '/api/users',
550
+ responses: jsonResponse({
551
+ description: 'Users list',
552
+ schema: z.object({ users: z.array(UserSchema) }),
553
+ }),
554
+ },
555
555
  })
556
556
  async getUsers() {
557
557
  return { users: await this.userService.findAll() };
@@ -564,7 +564,7 @@ async getUsers() {
564
564
  - **Related References:**
565
565
  - [Schema Utility](./schema.md) - JSON content and response helpers
566
566
  - [Controllers](../base/controllers.md) - Defining routes and handlers
567
- - [OpenAPI Component](../components/swagger) - API documentation
567
+ - [OpenAPI Component](/extensions/components/swagger) - API documentation
568
568
 
569
569
  - **External Resources:**
570
570
  - [Hono JSX Documentation](https://hono.dev/guides/jsx)
@@ -1,16 +1,18 @@
1
1
  # Module Utility
2
2
 
3
- The Module utility provides a simple function to validate the existence of a Node.js module at runtime.
3
+ The Module utility provides a function to validate the existence of Node.js modules at runtime. It uses `createRequire` from `node:module` rooted at the application's `process.cwd()/node_modules`, so peer dependencies in the consuming application are properly resolved even though this utility lives inside `packages/helpers`.
4
4
 
5
5
  ## `validateModule`
6
6
 
7
- The `validateModule` function checks if a list of modules can be resolved. If a module is not found, it throws a descriptive error, prompting the developer to install it. This is particularly useful for features that have optional peer dependencies.
7
+ The `validateModule` function checks if a list of modules can be resolved. If a module is not found, it logs the error and throws a descriptive error, prompting the developer to install it. This is particularly useful for features that have optional peer dependencies.
8
8
 
9
9
  ### `validateModule(opts)`
10
10
 
11
11
  - `opts` (object):
12
- - `scope` (string, optional): A string to identify the feature or component that requires the module, making the error message more informative.
13
- - `modules` (Array&lt;string&gt;): An array of module names to validate.
12
+ - `scope` (string, optional): A string to identify the feature or component that requires the module, making the error message more informative. Defaults to empty string.
13
+ - `modules` (Array&lt;string&gt;): An array of module names to validate. Defaults to empty array.
14
+
15
+ This is an `async` function, though it performs synchronous resolution internally.
14
16
 
15
17
  ### Example
16
18
 
@@ -24,7 +26,7 @@ export class SwaggerComponent extends BaseComponent {
24
26
 
25
27
  override async binding() {
26
28
  // This will throw an error if '@hono/swagger-ui' is not installed
27
- validateModule({ scope: SwaggerComponent.name, modules: ['@hono/swagger-ui'] });
29
+ await validateModule({ scope: SwaggerComponent.name, modules: ['@hono/swagger-ui'] });
28
30
 
29
31
  const { swaggerUI } = await import('@hono/swagger-ui');
30
32
 
@@ -33,7 +35,7 @@ export class SwaggerComponent extends BaseComponent {
33
35
  }
34
36
  ```
35
37
 
36
- If the module is missing, the application will fail to start with an error message like:
38
+ If the module is missing, the application will fail with an error message like:
37
39
 
38
40
  ```
39
41
  [validateModule] @hono/swagger-ui is required for SwaggerComponent. Please install '@hono/swagger-ui'
@@ -9,17 +9,19 @@ The Parse utility provides a collection of functions for data type checking, con
9
9
 
10
10
  ## Type Conversion
11
11
 
12
- - **`int(input)`**: Parses a value to an integer. Handles string with commas and defaults to `0` if the input is invalid.
13
- - **`float(input, digit = 2)`**: Parses a value to a float, rounding to a specified number of digits. Handles string with commas and defaults to `0` if the input is invalid.
14
- - **`toBoolean(input)`**: Converts various string/number representations (e.g., `'true'`, `'1'`, `1`) to a boolean.
15
- - **`toStringDecimal(input, digit = 2)`**: Formats a number to a string with a specified number of decimal places, using locale-specific formatting.
12
+ - **`int(input)`**: Parses a value to an integer. Handles strings with commas and defaults to `0` if the input is invalid.
13
+ - **`float(input, digit = 2)`**: Parses a value to a float, rounding to a specified number of digits. Handles strings with commas and defaults to `0` if the input is invalid.
14
+ - **`toBoolean(input)`**: Converts a value to a boolean. Returns `false` for empty string, `'false'`, `'0'`, `false`, `0`, `null`, and `undefined`. Returns `true` for everything else.
15
+ - **`toStringDecimal(input, digit = 2, options?)`**: Formats a number to a string with a specified number of decimal places. By default uses locale-specific formatting (`useLocaleFormat: true`). When `useLocaleFormat` is `false`, uses `toFixed()` instead.
16
16
 
17
17
  ```typescript
18
- import { int, float, toBoolean } from '@venizia/ignis-helpers';
18
+ import { int, float, toBoolean, toStringDecimal } from '@venizia/ignis-helpers';
19
19
 
20
20
  const myInt = int('1,000'); // => 1000
21
21
  const myFloat = float('1,234.567', 2); // => 1234.57
22
22
  const myBool = toBoolean('true'); // => true
23
+ const formatted = toStringDecimal(1234.5, 2); // => '1,234.50'
24
+ const fixed = toStringDecimal(1234.5, 2, { useLocaleFormat: false }); // => '1234.50'
23
25
  ```
24
26
 
25
27
  ## String and Object Transformation
@@ -59,17 +61,22 @@ getNumberValue('1.234,56', { method: 'float', locale: 'eu' }); // => 1234.56
59
61
 
60
62
  ## Array Transformation
61
63
 
62
- - **`parseArrayToRecordWithKey(opts)`**: Transforms an array of objects into a record (plain object), using a specified property of the objects as keys.
63
- - **`parseArrayToMapWithKey(arr, keyMap)`**: Transforms an array of objects into a `Map`, using a specified property of the objects as keys. This is useful for efficient lookups.
64
+ - **`parseArrayToRecordWithKey(opts)`**: Transforms an array of objects into a record (plain object), using a specified property of the objects as keys. Takes an options object with `arr` and `keyMap` properties. Throws an error if `keyMap` is not found in an element. Last element wins on duplicate keys.
65
+ - **`parseArrayToMapWithKey(arr, keyMap)`**: Transforms an array of objects into a `Map`, using a specified property of the objects as keys. Takes positional arguments (not an options object). Throws an error if `keyMap` is not found in an element. Last element wins on duplicate keys.
64
66
 
65
67
  ```typescript
66
- import { parseArrayToMapWithKey } from '@venizia/ignis-helpers';
68
+ import { parseArrayToRecordWithKey, parseArrayToMapWithKey } from '@venizia/ignis-helpers';
67
69
 
68
70
  const users = [
69
71
  { id: 1, name: 'Alice' },
70
72
  { id: 2, name: 'Bob' },
71
73
  ];
72
74
 
75
+ // Record (options object pattern)
76
+ const usersRecord = parseArrayToRecordWithKey({ arr: users, keyMap: 'id' });
77
+ // => { 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } }
78
+
79
+ // Map (positional arguments)
73
80
  const usersMap = parseArrayToMapWithKey(users, 'id');
74
81
  // => Map { 1 => { id: 1, name: 'Alice' }, 2 => { id: 2, name: 'Bob' } }
75
82
 
@@ -79,7 +86,7 @@ const user = usersMap.get(1);
79
86
 
80
87
  ## Unique ID
81
88
 
82
- - **`getUID()`**: Generates a simple, short unique ID string.
89
+ - **`getUID()`**: Generates a simple, short unique ID string based on `Math.random()`, returned in uppercase.
83
90
 
84
91
  ```typescript
85
92
  import { getUID } from '@venizia/ignis-helpers';
@@ -1,18 +1,20 @@
1
1
  # Performance Utility
2
2
 
3
- The Performance utility provides functions for measuring and logging the execution time of code blocks, which is useful for identifying bottlenecks and optimizing your application.
3
+ The Performance utility provides functions for measuring and logging the execution time of code blocks, which is useful for identifying bottlenecks and optimizing your application. All timing uses `performance.now()` (milliseconds).
4
4
 
5
5
  ## `executeWithPerformanceMeasure`
6
6
 
7
- This is a higher-order function that wraps a task (a function returning a Promise), automatically logging its start time, end time, and total execution duration.
7
+ This is a higher-order function that wraps a task (a function), automatically logging its start time, end time, and total execution duration. Returns a Promise that resolves to the task's return value.
8
8
 
9
9
  ### `executeWithPerformanceMeasure(opts)`
10
10
 
11
11
  - `opts` (object):
12
- - `logger` (ApplicationLogger, optional): A logger instance to use for logging. Defaults to `console`.
13
- - `description` (string, optional): A description of the task being measured.
12
+ - `logger` (Logger, optional): A logger instance to use for logging. Defaults to `console`.
13
+ - `level` (string, optional): The log level to use (e.g., `'debug'`, `'info'`). Defaults to `'debug'`.
14
+ - `description` (string, optional): A description of the task being measured. Defaults to `'Executing'`.
14
15
  - `scope` (string): A scope to identify the context of the measurement.
15
- - `task` (Function): The asynchronous function (or a function that returns a Promise) to be executed and measured.
16
+ - `task` (Function): The function to be executed and measured. Can be sync or async (wrapped with `Promise.resolve()`).
17
+ - `args` (any, optional): Arguments to include in the log output for debugging purposes. When provided, they are logged as JSON (`%j` format).
16
18
 
17
19
  ### Example
18
20
 
@@ -43,12 +45,21 @@ When `registerComponents` is called, it will produce log output similar to this:
43
45
  [RegisterComponents] DONE | Register application components | Took: 12.3456 (ms)
44
46
  ```
45
47
 
48
+ With `args` provided:
49
+
50
+ ```
51
+ [RegisterComponents] START | Register application components... | Args: {"name":"auth"}
52
+ [RegisterComponents] DONE | Register application components | Args: {"name":"auth"} | Took: 12.3456 (ms)
53
+ ```
54
+
46
55
  ## Low-Level Utilities
47
56
 
48
57
  For more granular measurements, you can use the lower-level functions:
49
58
 
50
- - **`getPerformanceCheckpoint()`**: Returns a high-resolution timestamp, which you can use as a starting point.
51
- - **`getExecutedPerformance(opts)`**: Calculates the elapsed time in milliseconds since a given checkpoint.
59
+ - **`getPerformanceCheckpoint()`**: Returns a high-resolution timestamp from `performance.now()`, which you can use as a starting point.
60
+ - **`getExecutedPerformance(opts)`**: Calculates the elapsed time in milliseconds since a given checkpoint. Rounds to 6 decimal places by default.
61
+ - `opts.from` (number): The starting checkpoint from `getPerformanceCheckpoint()`.
62
+ - `opts.digit` (number, optional): Number of decimal places for rounding. Defaults to `6`.
52
63
 
53
64
  ### Example
54
65
 
@@ -61,4 +72,8 @@ const start = getPerformanceCheckpoint();
61
72
 
62
73
  const duration = getExecutedPerformance({ from: start });
63
74
  console.log(`The work took ${duration} ms.`);
75
+
76
+ // With custom precision
77
+ const preciseDuration = getExecutedPerformance({ from: start, digit: 2 });
78
+ console.log(`The work took ${preciseDuration} ms.`);
64
79
  ```
@@ -11,7 +11,7 @@ This function executes an array of asynchronous tasks concurrently, but with a s
11
11
  - `opts` (object):
12
12
  - `tasks` (Array&lt;() => Promise&lt;T&gt;&gt;): An array of functions that each return a Promise.
13
13
  - `limit` (number): The maximum number of promises to execute in parallel.
14
- - `onTaskDone` (&lt;R&gt;(opts: { result: R }) => ValueOrPromise&lt;void&gt;, optional): A callback function that is executed whenever a task is completed.
14
+ - `onTaskDone` (&lt;R&gt;(opts: { result: R }) => ValueOrPromise&lt;void&gt;, optional): A callback function that is executed whenever a task is completed (specifically, when the concurrency limit is reached and a task finishes to make room).
15
15
 
16
16
  ### Example
17
17
 
@@ -38,9 +38,22 @@ const results = await executePromiseWithLimit({
38
38
  console.log('All tasks finished:', results);
39
39
  ```
40
40
 
41
+ ## `transformValueOrPromise`
42
+
43
+ This async function applies a transformation function to a value that might be a direct value or a Promise. It always returns a Promise.
44
+
45
+ ```typescript
46
+ import { transformValueOrPromise } from '@venizia/ignis-helpers';
47
+
48
+ const double = (n: number) => n * 2;
49
+
50
+ const result1 = await transformValueOrPromise(5, double); // => 10
51
+ const result2 = await transformValueOrPromise(Promise.resolve(5), double); // => 10
52
+ ```
53
+
41
54
  ## `isPromiseLike`
42
55
 
43
- A type guard function to check if a given value is a Promise-like object (i.e., it has a `then` method).
56
+ A type guard function to check if a given value is a Promise-like object (i.e., it has a `then` method). Checks that the value is non-null, is an object or function, and has a `then` property that is a function.
44
57
 
45
58
  ```typescript
46
59
  import { isPromiseLike } from '@venizia/ignis-helpers';
@@ -57,22 +70,9 @@ if (isPromiseLike(b)) {
57
70
  }
58
71
  ```
59
72
 
60
- ## `transformValueOrPromise`
61
-
62
- This function applies a transformation function to a value that might be a direct value or a Promise.
63
-
64
- ```typescript
65
- import { transformValueOrPromise, isPromiseLike } from '@venizia/ignis-helpers';
66
-
67
- const double = (n: number) => n * 2;
68
-
69
- const result1 = await transformValueOrPromise(5, double); // => 10
70
- const result2 = await transformValueOrPromise(Promise.resolve(5), double); // => 10
71
- ```
72
-
73
73
  ## `getDeepProperty`
74
74
 
75
- Safely retrieves a deeply nested property from an object using a dot-separated path string. It throws an error if any part of the path is null or undefined.
75
+ Traverses a dot-separated property path on an object and returns the value. It throws an error if any intermediate part of the path is null or undefined.
76
76
 
77
77
  ```typescript
78
78
  import { getDeepProperty } from '@venizia/ignis-helpers';
@@ -80,4 +80,7 @@ import { getDeepProperty } from '@venizia/ignis-helpers';
80
80
  const obj = { a: { b: { c: 'hello' } } };
81
81
 
82
82
  const value = getDeepProperty(obj, 'a.b.c'); // => 'hello'
83
+
84
+ // Throws: Cannot read property 'x' of undefined
85
+ getDeepProperty(obj, 'a.x.y');
83
86
  ```
@@ -1,6 +1,6 @@
1
1
  # Request Utility
2
2
 
3
- The Request utility provides functions for handling HTTP request data, such as parsing multipart form data.
3
+ The Request utility provides functions for handling HTTP request data, such as parsing multipart form data, and utilities for creating secure Content-Disposition headers.
4
4
 
5
5
  ## `parseMultipartBody`
6
6
 
@@ -9,21 +9,21 @@ The `parseMultipartBody` function is an asynchronous utility for parsing `multip
9
9
  ### `parseMultipartBody(opts)`
10
10
 
11
11
  - `opts` (object):
12
- - `context` (Hono.Context): The Hono context object for the current request.
13
- - `storage` ('memory' | 'disk', optional): The storage strategy for uploaded files. Defaults to `'memory'`.
14
- - `uploadDir` (string, optional): The directory to save files to when using the `'disk'` storage strategy. Defaults to `'./uploads'`.
12
+ - `context` (object with `req` property): The Hono context object for the current request. Uses `context.req.formData()` internally.
13
+ - `storage` (`'memory'` | `'disk'`, optional): The storage strategy for uploaded files. Defaults to `'memory'`.
14
+ - `uploadDir` (string, optional): The directory to save files to when using the `'disk'` storage strategy. Defaults to `'./uploads'`. The directory is created recursively if it does not exist.
15
15
 
16
- The function returns a `Promise` that resolves to an array of `IParsedFile` objects.
16
+ The function returns a `Promise` that resolves to an array of `IParsedFile` objects. String form fields are skipped (only `File` entries are processed).
17
17
 
18
18
  ### `IParsedFile` Interface
19
19
 
20
20
  - `fieldname`: The name of the form field.
21
21
  - `originalname`: The original name of the uploaded file.
22
- - `encoding`: The file's encoding.
22
+ - `encoding`: The file's encoding (always `'utf8'`).
23
23
  - `mimetype`: The MIME type of the file.
24
24
  - `size`: The size of the file in bytes.
25
25
  - `buffer` (Buffer, optional): The file's content as a Buffer (if `storage` is `'memory'`).
26
- - `filename` (string, optional): The name of the file on disk (if `storage` is `'disk'`).
26
+ - `filename` (string, optional): The generated name of the file on disk (if `storage` is `'disk'`). Format: `{timestamp}-{randomString}-{sanitizedOriginalName}`.
27
27
  - `path` (string, optional): The full path to the file on disk (if `storage` is `'disk'`).
28
28
 
29
29
  ### Example
@@ -31,11 +31,11 @@ The function returns a `Promise` that resolves to an array of `IParsedFile` obje
31
31
  Here is an example of how to use `parseMultipartBody` in a controller to handle a file upload.
32
32
 
33
33
  ```typescript
34
- import { BaseController, controller } from '@venizia/ignis';
34
+ import { BaseRestController, controller } from '@venizia/ignis';
35
35
  import { parseMultipartBody, HTTP } from '@venizia/ignis-helpers';
36
36
 
37
37
  @controller({ path: '/files' })
38
- export class FileController extends BaseController {
38
+ export class FileController extends BaseRestController {
39
39
  // ...
40
40
  override binding() {
41
41
  this.defineRoute({
@@ -81,27 +81,38 @@ These utilities help create secure, RFC-compliant `Content-Disposition` headers
81
81
 
82
82
  Creates a safe Content-Disposition header with proper filename encoding for file downloads.
83
83
 
84
- #### `createContentDispositionHeader(filename: string): string`
84
+ #### `createContentDispositionHeader(opts)`
85
85
 
86
- - `filename` (string): The filename to use in the Content-Disposition header.
86
+ - `opts` (object):
87
+ - `filename` (string): The filename to use in the Content-Disposition header.
88
+ - `type` (`'attachment'` | `'inline'`): The disposition type.
87
89
 
88
90
  The function returns a properly formatted `Content-Disposition` header string with both ASCII and UTF-8 encoded filenames for maximum browser compatibility.
89
91
 
90
92
  **Features:**
91
- - Automatic filename sanitization (removes path components and dangerous characters)
92
- - UTF-8 encoding support for international characters
93
+ - Automatic filename sanitization via `sanitizeFilename()`
94
+ - UTF-8 encoding support via `encodeRFC5987()`
93
95
  - RFC 5987 compliant
94
- - Fallback for older browsers
96
+ - Dual `filename` / `filename*` for browser compatibility
95
97
 
96
98
  **Example:**
97
99
 
98
100
  ```typescript
99
101
  import { createContentDispositionHeader } from '@venizia/ignis-helpers';
100
102
 
101
- // In a download endpoint
102
- ctx.header('content-disposition', createContentDispositionHeader('my-document.pdf'));
103
-
103
+ // Attachment (file download)
104
+ ctx.header('content-disposition', createContentDispositionHeader({
105
+ filename: 'my-document.pdf',
106
+ type: 'attachment',
107
+ }));
104
108
  // Output: attachment; filename="my-document.pdf"; filename*=UTF-8''my-document.pdf
109
+
110
+ // Inline (display in browser)
111
+ ctx.header('content-disposition', createContentDispositionHeader({
112
+ filename: 'report.pdf',
113
+ type: 'inline',
114
+ }));
115
+ // Output: inline; filename="report.pdf"; filename*=UTF-8''report.pdf
105
116
  ```
106
117
 
107
118
  ---
@@ -117,13 +128,13 @@ Sanitizes a filename for safe use, removing path components and dangerous charac
117
128
  Returns a safe filename suitable for use in headers or filesystem operations.
118
129
 
119
130
  **Features:**
120
- - Removes path components (prevents directory traversal attacks)
121
- - Allows only alphanumeric characters, spaces, hyphens, underscores, and dots
131
+ - Removes path components via `path.basename()` (prevents directory traversal attacks)
132
+ - Allows only word characters (`\w`), spaces, hyphens, underscores, and dots
122
133
  - Replaces dangerous characters with underscores
123
134
  - Removes leading dots (prevents hidden files)
124
135
  - Replaces consecutive dots with a single dot
125
136
  - Removes ".." patterns (additional path traversal protection)
126
- - Prevents empty filenames and suspicious patterns
137
+ - Returns `'download'` for empty, suspicious, or invalid filenames
127
138
 
128
139
  **Example:**
129
140
 
@@ -134,7 +145,6 @@ sanitizeFilename('../../etc/passwd'); // Returns: 'passwd'
134
145
  sanitizeFilename('my<file>name.txt'); // Returns: 'my_file_name.txt'
135
146
  sanitizeFilename('.hidden'); // Returns: 'hidden'
136
147
  sanitizeFilename('file...txt'); // Returns: 'file.txt'
137
- sanitizeFilename('документ.pdf'); // Returns: '_________.pdf'
138
148
  sanitizeFilename(''); // Returns: 'download'
139
149
  sanitizeFilename('..'); // Returns: 'download'
140
150
  ```
@@ -142,7 +152,7 @@ sanitizeFilename('..'); // Returns: 'download'
142
152
 
143
153
  ### `encodeRFC5987`
144
154
 
145
- Encodes a filename according to RFC 5987 for use in HTTP headers.
155
+ Encodes a filename according to RFC 5987 for use in HTTP headers. Encodes using `encodeURIComponent` and additionally escapes single quotes, parentheses, and asterisks.
146
156
 
147
157
  #### `encodeRFC5987(filename: string): string`
148
158
 
@@ -156,22 +166,31 @@ Returns an RFC 5987 encoded string suitable for the `filename*` parameter in Con
156
166
  import { encodeRFC5987 } from '@venizia/ignis-helpers';
157
167
 
158
168
  encodeRFC5987('my document.pdf'); // Returns: 'my%20document.pdf'
159
- encodeRFC5987('файл.txt'); // Returns: '%D1%84%D0%B0%D0%B9%D0%BB.txt'
160
169
  ```
161
170
 
162
171
 
172
+ ## `IRequestedRemark` Interface
173
+
174
+ The Request utility also exports the `IRequestedRemark` interface, which describes a request remark object:
175
+
176
+ - `id` (string): The request identifier.
177
+ - `url` (string): The request URL.
178
+ - `method` (string): The HTTP method.
179
+ - `[extra: string | symbol]`: Additional arbitrary properties.
180
+
181
+
163
182
  ## Complete File Download Example
164
183
 
165
184
  Here's a complete example combining multipart upload parsing with secure file downloads:
166
185
 
167
186
  ```typescript
168
- import { BaseController, controller } from '@venizia/ignis';
187
+ import { BaseRestController, controller } from '@venizia/ignis';
169
188
  import { parseMultipartBody, createContentDispositionHeader, HTTP } from '@venizia/ignis-helpers';
170
189
  import fs from 'node:fs';
171
190
  import path from 'node:path';
172
191
 
173
192
  @controller({ path: '/files' })
174
- export class FileController extends BaseController {
193
+ export class FileController extends BaseRestController {
175
194
  override binding() {
176
195
  // Upload endpoint
177
196
  this.bindRoute({
@@ -209,7 +228,10 @@ export class FileController extends BaseController {
209
228
  // Set secure headers
210
229
  ctx.header('content-type', 'application/octet-stream');
211
230
  ctx.header('content-length', fileStat.size.toString());
212
- ctx.header('content-disposition', createContentDispositionHeader(filename));
231
+ ctx.header('content-disposition', createContentDispositionHeader({
232
+ filename,
233
+ type: 'attachment',
234
+ }));
213
235
  ctx.header('x-content-type-options', 'nosniff');
214
236
 
215
237
  return new Response(fileStream, {