@venizia/ignis-docs 0.2.0 → 0.2.1-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.
- package/README.md +14 -14
- package/content/best-practices/api-usage-examples.md +39 -19
- package/content/best-practices/architectural-patterns.md +24 -13
- package/content/best-practices/architecture-decisions.md +29 -16
- package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
- package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
- package/content/best-practices/code-style-standards/control-flow.md +33 -1
- package/content/best-practices/code-style-standards/documentation.md +28 -8
- package/content/best-practices/code-style-standards/function-patterns.md +15 -4
- package/content/best-practices/code-style-standards/index.md +7 -2
- package/content/best-practices/code-style-standards/naming-conventions.md +27 -3
- package/content/best-practices/code-style-standards/route-definitions.md +9 -5
- package/content/best-practices/code-style-standards/tooling.md +14 -1
- package/content/best-practices/code-style-standards/type-safety.md +39 -6
- package/content/best-practices/common-pitfalls.md +59 -35
- package/content/best-practices/contribution-workflow.md +8 -4
- package/content/best-practices/data-modeling.md +78 -62
- package/content/best-practices/deployment-strategies.md +56 -19
- package/content/best-practices/error-handling.md +247 -153
- package/content/best-practices/index.md +6 -0
- package/content/best-practices/performance-optimization.md +26 -13
- package/content/best-practices/security-guidelines.md +35 -9
- package/content/best-practices/testing-strategies.md +7 -2
- package/content/best-practices/troubleshooting-tips.md +27 -17
- package/content/extensions/components/api-reference.md +109 -323
- package/content/extensions/components/authentication/api.md +489 -602
- package/content/extensions/components/authentication/errors.md +135 -498
- package/content/extensions/components/authentication/index.md +89 -801
- package/content/extensions/components/authentication/usage.md +231 -955
- package/content/extensions/components/authorization/api.md +991 -644
- package/content/extensions/components/authorization/errors.md +204 -208
- package/content/extensions/components/authorization/getting-started.md +227 -0
- package/content/extensions/components/authorization/index.md +88 -795
- package/content/extensions/components/authorization/usage.md +219 -528
- package/content/extensions/components/health-check.md +77 -243
- package/content/extensions/components/index.md +24 -90
- package/content/extensions/components/mail/api.md +553 -285
- package/content/extensions/components/mail/errors.md +76 -62
- package/content/extensions/components/mail/index.md +111 -463
- package/content/extensions/components/mail/usage.md +139 -173
- package/content/extensions/components/request-tracker.md +70 -173
- package/content/extensions/components/socket-io/api.md +462 -785
- package/content/extensions/components/socket-io/errors.md +49 -51
- package/content/extensions/components/socket-io/index.md +69 -372
- package/content/extensions/components/socket-io/usage.md +212 -105
- package/content/extensions/components/static-asset/api.md +461 -141
- package/content/extensions/components/static-asset/errors.md +121 -53
- package/content/extensions/components/static-asset/index.md +84 -606
- package/content/extensions/components/static-asset/usage.md +184 -299
- package/content/extensions/components/template/index.md +3 -3
- package/content/extensions/components/websocket/api.md +311 -404
- package/content/extensions/components/websocket/errors.md +47 -56
- package/content/extensions/components/websocket/index.md +75 -407
- package/content/extensions/components/websocket/usage.md +120 -338
- package/content/extensions/helpers/cron/index.md +52 -160
- package/content/extensions/helpers/crypto/index.md +67 -480
- package/content/extensions/helpers/crypto/reference.md +528 -0
- package/content/extensions/helpers/env/index.md +64 -178
- package/content/extensions/helpers/error/index.md +286 -196
- package/content/extensions/helpers/index.md +61 -47
- package/content/extensions/helpers/inversion/index.md +76 -550
- package/content/extensions/helpers/inversion/reference.md +530 -0
- package/content/extensions/helpers/kafka/admin.md +23 -3
- package/content/extensions/helpers/kafka/compile-binary.md +83 -52
- package/content/extensions/helpers/kafka/consumer.md +65 -28
- package/content/extensions/helpers/kafka/examples.md +46 -253
- package/content/extensions/helpers/kafka/index.md +55 -617
- package/content/extensions/helpers/kafka/producer.md +156 -22
- package/content/extensions/helpers/kafka/schema-registry.md +59 -79
- package/content/extensions/helpers/logger/hf-logger.md +220 -0
- package/content/extensions/helpers/logger/index.md +78 -552
- package/content/extensions/helpers/logger/pino.md +105 -0
- package/content/extensions/helpers/logger/reference.md +937 -0
- package/content/extensions/helpers/network/api.md +276 -195
- package/content/extensions/helpers/network/index.md +87 -524
- package/content/extensions/helpers/queue/index.md +80 -897
- package/content/extensions/helpers/queue/reference.md +494 -0
- package/content/extensions/helpers/redis/index.md +86 -640
- package/content/extensions/helpers/redis/reference.md +784 -0
- package/content/extensions/helpers/secrets/index.md +136 -0
- package/content/extensions/helpers/socket-io/api.md +325 -204
- package/content/extensions/helpers/socket-io/index.md +79 -429
- package/content/extensions/helpers/storage/api.md +584 -464
- package/content/extensions/helpers/storage/index.md +80 -573
- package/content/extensions/helpers/types/index.md +75 -495
- package/content/extensions/helpers/types/reference.md +689 -0
- package/content/extensions/helpers/uid/index.md +176 -189
- package/content/extensions/helpers/websocket/api.md +366 -214
- package/content/extensions/helpers/websocket/index.md +68 -503
- package/content/extensions/helpers/worker-thread/index.md +62 -396
- package/content/extensions/helpers/worker-thread/reference.md +428 -0
- package/content/extensions/index.md +38 -39
- package/content/extensions/src-details/mcp-server.md +96 -548
- package/content/guides/core-concepts/persistent/datasources.md +2 -2
- package/content/guides/core-concepts/persistent/index.md +5 -1
- package/content/guides/core-concepts/persistent/models.md +1 -1
- package/content/guides/core-concepts/persistent/pglite.md +218 -0
- package/content/guides/core-concepts/persistent/postgres-drivers.md +21 -15
- package/content/guides/core-concepts/persistent/search-meilisearch.md +22 -12
- package/content/guides/core-concepts/persistent/search-typesense.md +28 -16
- package/content/guides/core-concepts/persistent/sqlite.md +296 -0
- package/content/guides/core-concepts/persistent/transactions.md +12 -11
- package/content/guides/core-concepts/secrets-vault.md +177 -0
- package/content/guides/core-concepts/services.md +1 -1
- package/content/guides/get-started/5-minute-quickstart.md +59 -206
- package/content/guides/get-started/philosophy.md +135 -670
- package/content/guides/get-started/setup.md +53 -74
- package/content/guides/migrations/redis-helpers-migration.md +5 -4
- package/content/guides/migrations/scoped-rbac-migration.md +5 -5
- package/content/guides/migrations/unified-connectors-migration.md +8 -7
- package/content/guides/tutorials/ecommerce-api.md +3 -8
- package/content/guides/tutorials/realtime-chat.md +1 -1
- package/content/references/base/application.md +64 -22
- package/content/references/base/components.md +3 -3
- package/content/references/base/connectors.md +79 -136
- package/content/references/base/controllers.md +12 -12
- package/content/references/base/datasources-reference.md +600 -0
- package/content/references/base/datasources.md +84 -444
- package/content/references/base/dependency-injection.md +25 -46
- package/content/references/base/filter-system/application-usage.md +95 -123
- package/content/references/base/filter-system/array-operators.md +33 -43
- package/content/references/base/filter-system/comparison-operators.md +55 -63
- package/content/references/base/filter-system/default-filter.md +144 -350
- package/content/references/base/filter-system/fields-order-pagination.md +116 -148
- package/content/references/base/filter-system/index.md +114 -253
- package/content/references/base/filter-system/json-filtering.md +52 -181
- package/content/references/base/filter-system/list-operators.md +28 -44
- package/content/references/base/filter-system/logical-operators.md +69 -114
- package/content/references/base/filter-system/null-operators.md +41 -98
- package/content/references/base/filter-system/pattern-matching.md +48 -51
- package/content/references/base/filter-system/quick-reference.md +93 -196
- package/content/references/base/filter-system/range-operators.md +22 -38
- package/content/references/base/filter-system/tips.md +70 -133
- package/content/references/base/filter-system/use-cases.md +173 -232
- package/content/references/base/grpc-controllers.md +53 -17
- package/content/references/base/index.md +5 -3
- package/content/references/base/middlewares.md +43 -28
- package/content/references/base/models-reference.md +886 -0
- package/content/references/base/models.md +81 -1452
- package/content/references/base/providers.md +8 -8
- package/content/references/base/repositories/advanced.md +281 -419
- package/content/references/base/repositories/index.md +84 -644
- package/content/references/base/repositories/mixins.md +25 -21
- package/content/references/base/repositories/relations.md +194 -375
- package/content/references/base/repositories/soft-deletable.md +67 -55
- package/content/references/base/secrets.md +267 -0
- package/content/references/base/services.md +8 -6
- package/content/references/configuration/environment-variables.md +73 -21
- package/content/references/configuration/index.md +51 -31
- package/content/references/index.md +1 -1
- package/content/references/quick-reference.md +3 -16
- package/content/references/utilities/crypto.md +35 -76
- package/content/references/utilities/date.md +33 -73
- package/content/references/utilities/duration.md +85 -0
- package/content/references/utilities/index.md +6 -2
- package/content/references/utilities/jsx-reference.md +298 -0
- package/content/references/utilities/jsx.md +82 -525
- package/content/references/utilities/module.md +81 -61
- package/content/references/utilities/parse.md +34 -64
- package/content/references/utilities/performance.md +33 -58
- package/content/references/utilities/promise.md +28 -62
- package/content/references/utilities/request.md +58 -218
- package/content/references/utilities/retry.md +139 -0
- package/content/references/utilities/schema.md +43 -137
- package/content/references/utilities/statuses-reference.md +361 -0
- package/content/references/utilities/statuses.md +63 -667
- package/dist/mcp-server/index.js +0 -0
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.js +3 -3
- package/dist/mcp-server/tools/github/search-code.tool.js +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +1 -1
- package/dist/mcp-server/tools/github/view-source-file.tool.js +1 -1
- package/package.json +24 -23
|
@@ -1,90 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Date Utility
|
|
3
|
+
description: Pre-configured dayjs re-export plus sleep, weekday, timezone, and high-resolution timing helpers
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-07-16
|
|
6
|
+
---
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
# Date Utility
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
A pre-configured `dayjs` re-export, plus small standalone helpers for sleeping, weekday math, timezone conversion, and high-resolution timing.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## In one example
|
|
10
13
|
|
|
11
14
|
```typescript
|
|
12
|
-
import { dayjs } from '@venizia/ignis-helpers';
|
|
13
|
-
|
|
14
|
-
// Get the current date and time
|
|
15
|
-
const now = dayjs();
|
|
16
|
-
|
|
17
|
-
// Format a date
|
|
18
|
-
const formatted = now.format('YYYY-MM-DD HH:mm:ss');
|
|
19
|
-
```
|
|
15
|
+
import { dayjs, sleep, getDateTz } from '@venizia/ignis-helpers';
|
|
20
16
|
|
|
21
|
-
|
|
17
|
+
const now = dayjs().format('YYYY-MM-DD HH:mm:ss');
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
await sleep(2000); // pause for 2 seconds
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
import { sleep } from '@venizia/ignis-helpers';
|
|
27
|
-
|
|
28
|
-
async function myAsyncFunction() {
|
|
29
|
-
console.log('Start');
|
|
30
|
-
await sleep(2000); // Wait for 2 seconds
|
|
31
|
-
console.log('End');
|
|
32
|
-
}
|
|
21
|
+
const tokyoTime = getDateTz({ date: '2023-10-27T10:00:00Z', timezone: 'Asia/Tokyo' });
|
|
33
22
|
```
|
|
34
23
|
|
|
35
|
-
##
|
|
36
|
-
|
|
37
|
-
- **`isWeekday(date)`**: Checks if a given date is a weekday (Monday to Friday). Accepts a `string` or `dayjs.Dayjs` instance.
|
|
38
|
-
- **`getPreviousWeekday(opts?)`**: Returns the previous weekday from a given date. If no date is provided, defaults to today.
|
|
39
|
-
- **`getNextWeekday(opts?)`**: Returns the next weekday from a given date. If no date is provided, defaults to today.
|
|
24
|
+
## Functions
|
|
40
25
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
26
|
+
| Function | Signature | What it does |
|
|
27
|
+
|----------|-----------|---------------|
|
|
28
|
+
| `dayjs` | re-exported `dayjs` object | The `dayjs` factory, pre-loaded with plugins - use it exactly like raw `dayjs`. |
|
|
29
|
+
| `sleep` | `sleep(ms: number): Promise<void>` | Resolves after `ms` milliseconds (`setTimeout` wrapped in a Promise). |
|
|
30
|
+
| `isWeekday` | `isWeekday(date: string \| dayjs.Dayjs): boolean` | `true` when `date` falls Monday through Friday (ISO weekday 1-5). |
|
|
31
|
+
| `getPreviousWeekday` | `getPreviousWeekday(opts?: { date?: string \| dayjs.Dayjs }): dayjs.Dayjs` | Walks backward a day at a time from `date` (default: today) until it lands on a weekday. |
|
|
32
|
+
| `getNextWeekday` | `getNextWeekday(opts?: { date?: string \| dayjs.Dayjs }): dayjs.Dayjs` | Walks forward a day at a time from `date` (default: today) until it lands on a weekday. |
|
|
33
|
+
| `getDateTz` | `getDateTz(opts: { date: string; timezone: string; useClientTz?: boolean; timeOffset?: number }): dayjs.Dayjs` | Parses `date` and converts it to `timezone`, optionally shifting by `timeOffset` hours. |
|
|
34
|
+
| `hrTime` | `hrTime(): number` | High-resolution seconds from `process.hrtime()`, rounded to 9 decimal places - for benchmarking. |
|
|
45
35
|
|
|
46
|
-
|
|
47
|
-
const nextBusinessDay = getNextWeekday({ date: '2026-03-13' });
|
|
48
|
-
```
|
|
36
|
+
## Notes
|
|
49
37
|
|
|
50
|
-
|
|
38
|
+
- **Plugins loaded once at module import:** `CustomParseFormat`, `UTC`, `Timezone`, `Weekday`, `IsoWeek`.
|
|
39
|
+
- **Default timezone is `Asia/Ho_Chi_Minh`**, set via `dayjs.tz.setDefault()` at module load. Override with the `APP_ENV_APPLICATION_TIMEZONE` environment variable - it is read once, so changing it at runtime needs a restart.
|
|
40
|
+
- **`getPreviousWeekday` / `getNextWeekday` are day-by-day loops**, not calendar lookups. They call `isWeekday()` after each step, so the worst case (stepping over a weekend) is only 2-3 iterations.
|
|
41
|
+
- **`getDateTz`'s `useClientTz`** defaults to `false` and controls whether `dayjs().tz()` keeps the original wall-clock time or converts it - see the [Day.js Timezone plugin docs](https://day.js.org/docs/en/timezone/timezone) for the exact semantics.
|
|
51
42
|
|
|
52
|
-
|
|
43
|
+
## See also
|
|
53
44
|
|
|
54
|
-
|
|
45
|
+
- [Utilities Overview](/references/utilities/) - all utility functions
|
|
46
|
+
- [Day.js documentation](https://day.js.org/docs/en/installation/installation) - underlying date library
|
|
55
47
|
|
|
56
|
-
|
|
57
|
-
- `date` (string): The date string to parse.
|
|
58
|
-
- `timezone` (string): The IANA timezone name.
|
|
59
|
-
- `useClientTz` (boolean, optional): Whether to keep the client's timezone. Defaults to `false`.
|
|
60
|
-
- `timeOffset` (number, optional): Number of hours to add to the result. Defaults to `0`.
|
|
48
|
+
**Files:**
|
|
61
49
|
|
|
62
|
-
|
|
63
|
-
import { getDateTz } from '@venizia/ignis-helpers';
|
|
64
|
-
|
|
65
|
-
const tokyoTime = getDateTz({
|
|
66
|
-
date: '2023-10-27T10:00:00Z',
|
|
67
|
-
timezone: 'Asia/Tokyo',
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
// With hour offset
|
|
71
|
-
const offsetTime = getDateTz({
|
|
72
|
-
date: '2023-10-27T10:00:00Z',
|
|
73
|
-
timezone: 'Asia/Tokyo',
|
|
74
|
-
timeOffset: 2, // Add 2 hours
|
|
75
|
-
});
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## `hrTime`
|
|
79
|
-
|
|
80
|
-
The `hrTime` function returns a high-resolution time measurement in seconds using `process.hrtime()`, useful for performance benchmarking. The result is rounded to 9 decimal places.
|
|
81
|
-
|
|
82
|
-
```typescript
|
|
83
|
-
import { hrTime } from '@venizia/ignis-helpers';
|
|
84
|
-
|
|
85
|
-
const start = hrTime();
|
|
86
|
-
// ... some long-running operation
|
|
87
|
-
const end = hrTime();
|
|
88
|
-
|
|
89
|
-
console.log(`Operation took ${end - start} seconds.`);
|
|
90
|
-
```
|
|
50
|
+
- [`packages/helpers/src/utilities/date.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/date.utility.ts)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Duration Utility
|
|
3
|
+
description: A unit vocabulary, written-duration parsing, and conversion between units and milliseconds
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-08-15
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Duration Utility
|
|
9
|
+
|
|
10
|
+
A duration is `{ unit, value }`. This turns that into milliseconds, reads it out of a written string like `30d`, and converts between units.
|
|
11
|
+
|
|
12
|
+
## In one example
|
|
13
|
+
|
|
14
|
+
```typescript
|
|
15
|
+
import { DurationMultipliers, DurationUnits } from '@venizia/ignis-helpers/common';
|
|
16
|
+
|
|
17
|
+
const gracePeriod = { unit: DurationUnits.DAY, value: 30 };
|
|
18
|
+
|
|
19
|
+
DurationMultipliers.toMilliseconds(gracePeriod); // 2_592_000_000
|
|
20
|
+
DurationMultipliers.parseToMilliseconds('30d'); // 2_592_000_000
|
|
21
|
+
DurationMultipliers.convert({ value: 36, from: 'hour', to: 'day' }); // 1.5
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Everything here is browser-pure, so it works unchanged in a Worker. Import from `@venizia/ignis-helpers/common`, or from the root barrel if you already depend on it.
|
|
25
|
+
|
|
26
|
+
## Units
|
|
27
|
+
|
|
28
|
+
| Constant | Value | Milliseconds |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| `DurationUnits.MILLISECOND` | `'millisecond'` | 1 |
|
|
31
|
+
| `DurationUnits.SECOND` | `'second'` | 1 000 |
|
|
32
|
+
| `DurationUnits.MINUTE` | `'minute'` | 60 000 |
|
|
33
|
+
| `DurationUnits.HOUR` | `'hour'` | 3 600 000 |
|
|
34
|
+
| `DurationUnits.DAY` | `'day'` | 86 400 000 |
|
|
35
|
+
| `DurationUnits.WEEK` | `'week'` | 604 800 000 |
|
|
36
|
+
| `DurationUnits.MONTH` | `'month'` | 2 592 000 000 |
|
|
37
|
+
| `DurationUnits.YEAR` | `'year'` | 31 536 000 000 |
|
|
38
|
+
|
|
39
|
+
`TDurationUnit` is the union of those eight values. `IDuration` is `{ unit: TDurationUnit; value: number }`.
|
|
40
|
+
|
|
41
|
+
## Functions
|
|
42
|
+
|
|
43
|
+
| Function | Signature | What it does |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| `DurationUnits.isValid` | `isValid(input: string): input is TDurationUnit` | `true` when `input` is one of the eight canonical names. |
|
|
46
|
+
| `DurationAliases.resolve` | `resolve(input: string): TDurationUnit \| null` | Reads a written unit - `'d'`, `'days'`, `'DAY'` - into its canonical name. |
|
|
47
|
+
| `DurationMultipliers.toMilliseconds` | `toMilliseconds(opts: IDuration \| null): number \| null` | Converts a duration to milliseconds, rounded. |
|
|
48
|
+
| `DurationMultipliers.fromMilliseconds` | `fromMilliseconds(opts: { milliseconds: number; unit: TDurationUnit }): number \| null` | The inverse. Fractional, not rounded. |
|
|
49
|
+
| `DurationMultipliers.convert` | `convert(opts: { value: number; from: TDurationUnit; to: TDurationUnit }): number \| null` | Converts between two units. Fractional. |
|
|
50
|
+
| `DurationMultipliers.parse` | `parse(input: string): IDuration \| null` | Reads `'30d'`, `'1500 ms'`, `'2 hours'` into an `IDuration`. |
|
|
51
|
+
| `DurationMultipliers.parseToMilliseconds` | `parseToMilliseconds(input: string): number \| null` | `parse` followed by `toMilliseconds`. |
|
|
52
|
+
|
|
53
|
+
## A month is 30 days and a year is 365
|
|
54
|
+
|
|
55
|
+
These size a **window** - a grace period, a cache TTL, a near-expiry horizon. They are not calendar arithmetic.
|
|
56
|
+
|
|
57
|
+
Adding `MONTH` to 31 January lands on 2 March in a leap year and 3 March otherwise. If you need a real calendar date, use [the date utility](./date.md) and let `dayjs` handle months.
|
|
58
|
+
|
|
59
|
+
## Every function answers `null`, never throws
|
|
60
|
+
|
|
61
|
+
An unknown unit, a `NaN` value, an unreadable string - all return `null`. A caller that wants an error raises its own, with its own context:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const ttlMs = DurationMultipliers.parseToMilliseconds(config.cacheTtl);
|
|
65
|
+
|
|
66
|
+
if (ttlMs === null) {
|
|
67
|
+
throw getError({ message: `[cache] Unreadable TTL: '${config.cacheTtl}'` });
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Zero is a real duration, not an absent one - `{ unit: 'day', value: 0 }` converts to `0`. A negative value converts too, so a caller deciding what a negative window means keeps that decision.
|
|
72
|
+
|
|
73
|
+
## Writing a duration
|
|
74
|
+
|
|
75
|
+
`parse` accepts one number, an optional space, and one unit: `30d`, `1.5h`, `-2 Hours`, `1500 ms`. Anything it cannot read whole is `null` - there is no partial credit, so `'30d extra'` fails rather than silently meaning 30 days.
|
|
76
|
+
|
|
77
|
+
Accepted spellings are the member names of `DurationAliases`, lower-cased: short (`d`), abbreviated (`wk`), singular (`day`) and plural (`days`).
|
|
78
|
+
|
|
79
|
+
**`m` is minute and `mo` is month.** That is the choice every duration library makes, and the one ambiguity worth checking before you write a config value. There is no single-letter month.
|
|
80
|
+
|
|
81
|
+
## Notes
|
|
82
|
+
|
|
83
|
+
- **The fast path allocates nothing.** `resolve` tries your input as given before normalising it, so a canonical `'day'` costs about 2ns while a messy `' Days '` pays the ~80ns that trimming and upper-casing costs. Pass canonical spellings in a loop.
|
|
84
|
+
- **`toMilliseconds` rounds; `fromMilliseconds` and `convert` do not.** Rounding on the way out would silently lose a 36-hour window asked for in days.
|
|
85
|
+
- **The alias table is derived from `DurationAliases`**, not hand-listed beside it, so a new spelling is one member and the two cannot drift.
|
|
@@ -8,12 +8,14 @@ Pure, standalone functions providing common, reusable logic for the IGNIS framew
|
|
|
8
8
|
|---------|---------|---------|---------------|
|
|
9
9
|
| **Crypto** | `ignis-helpers` | Cryptographic hashing | `hash()` |
|
|
10
10
|
| **Date** | `ignis-helpers` | Date/time manipulation | `dayjs`, `sleep()`, `isWeekday()`, `getDateTz()`, `hrTime()` |
|
|
11
|
+
| **Duration** | `ignis-helpers` | Duration units and conversion | `DurationUnits`, `DurationMultipliers.toMilliseconds()`, `.parseToMilliseconds()` |
|
|
11
12
|
| **JSX** | `ignis` | HTML/JSX responses | `htmlContent()`, `htmlResponse()` |
|
|
12
|
-
| **Module** | `ignis-helpers` |
|
|
13
|
+
| **Module** | `ignis-helpers` | Optional peer loading | `ModuleUtility` |
|
|
13
14
|
| **Parse** | `ignis-helpers` | Data type conversion | `int()`, `float()`, `toBoolean()`, `toCamel()` |
|
|
14
15
|
| **Performance** | `ignis-helpers` | Execution timing | `executeWithPerformanceMeasure()`, `getPerformanceCheckpoint()` |
|
|
15
16
|
| **Promise** | `ignis-helpers` | Promise helpers | `executePromiseWithLimit()`, `isPromiseLike()`, `getDeepProperty()` |
|
|
16
17
|
| **Request** | `ignis-helpers` | HTTP utilities | `parseMultipartBody()`, `sanitizeFilename()`, `createContentDispositionHeader()` |
|
|
18
|
+
| **Retry** | `ignis-helpers` | Backoff-driven retries | `executeWithRetry()`, `executeWithRetryUntil()`, `computeBackoffDelayMs()` |
|
|
17
19
|
| **Schema** | `ignis` | Zod schema helpers | `jsonContent()`, `jsonResponse()`, `requiredString()`, `idParamsSchema()` |
|
|
18
20
|
| **Statuses** | `ignis` | Status code constants | `Statuses`, `CommonStatuses`, `UserStatuses`, `RoleStatuses` |
|
|
19
21
|
|
|
@@ -22,13 +24,14 @@ Pure, standalone functions providing common, reusable logic for the IGNIS framew
|
|
|
22
24
|
### Data Processing
|
|
23
25
|
|
|
24
26
|
- [**Crypto**](./crypto.md) - Simple, stateless cryptographic functions for hashing (SHA256 HMAC, MD5)
|
|
25
|
-
- [**Parse**](./parse.md) - Functions for parsing and converting data types safely (integers, floats, booleans, camelCase,
|
|
27
|
+
- [**Parse**](./parse.md) - Functions for parsing and converting data types safely (integers, floats, booleans, camelCase, array-to-map)
|
|
26
28
|
- [**Schema**](./schema.md) - Helpers for creating Zod schemas for OpenAPI request/response validation
|
|
27
29
|
- [**Statuses**](./statuses.md) - Standardized status code constants for entity lifecycle management
|
|
28
30
|
|
|
29
31
|
### Time & Performance
|
|
30
32
|
|
|
31
33
|
- [**Date**](./date.md) - Date and time manipulation functions built on `dayjs` with timezone support
|
|
34
|
+
- [**Duration**](./duration.md) - A unit vocabulary, written-duration parsing, and conversion between units and milliseconds
|
|
32
35
|
- [**Performance**](./performance.md) - Utilities for measuring code execution time and performance profiling
|
|
33
36
|
|
|
34
37
|
### Async & HTTP
|
|
@@ -36,6 +39,7 @@ Pure, standalone functions providing common, reusable logic for the IGNIS framew
|
|
|
36
39
|
- [**JSX**](./jsx.md) - HTML and JSX response utilities for server-side rendering and OpenAPI documentation
|
|
37
40
|
- [**Promise**](./promise.md) - Helper functions for working with Promises including concurrency limiting and value transformation
|
|
38
41
|
- [**Request**](./request.md) - HTTP request utilities for parsing multipart form data and creating secure Content-Disposition headers
|
|
42
|
+
- [**Retry**](./retry.md) - Backoff-driven retry helpers - error-triggered (`executeWithRetry`) and predicate-driven (`executeWithRetryUntil`)
|
|
39
43
|
|
|
40
44
|
### Runtime
|
|
41
45
|
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: JSX/HTML Utility - Full Reference
|
|
3
|
+
description: Complete reference for the JSX/HTML response helpers, defineJSXRoute, and Hono JSX component patterns
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# JSX/HTML Utility - Full Reference
|
|
8
|
+
|
|
9
|
+
Exhaustive reference for `htmlContent()`, `htmlResponse()`, and `BaseRestController.defineJSXRoute()`. For a readable introduction and the common tasks, start with the [JSX/HTML overview](/references/utilities/jsx).
|
|
10
|
+
|
|
11
|
+
**Files:**
|
|
12
|
+
|
|
13
|
+
- [`packages/core-server/src/utilities/jsx.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/utilities/jsx.utility.ts) - `htmlContent`, `htmlResponse`
|
|
14
|
+
- [`packages/core-server/src/base/controllers/rest/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/controllers/rest/base.ts) - `BaseRestController.defineJSXRoute`
|
|
15
|
+
- [`packages/core-server/src/base/controllers/rest/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/controllers/rest/abstract.ts) - `AbstractRestController.getJSXRouteConfigs`
|
|
16
|
+
- [`packages/helpers/src/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/common/types.ts) - `FC`, `PropsWithChildren`, `Child` (re-exported from `hono/jsx`)
|
|
17
|
+
|
|
18
|
+
## `htmlContent()`
|
|
19
|
+
|
|
20
|
+
Creates a standard OpenAPI content object for `text/html` responses.
|
|
21
|
+
|
|
22
|
+
`Source ->` [`packages/core-server/src/utilities/jsx.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/utilities/jsx.utility.ts)
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
const htmlContent = (opts: { description: string; required?: boolean }) => ({
|
|
26
|
+
description: opts.description,
|
|
27
|
+
content: {
|
|
28
|
+
'text/html': {
|
|
29
|
+
schema: z.string().openapi({
|
|
30
|
+
description: 'HTML content',
|
|
31
|
+
example: '<!DOCTYPE html><html><head><title>Page</title></head><body>...</body></html>',
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
required: opts.required ?? false,
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Parameters
|
|
40
|
+
|
|
41
|
+
| Parameter | Type | Required | Default | Description |
|
|
42
|
+
|-----------|------|----------|---------|-------------|
|
|
43
|
+
| `description` | `string` | Yes | - | Description of the HTML content, shown in the generated OpenAPI document |
|
|
44
|
+
| `required` | `boolean` | No | `false` | Whether the content is required |
|
|
45
|
+
|
|
46
|
+
### Returns
|
|
47
|
+
|
|
48
|
+
An OpenAPI content configuration object: `description`, `content['text/html'].schema` (a `z.string()`), and `required`.
|
|
49
|
+
|
|
50
|
+
## `htmlResponse()`
|
|
51
|
+
|
|
52
|
+
Creates a standard OpenAPI response object for HTML endpoints. It pairs a success (`200`) HTML response with a JSON error response for `4xx | 5xx` status codes using `ErrorSchema`.
|
|
53
|
+
|
|
54
|
+
`Source ->` [`packages/core-server/src/utilities/jsx.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/utilities/jsx.utility.ts)
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
const htmlResponse = (opts: { description: string; required?: boolean }) => ({
|
|
58
|
+
[HTTP.ResultCodes.RS_2.Ok]: htmlContent({ description: opts.description, required: opts.required }),
|
|
59
|
+
['4xx | 5xx']: {
|
|
60
|
+
description: 'Error Response',
|
|
61
|
+
content: { 'application/json': { schema: ErrorSchema } },
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Parameters
|
|
67
|
+
|
|
68
|
+
| Parameter | Type | Required | Default | Description |
|
|
69
|
+
|-----------|------|----------|---------|-------------|
|
|
70
|
+
| `description` | `string` | Yes | - | Description of the successful HTML response |
|
|
71
|
+
| `required` | `boolean` | No | `false` | Whether the content is required |
|
|
72
|
+
|
|
73
|
+
### Returns
|
|
74
|
+
|
|
75
|
+
A responses object: `200` (via `htmlContent()`) plus `4xx | 5xx` (JSON `ErrorSchema`).
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { htmlResponse } from '@venizia/ignis';
|
|
79
|
+
|
|
80
|
+
this.defineRoute({
|
|
81
|
+
configs: {
|
|
82
|
+
path: '/dashboard',
|
|
83
|
+
method: 'get',
|
|
84
|
+
responses: htmlResponse({ description: 'Dashboard HTML page' }),
|
|
85
|
+
},
|
|
86
|
+
handler: c => c.html(<h1>Dashboard</h1>),
|
|
87
|
+
});
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## `BaseRestController.defineJSXRoute()`
|
|
91
|
+
|
|
92
|
+
Defines and registers a JSX/HTML route in a single call - the JSX counterpart of `defineRoute()`.
|
|
93
|
+
|
|
94
|
+
`Source ->` [`packages/core-server/src/base/controllers/rest/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/controllers/rest/base.ts)
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
defineJSXRoute<RouteConfig extends IAuthRouteConfig, ResponseType = unknown>(opts: {
|
|
98
|
+
configs: RouteConfig;
|
|
99
|
+
handler: TRouteHandler<ResponseType, RouteEnv>;
|
|
100
|
+
hook?: Hook<any, RouteEnv, string, ValueOrPromise<any>>;
|
|
101
|
+
}): IDefineRouteOptions<RouteConfig, RouteEnv, RouteSchema, BasePath>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
| Parameter | Type | Required | Description |
|
|
105
|
+
|-----------|------|----------|-------------|
|
|
106
|
+
| `configs` | `RouteConfig` (extends `IAuthRouteConfig`) | Yes | `path`, `method`, `responses`, plus the same `authenticate`/`authorize`/`request`/`middleware` fields `defineRoute` accepts |
|
|
107
|
+
| `handler` | `TRouteHandler<ResponseType, RouteEnv>` | Yes | Receives the route context (`TRouteContext`); return `c.html(<Component />)` |
|
|
108
|
+
| `hook` | `Hook<...>` | No | Same validation hook `defineRoute` accepts |
|
|
109
|
+
|
|
110
|
+
### Behavior
|
|
111
|
+
|
|
112
|
+
`defineJSXRoute` is `defineRoute` with one difference: it builds the route configuration through `getJSXRouteConfigs` instead of `getRouteConfigs`.
|
|
113
|
+
|
|
114
|
+
`Source ->` [`packages/core-server/src/base/controllers/rest/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/src/base/controllers/rest/abstract.ts)
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
getJSXRouteConfigs<RouteConfig extends IAuthRouteConfig>(opts: { configs: RouteConfig }) {
|
|
118
|
+
const { restConfig, security, mws } = this.buildRouteMiddlewares(opts);
|
|
119
|
+
const { responses, tags = [] } = restConfig;
|
|
120
|
+
|
|
121
|
+
return createRoute<string, RouteConfig>(
|
|
122
|
+
Object.assign({}, restConfig, {
|
|
123
|
+
middleware: mws,
|
|
124
|
+
responses: Object.assign({}, htmlResponse({ description: 'HTML page' }), responses),
|
|
125
|
+
tags: [...tags, this.scope],
|
|
126
|
+
security,
|
|
127
|
+
}) as any,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- **Default response merged in first.** `htmlResponse({ description: 'HTML page' })` is the base object. Your own `responses` is merged over it with `Object.assign`, so any status code you declare (typically `200`) overrides the default entry with the same key.
|
|
133
|
+
- **Everything else matches `defineRoute`.** Auth middleware, tags (`this.scope` is always appended), and OpenAPI security are built the same way as JSON routes via `buildRouteMiddlewares`.
|
|
134
|
+
- **Handler contract is unchanged.** The handler still returns whatever `c.html(...)` produces (a `Response`). `defineJSXRoute` only changes how the route's OpenAPI shape is computed, not how the handler runs.
|
|
135
|
+
|
|
136
|
+
## JSX setup
|
|
137
|
+
|
|
138
|
+
### `tsconfig.json`
|
|
139
|
+
|
|
140
|
+
`.tsx` files compile against Hono's JSX runtime, not React's:
|
|
141
|
+
|
|
142
|
+
```json
|
|
143
|
+
{
|
|
144
|
+
"compilerOptions": {
|
|
145
|
+
"jsx": "react-jsx",
|
|
146
|
+
"jsxImportSource": "hono/jsx"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Verified in [`packages/core-server/tsconfig.json`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core-server/tsconfig.json) and the JSX example app's [`examples/rpc-api-server/tsconfig.json`](https://github.com/VENIZIA-AI/ignis/blob/main/examples/rpc-api-server/tsconfig.json).
|
|
152
|
+
|
|
153
|
+
### Component types
|
|
154
|
+
|
|
155
|
+
`FC`, `PropsWithChildren`, and `Child` are re-exported from `@venizia/ignis-helpers` (sourced from `hono/jsx`) - import them from there rather than reaching into `hono/jsx` directly.
|
|
156
|
+
|
|
157
|
+
`Source ->` [`packages/helpers/src/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/common/types.ts)
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
export type { Child, FC, PropsWithChildren } from 'hono/jsx';
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### No separate renderer
|
|
164
|
+
|
|
165
|
+
IGNIS registers no `jsxRenderer` middleware and no template engine. A handler builds a JSX tree and calls Hono's own `c.html()` on it - that call is what triggers rendering to an HTML string.
|
|
166
|
+
|
|
167
|
+
## Component patterns
|
|
168
|
+
|
|
169
|
+
### Layout composition
|
|
170
|
+
|
|
171
|
+
A layout component takes `children` (typed via `PropsWithChildren`) and wraps them in the surrounding document shell. Page components render into a layout the same way any JSX component nests another.
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import type { FC, PropsWithChildren } from '@venizia/ignis-helpers';
|
|
175
|
+
|
|
176
|
+
interface MainLayoutProps {
|
|
177
|
+
title: string;
|
|
178
|
+
description?: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export const MainLayout: FC<PropsWithChildren<MainLayoutProps>> = ({ title, description, children }) => (
|
|
182
|
+
<html lang="en">
|
|
183
|
+
<head>
|
|
184
|
+
<meta charSet="UTF-8" />
|
|
185
|
+
<title>{title}</title>
|
|
186
|
+
{description && <meta name="description" content={description} />}
|
|
187
|
+
</head>
|
|
188
|
+
<body>
|
|
189
|
+
<main>{children}</main>
|
|
190
|
+
</body>
|
|
191
|
+
</html>
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
interface HomePageProps {
|
|
195
|
+
timestamp?: string;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export const HomePage: FC<HomePageProps> = ({ timestamp }) => (
|
|
199
|
+
<MainLayout title="Home" description="Welcome to IGNIS">
|
|
200
|
+
<h1>Welcome to IGNIS!</h1>
|
|
201
|
+
{timestamp && <p>Page rendered at: {timestamp}</p>}
|
|
202
|
+
</MainLayout>
|
|
203
|
+
);
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Wiring pages into a controller
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
import { BaseRestController, controller, htmlContent, type IControllerOptions, type ValueOrPromise } from '@venizia/ignis';
|
|
210
|
+
import { HTTP } from '@venizia/ignis-helpers';
|
|
211
|
+
import { HomePage } from '@/views/pages/home.page';
|
|
212
|
+
|
|
213
|
+
@controller({ path: '/' })
|
|
214
|
+
export class ViewController extends BaseRestController {
|
|
215
|
+
constructor(opts: IControllerOptions) {
|
|
216
|
+
super({ ...opts, scope: ViewController.name, path: '/' });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
override binding(): ValueOrPromise<void> {
|
|
220
|
+
this.defineJSXRoute({
|
|
221
|
+
configs: {
|
|
222
|
+
path: '/',
|
|
223
|
+
method: 'get',
|
|
224
|
+
description: 'Home page rendered with JSX',
|
|
225
|
+
tags: ['Views'],
|
|
226
|
+
responses: {
|
|
227
|
+
[HTTP.ResultCodes.RS_2.Ok]: htmlContent({ description: 'Home page HTML' }),
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
handler: c => {
|
|
231
|
+
const timestamp = new Date().toISOString();
|
|
232
|
+
return c.html(<HomePage timestamp={timestamp} />);
|
|
233
|
+
},
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Raw HTML with `dangerouslySetInnerHTML`
|
|
240
|
+
|
|
241
|
+
Hono JSX's intrinsic elements accept a `dangerouslySetInnerHTML` prop - an object with an `__html` string - matching React's escape hatch. Use it only for HTML you already trust (a stored template, sanitized markdown output) - never for unsanitized user input.
|
|
242
|
+
|
|
243
|
+
```tsx
|
|
244
|
+
async previewTemplate(c: TRouteContext) {
|
|
245
|
+
const { templateId } = c.req.valid<{ templateId: string }>('param');
|
|
246
|
+
const template = await this.emailService.getTemplate(templateId);
|
|
247
|
+
|
|
248
|
+
return c.html(
|
|
249
|
+
<html>
|
|
250
|
+
<head>
|
|
251
|
+
<title>Email Preview: {template.subject}</title>
|
|
252
|
+
</head>
|
|
253
|
+
<body>
|
|
254
|
+
<div dangerouslySetInnerHTML={{ __html: template.html }} />
|
|
255
|
+
</body>
|
|
256
|
+
</html>,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Comparison with JSON utilities
|
|
262
|
+
|
|
263
|
+
### `htmlContent` vs `jsonContent`
|
|
264
|
+
|
|
265
|
+
| Aspect | `htmlContent()` | `jsonContent()` |
|
|
266
|
+
|--------|------------------|-------------------|
|
|
267
|
+
| Content type | `text/html` | `application/json` |
|
|
268
|
+
| Schema | `z.string()` | Caller-supplied Zod schema |
|
|
269
|
+
| Use case | HTML pages, JSX rendering | API responses, structured data |
|
|
270
|
+
|
|
271
|
+
### `htmlResponse` vs `jsonResponse`
|
|
272
|
+
|
|
273
|
+
| Aspect | `htmlResponse()` | `jsonResponse()` |
|
|
274
|
+
|--------|---------------------|----------------------|
|
|
275
|
+
| Success type | `text/html` (`200`) | `application/json` (`200`) |
|
|
276
|
+
| Error type | `application/json` (`4xx \| 5xx`) | `application/json` (`4xx \| 5xx`) |
|
|
277
|
+
| Use case | Server-rendered web pages | REST APIs |
|
|
278
|
+
|
|
279
|
+
See [Schema Utility](./schema.md) for `jsonContent`/`jsonResponse`.
|
|
280
|
+
|
|
281
|
+
## Route definition choices
|
|
282
|
+
|
|
283
|
+
`defineJSXRoute` is the recommended way to register a JSX route because it fills in a default HTML `200` response automatically. Two equivalent alternatives exist when you need more control:
|
|
284
|
+
|
|
285
|
+
| Approach | When to use |
|
|
286
|
+
|----------|-------------|
|
|
287
|
+
| `this.defineJSXRoute({ configs, handler })` | Default choice - HTML response shape is inferred, override `responses[200]` only if you need a custom description |
|
|
288
|
+
| `this.defineRoute({ configs: { responses: htmlResponse({ description }) }, handler })` | You want the full `responses` object built explicitly via `htmlResponse()`, without relying on the JSX default merge |
|
|
289
|
+
| `this.bindRoute({ configs }).to({ handler })` | Fluent two-step registration, same `configs` shape as either of the above |
|
|
290
|
+
|
|
291
|
+
Authentication and authorization on a JSX route use the same `configs.authenticate` / `configs.authorize` fields as JSON routes - see [Controllers](../base/controllers.md).
|
|
292
|
+
|
|
293
|
+
## See also
|
|
294
|
+
|
|
295
|
+
- [JSX/HTML overview](/references/utilities/jsx) - introduction and common tasks
|
|
296
|
+
- [Schema Utility](./schema.md) - `jsonContent`, `jsonResponse`, and the wider response-helper family
|
|
297
|
+
- [Controllers](../base/controllers.md) - `defineRoute`, `bindRoute`, route configuration, authentication
|
|
298
|
+
- **External:** [Hono JSX Documentation](https://hono.dev/docs/guides/jsx)
|