@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,110 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Module Utility
|
|
3
|
+
description: Loads an optional peer dependency with a clear install error, and without letting the bundler see the specifier
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-07-27
|
|
6
|
+
---
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
# Module Utility
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
`ModuleUtility` loads an optional peer dependency. IGNIS components depend on packages the framework does not bundle - `nodemailer` for mail, `node-vault` for secrets. Loading one through `ModuleUtility` buys you two things: a missing package throws an install instruction instead of a cryptic crash, and the specifier stays invisible to `Bun.build`.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
## In one example
|
|
10
13
|
|
|
11
|
-
|
|
14
|
+
This is how the secrets helper reaches `node-vault`:
|
|
12
15
|
|
|
13
16
|
```typescript
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
import { ModuleUtility } from '@venizia/ignis-helpers';
|
|
18
|
+
|
|
19
|
+
// Fails fast with a clear install instruction if the peer is absent.
|
|
20
|
+
ModuleUtility.assertInstalled({
|
|
21
|
+
scope: 'HashiCorpVaultHelper',
|
|
22
|
+
modules: ['node-vault'],
|
|
23
|
+
allowRegistered: true,
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// The specifier is a parameter, so Bun.build never sees it.
|
|
27
|
+
const vault = await ModuleUtility.load({ module: 'node-vault' });
|
|
28
|
+
const client = vault.default({ endpoint, apiVersion: 'v1' });
|
|
18
29
|
```
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
## Methods
|
|
21
32
|
|
|
22
|
-
|
|
|
23
|
-
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
33
|
+
| Method | Signature | What it does |
|
|
34
|
+
|--------|-----------|---------------|
|
|
35
|
+
| `load` | `load<T>(opts: { module: string }): Promise<T>` | Imports the module. Use this everywhere except a constructor |
|
|
36
|
+
| `loadSync` | `loadSync<T>(opts: { module: string }): T` | Same, without awaiting - for a constructor or any path that cannot be async |
|
|
37
|
+
| `assertInstalled` | `assertInstalled(opts: { modules: Array<string>; scope?: string; allowRegistered?: boolean }): void` | Presence check only. Resolves each module in order and throws on the first miss, without executing any of them |
|
|
38
|
+
| `register` | `register(opts: { modules: Record<string, AnyType> }): void` | Hands the framework peers the application already holds. `load` and `loadSync` serve a registered specifier from memory, with no filesystem lookup |
|
|
26
39
|
|
|
27
|
-
###
|
|
40
|
+
### `allowRegistered`
|
|
28
41
|
|
|
29
|
-
|
|
42
|
+
`assertInstalled` reports what is on disk. Set `allowRegistered` to count a `register` entry as present too.
|
|
30
43
|
|
|
31
|
-
|
|
32
|
-
[validateModule] <module> is required for <scope>. Please install '<module>'
|
|
33
|
-
```
|
|
44
|
+
Set it only where `load` or `loadSync` is what finally loads the module. A caller that resolves the specifier itself cannot see the registry - `pino.transport()` resolves inside a worker thread, and the gRPC adapter uses its own `createRequire`. For those, counting a registration as installed would replace a precise error with an opaque one.
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
## Error message format
|
|
36
47
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
| `scope` | Message |
|
|
49
|
+
|---------|---------|
|
|
50
|
+
| provided | `[ModuleUtility.<method>] <module> is required for <scope>. Please install '<module>'` |
|
|
51
|
+
| omitted | `[ModuleUtility.<method>] <module> is required. Please install '<module>'` |
|
|
40
52
|
|
|
41
|
-
|
|
53
|
+
## Why not a plain import
|
|
42
54
|
|
|
43
|
-
|
|
55
|
+
`Bun.build` resolves a literal specifier at bundle time. A literal `require('mailgun.js')` anywhere reachable from an entry point forces every consumer who compiles a binary to install that peer or list it in `external` - even a consumer who never touches mail.
|
|
44
56
|
|
|
45
|
-
|
|
46
|
-
import { validateModule } from '@venizia/ignis-helpers';
|
|
57
|
+
A `const` does not help. `minify: { syntax: true }` folds `const s = 'mailgun.js'; import(s)` straight back into a resolvable literal.
|
|
47
58
|
|
|
48
|
-
|
|
59
|
+
Only a specifier that crosses a function boundary survives as a runtime import. That is the whole reason `ModuleUtility` takes the module name as a parameter.
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
async configure() {
|
|
52
|
-
// Fails fast with a clear install instruction if the peer dep is absent
|
|
53
|
-
await validateModule({ scope: MyGrpcController.name, modules: GRPC_MODULES });
|
|
61
|
+
## Compiled binaries
|
|
54
62
|
|
|
55
|
-
|
|
56
|
-
// ... register routes
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
```
|
|
63
|
+
Runtime resolution needs a `node_modules` to resolve against. A `bun build --compile` binary usually runs without one - the deployment ships the executable and nothing else - so a peer the application genuinely installed is still unreachable at runtime, and the component that needs it dies at boot with the install hint.
|
|
60
64
|
|
|
61
|
-
|
|
65
|
+
**Check the component's options first.** Every component that reaches a peer this way takes it through its own options:
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
| Component | Option | You pass |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| Mail transports | `module` | the `nodemailer` or `mailgun.js` module |
|
|
70
|
+
| gRPC component | `module` | `{ connect, protocol }` from `@connectrpc/connect` |
|
|
71
|
+
| `HashiCorpVaultHelper` | `client` | an already-built vault client |
|
|
72
|
+
| `DotenvVaultHelper` | `decode` | your own decode function |
|
|
73
|
+
|
|
74
|
+
Prefer the option. It is typed, it lands where it is used, and no ordering can defeat it.
|
|
75
|
+
|
|
76
|
+
`register` is the fallback for a peer the framework reaches with no options seam in between. The static import is what pulls the library into the binary; `register` is what lets the framework find it there:
|
|
64
77
|
|
|
65
78
|
```typescript
|
|
66
|
-
import {
|
|
67
|
-
import
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
await validateModule({
|
|
72
|
-
scope: KafkaQueueHelper.name,
|
|
73
|
-
modules: ['kafkajs', 'kafkajs-snappy'],
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const { Kafka } = await import('kafkajs');
|
|
77
|
-
// ... initialise Kafka client
|
|
78
|
-
}
|
|
79
|
-
}
|
|
79
|
+
import { ModuleUtility } from '@venizia/ignis-helpers';
|
|
80
|
+
import * as vault from 'node-vault';
|
|
81
|
+
|
|
82
|
+
// At the entrypoint, before anything that reaches the peer runs.
|
|
83
|
+
ModuleUtility.register({ modules: { 'node-vault': vault } });
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
The registry is keyed by specifier and the value is returned as-is: what you register under `node-vault` is exactly what `load({ module: 'node-vault' })` hands the caller. `import * as` gives the right shape for a CommonJS peer.
|
|
87
|
+
|
|
88
|
+
Register before the consumer runs. Nothing enforces that ordering, which is the reason to prefer an options seam wherever one exists.
|
|
89
|
+
|
|
90
|
+
`register` cannot help a consumer that resolves the specifier itself. A `pino` transport target loads inside a worker thread the registry never reaches, so `pino-pretty` and `pino-roll` must be installed on disk.
|
|
91
|
+
|
|
92
|
+
Registration is only worth it for the compiled-binary case. An application running from source resolves its peers from `node_modules` already.
|
|
93
|
+
|
|
94
|
+
## Notes
|
|
95
|
+
|
|
96
|
+
- **Resolution is rooted at `process.cwd()/node_modules`** via Node's `createRequire`, so peers installed in the consuming application resolve even though this utility ships inside `packages/helpers/dist/`.
|
|
97
|
+
- **`assertInstalled` stops at the first miss.** Later entries are never checked.
|
|
98
|
+
- **`assertInstalled` never executes the module** - it only locates the file. Reach for it when you want to fail at startup rather than on first use.
|
|
99
|
+
- **Call it once, at startup, not per request.** Place it in an initialisation hook (`configure`, `binding`, `boot`).
|
|
100
|
+
- **`register` wins over the filesystem.** `load` and `loadSync` check the registry first, so a registered peer is never resolved. `assertInstalled` ignores the registry unless you set `allowRegistered`.
|
|
101
|
+
- **`register` never logs.** It runs at the entrypoint of a compiled binary, which is exactly where no logger provider exists yet.
|
|
102
|
+
- **Recommended pattern:** declare the dependency in `peerDependenciesMeta` with `optional: true`, then load it through `ModuleUtility` and pass the feature or class name as `scope` so the thrown message pinpoints the caller.
|
|
103
|
+
|
|
104
|
+
## See also
|
|
83
105
|
|
|
84
|
-
|
|
106
|
+
- [Utilities Overview](/references/utilities/) - all utility functions
|
|
85
107
|
|
|
86
|
-
|
|
87
|
-
2. Call `validateModule` at the top of the method that needs the package - before any `import()`.
|
|
88
|
-
3. Pass the feature or class name as `scope` so the error message pinpoints which feature triggered the check.
|
|
108
|
+
**Files:**
|
|
89
109
|
|
|
90
|
-
|
|
110
|
+
- [`packages/helpers/src/utilities/module.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/module.utility.ts)
|
|
@@ -1,85 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Parse Utility
|
|
3
|
+
description: Type checking, safe numeric and boolean conversion, camelCase transforms, and array-to-map helpers
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-07-16
|
|
6
|
+
---
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
- **`isInt(n)`**: Checks if a value is an integer.
|
|
8
|
-
- **`isFloat(n)`**: Checks if a value is a float.
|
|
8
|
+
# Parse Utility
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
A collection of standalone functions for type checking, safe type conversion, and string/object/array transformation - the small helpers repositories and controllers reach for when normalizing loosely typed input.
|
|
11
11
|
|
|
12
|
-
|
|
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.
|
|
12
|
+
## In one example
|
|
15
13
|
|
|
16
14
|
```typescript
|
|
17
|
-
import { int, float, toBoolean } from '@venizia/ignis-helpers';
|
|
15
|
+
import { int, float, toBoolean, toCamel, keysToCamel } from '@venizia/ignis-helpers';
|
|
18
16
|
|
|
19
17
|
const myInt = int('1,000'); // => 1000
|
|
20
18
|
const myFloat = float('1,234.567', 2); // => 1234.57
|
|
21
19
|
const myBool = toBoolean('true'); // => true
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## String and Object Transformation
|
|
25
|
-
|
|
26
|
-
- **`toCamel(s)`**: Converts a string from snake_case or kebab-case to camelCase.
|
|
27
|
-
- **`keysToCamel(object)`**: Recursively converts all keys in an object (and nested objects) to camelCase.
|
|
28
|
-
|
|
29
|
-
```typescript
|
|
30
|
-
import { toCamel, keysToCamel } from '@venizia/ignis-helpers';
|
|
31
|
-
|
|
32
|
-
const camelString = toCamel('my-snake_case-string');
|
|
33
|
-
// => 'mySnakeCaseString'
|
|
34
20
|
|
|
35
21
|
const camelObject = keysToCamel({ 'first-name': 'John', 'last_name': 'Doe' });
|
|
36
22
|
// => { firstName: 'John', lastName: 'Doe' }
|
|
37
23
|
```
|
|
38
24
|
|
|
39
|
-
##
|
|
25
|
+
## Functions
|
|
40
26
|
|
|
27
|
+
| Function | Signature | What it does |
|
|
28
|
+
|----------|-----------|---------------|
|
|
29
|
+
| `isInt` | `isInt(n: any): boolean` | `true` when `n` is (or coerces to) an integer. |
|
|
30
|
+
| `isFloat` | `isFloat(input: any): boolean` | `true` when `input` is (or coerces to) a non-integer number. |
|
|
31
|
+
| `int` | `int(input: any): number` | Parses `input` to an integer. Strips commas first; returns `0` for empty/invalid input. |
|
|
32
|
+
| `float` | `float(input: any, digit = 2): number` | Parses `input` to a float, rounded to `digit` places (via lodash `round`). Strips commas first; returns `0` for empty/invalid input. |
|
|
33
|
+
| `toBoolean` | `toBoolean(input: any): boolean` | `false` for `''`, `'false'`, `'0'`, `false`, `0`, `null`, `undefined`; `true` for everything else. |
|
|
34
|
+
| `toCamel` | `toCamel(s: string): string` | Converts a `snake_case` or `kebab-case` string to `camelCase`. |
|
|
35
|
+
| `keysToCamel` | `keysToCamel(object: object): any` | Recursively camelizes every key in `object`. Arrays stay arrays (their object elements are still camelized); `Date` values pass through untouched. |
|
|
36
|
+
| `parseArrayToMapWithKey` | `parseArrayToMapWithKey<T, K>(arr: T[], keyMap: K): Map<T[K], T>` | Turns an array of objects into a `Map` keyed by `keyMap`. Positional arguments, not an options object. Throws if `keyMap` is missing from an element; last element wins on duplicate keys. |
|
|
37
|
+
| `toDelimitedArray` | `toDelimitedArray(input: unknown, separator = ','): string[]` | Splits `input` on `separator` into trimmed, non-empty entries. `null`/`undefined` input returns `[]`. |
|
|
38
|
+
| `toTrimmed` | `toTrimmed(input: unknown): string` | Stringifies and trims `input`; `null`/`undefined` returns `''`. |
|
|
39
|
+
| `getUID` | `getUID(): string` | Generates a short, uppercase, `Math.random()`-based ID - not cryptographically unique. |
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// US format (default) - comma is thousands separator
|
|
41
|
+
## Notes
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
- **`int`/`float` are comma-tolerant.** Both strip `,` before parsing, so `'1,000'` and `1000` behave the same - useful for user-typed numeric input.
|
|
44
|
+
- **`parseArrayToMapWithKey` breaks the options-object convention on purpose** - it takes `(arr, keyMap)` positionally, unlike every other function on this page.
|
|
45
|
+
- **`toDelimitedArray` and `toTrimmed` are the transform functions for list-shaped and string env values** - typical use is `applicationEnvironment.get(KEY, { transform: toDelimitedArray })`.
|
|
46
|
+
- **`getUID` is for short, human-glanceable identifiers**, not for anything that needs global uniqueness guarantees - use the [UID helper](/extensions/helpers/) (Snowflake-based) for that.
|
|
48
47
|
|
|
49
|
-
|
|
50
|
-
|--------|------|---------|-------------|
|
|
51
|
-
| `method` | `'int' \| 'float'` | `'int'` | Parse as integer or float |
|
|
52
|
-
| `locale` | `'us' \| 'eu'` | `'us'` | Number format locale |
|
|
48
|
+
## See also
|
|
53
49
|
|
|
54
|
-
|
|
50
|
+
- [Utilities Overview](/references/utilities/) - all utility functions
|
|
51
|
+
- [Schema Utility](/references/utilities/schema) - `snakeToCamel` for Zod schemas built on `toCamel`/`keysToCamel`
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
**Files:**
|
|
57
54
|
|
|
58
|
-
|
|
59
|
-
import { parseArrayToMapWithKey } from '@venizia/ignis-helpers';
|
|
60
|
-
|
|
61
|
-
const users = [
|
|
62
|
-
{ id: 1, name: 'Alice' },
|
|
63
|
-
{ id: 2, name: 'Bob' },
|
|
64
|
-
];
|
|
65
|
-
|
|
66
|
-
// Record (options object pattern)
|
|
67
|
-
// => { 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } }
|
|
68
|
-
|
|
69
|
-
// Map (positional arguments)
|
|
70
|
-
const usersMap = parseArrayToMapWithKey(users, 'id');
|
|
71
|
-
// => Map { 1 => { id: 1, name: 'Alice' }, 2 => { id: 2, name: 'Bob' } }
|
|
72
|
-
|
|
73
|
-
const user = usersMap.get(1);
|
|
74
|
-
// => { id: 1, name: 'Alice' }
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
## Unique ID
|
|
78
|
-
|
|
79
|
-
- **`getUID()`**: Generates a simple, short unique ID string based on `Math.random()`, returned in uppercase.
|
|
80
|
-
|
|
81
|
-
```typescript
|
|
82
|
-
import { getUID } from '@venizia/ignis-helpers';
|
|
83
|
-
|
|
84
|
-
const uniqueId = getUID(); // => e.g., 'A1B2C3D4'
|
|
85
|
-
```
|
|
55
|
+
- [`packages/helpers/src/utilities/parse.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/parse.utility.ts)
|
|
@@ -1,79 +1,54 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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.
|
|
1
|
+
---
|
|
2
|
+
title: Performance Utility
|
|
3
|
+
description: Checkpoint-based timing and a logging wrapper for measuring execution duration
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-07-16
|
|
6
|
+
---
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
- `opts` (object):
|
|
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'`.
|
|
15
|
-
- `scope` (string): A scope to identify the context of the measurement.
|
|
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).
|
|
8
|
+
# Performance Utility
|
|
18
9
|
|
|
19
|
-
|
|
10
|
+
Functions for measuring and logging how long a code block takes to run, built on `performance.now()` (millisecond resolution). `BaseApplication` uses `executeWithPerformanceMeasure` internally to time component, controller, and datasource registration during startup.
|
|
20
11
|
|
|
21
|
-
|
|
12
|
+
## In one example
|
|
22
13
|
|
|
23
14
|
```typescript
|
|
24
|
-
// Inside BaseApplication class
|
|
25
15
|
import { executeWithPerformanceMeasure } from '@venizia/ignis-helpers';
|
|
26
16
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// ... logic to register components
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
}
|
|
17
|
+
await executeWithPerformanceMeasure({
|
|
18
|
+
logger: this.logger,
|
|
19
|
+
scope: this.registerComponents.name,
|
|
20
|
+
description: 'Register application components',
|
|
21
|
+
task: async () => {
|
|
22
|
+
// ... logic to register components
|
|
23
|
+
},
|
|
24
|
+
});
|
|
39
25
|
```
|
|
40
26
|
|
|
41
|
-
When `registerComponents` is called, it will produce log output similar to this:
|
|
42
|
-
|
|
43
27
|
```
|
|
44
28
|
[RegisterComponents] START | Register application components ...
|
|
45
29
|
[RegisterComponents] DONE | Register application components | Took: 12.3456 (ms)
|
|
46
30
|
```
|
|
47
31
|
|
|
48
|
-
|
|
32
|
+
## Functions
|
|
49
33
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
34
|
+
| Function | Signature | What it does |
|
|
35
|
+
|----------|-----------|---------------|
|
|
36
|
+
| `executeWithPerformanceMeasure` | `executeWithPerformanceMeasure<R>(opts: { logger?: ILogger; level?: string; description?: string; args?: any; scope: string; task: Function }): Promise<R>` | Runs `task` (sync or async), logging a `START` line before and a `DONE` line after with the elapsed time. Resolves to whatever `task` returns. |
|
|
37
|
+
| `getPerformanceCheckpoint` | `getPerformanceCheckpoint(): number` | Returns `performance.now()` - a starting timestamp to pass to `getExecutedPerformance`. |
|
|
38
|
+
| `getExecutedPerformance` | `getExecutedPerformance(opts: { from: number; digit?: number }): number` | Elapsed milliseconds since `from`, rounded to `digit` places (default `6`). |
|
|
54
39
|
|
|
55
|
-
##
|
|
40
|
+
## Notes
|
|
56
41
|
|
|
57
|
-
|
|
42
|
+
- **Defaults:** `logger` falls back to `console`, `level` to `'debug'`, `description` to `'Executing'`.
|
|
43
|
+
- **`args`, when provided, are logged as JSON (`%j`)** on both the `START` and `DONE` lines - handy for correlating a specific call's timing.
|
|
44
|
+
- **`task` is wrapped in `Promise.resolve()`**, so a plain synchronous function works exactly like an `async` one.
|
|
45
|
+
- **Prefer the checkpoint pair for granular, multi-point measurements** inside a single function, where wrapping the whole thing in `executeWithPerformanceMeasure` would not isolate the segment you care about.
|
|
58
46
|
|
|
59
|
-
|
|
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`.
|
|
47
|
+
## See also
|
|
63
48
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
```typescript
|
|
67
|
-
import { getPerformanceCheckpoint, getExecutedPerformance } from '@venizia/ignis-helpers';
|
|
49
|
+
- [Utilities Overview](/references/utilities/) - all utility functions
|
|
50
|
+
- [Logger](/extensions/helpers/logger/) - the `ILogger` type accepted by `executeWithPerformanceMeasure`
|
|
68
51
|
|
|
69
|
-
|
|
52
|
+
**Files:**
|
|
70
53
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const duration = getExecutedPerformance({ from: start });
|
|
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.`);
|
|
79
|
-
```
|
|
54
|
+
- [`packages/helpers/src/utilities/performance.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/performance.utility.ts)
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Promise Utility
|
|
3
|
+
description: Concurrency-limited task execution plus small Promise and object-path helpers
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
lastUpdated: 2026-07-16
|
|
6
|
+
---
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
### `executePromiseWithLimit(opts)`
|
|
8
|
+
# Promise Utility
|
|
10
9
|
|
|
11
|
-
-
|
|
12
|
-
- `tasks` (Array<() => Promise<T>>): An array of functions that each return a Promise.
|
|
13
|
-
- `limit` (number): The maximum number of promises to execute in parallel.
|
|
14
|
-
- `onTaskDone` (<R>(opts: { result: R }) => ValueOrPromise<void>, 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).
|
|
10
|
+
Helper functions for working with Promises - bounding concurrency, normalizing thrown values, type-guarding thenables, reading nested object paths, and firing-and-forgetting async work safely.
|
|
15
11
|
|
|
16
|
-
|
|
12
|
+
## In one example
|
|
17
13
|
|
|
18
14
|
```typescript
|
|
19
15
|
import { executePromiseWithLimit, sleep } from '@venizia/ignis-helpers';
|
|
@@ -22,67 +18,37 @@ const tasks = [
|
|
|
22
18
|
() => sleep(1000).then(() => 'Task 1 done'),
|
|
23
19
|
() => sleep(500).then(() => 'Task 2 done'),
|
|
24
20
|
() => sleep(1200).then(() => 'Task 3 done'),
|
|
25
|
-
() => sleep(800).then(() => 'Task 4 done'),
|
|
26
21
|
];
|
|
27
22
|
|
|
28
|
-
console.log('Starting tasks with a limit of 2...');
|
|
29
|
-
|
|
30
23
|
const results = await executePromiseWithLimit({
|
|
31
24
|
tasks,
|
|
32
25
|
limit: 2,
|
|
33
|
-
onTaskDone: ({ result }) =>
|
|
34
|
-
console.log('A task finished:', result);
|
|
35
|
-
},
|
|
26
|
+
onTaskDone: ({ result }) => console.log('A task finished:', result),
|
|
36
27
|
});
|
|
37
|
-
|
|
38
|
-
console.log('All tasks finished:', results);
|
|
39
28
|
```
|
|
40
29
|
|
|
41
|
-
##
|
|
30
|
+
## Functions
|
|
42
31
|
|
|
43
|
-
|
|
32
|
+
| Function | Signature | What it does |
|
|
33
|
+
|----------|-----------|---------------|
|
|
34
|
+
| `executePromiseWithLimit` | `executePromiseWithLimit<T>(opts: { tasks: Array<() => Promise<T>>; limit: number; onTaskDone?: <R>(opts: { result: R }) => ValueOrPromise<void> }): Promise<T[]>` | Runs `tasks` with at most `limit` running concurrently. Calls `onTaskDone` each time the limit is hit and a slot frees up. Throws if `limit` is not a positive integer. |
|
|
35
|
+
| `toError` | `toError(error: unknown): Error` | Normalizes a `catch`-block value into an `Error` - passes an existing `Error` through, wraps anything else with `new Error(String(error))`. |
|
|
36
|
+
| `isPromiseLike` | `isPromiseLike<T>(value: T \| PromiseLike<T>): value is PromiseLike<T>` | Type guard: `true` when `value` is non-null and has a callable `.then`. |
|
|
37
|
+
| `getDeepProperty` | `getDeepProperty<T, V>(obj: T, path: string): V` | Reads a dot-separated `path` off `obj`. Throws if any intermediate segment is `null`/`undefined`. |
|
|
38
|
+
| `voidExecution` | `voidExecution(opts: { logger?: ILogger; scope: string; execution: ValueOrPromise<unknown> }): void` | Fire-and-forget: if `execution` is a Promise, routes a rejection to `logger.for(scope).error(...)` (or `console.error` without a logger) instead of an unhandled rejection. Synchronous values pass through untouched. |
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
import { toError } from '@venizia/ignis-helpers';
|
|
40
|
+
## Notes
|
|
47
41
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
console.error(error.message);
|
|
53
|
-
}
|
|
54
|
-
```
|
|
42
|
+
- **`executePromiseWithLimit` validates `limit` eagerly** - a non-integer or a value below `1` throws immediately, before any task runs.
|
|
43
|
+
- **`onTaskDone` only fires under backpressure.** It runs when the number of in-flight tasks reaches `limit` and `Promise.race` resolves one of them - not after every task.
|
|
44
|
+
- **`voidExecution` exists because `BaseHelper` cannot carry `protected` members** on classes the container instantiates anonymously (TS4094) - it is a standalone function, not a helper method, precisely so factory-built controllers can still use it.
|
|
45
|
+
- **`getDeepProperty` throws, it does not return `undefined`**, on a missing intermediate segment - wrap the call if you need optional-path semantics instead.
|
|
55
46
|
|
|
56
|
-
##
|
|
47
|
+
## See also
|
|
57
48
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import { isPromiseLike } from '@venizia/ignis-helpers';
|
|
49
|
+
- [Utilities Overview](/references/utilities/) - all utility functions
|
|
50
|
+
- [Date Utility](/references/utilities/date) - `sleep()`, used to build the tasks in the example above
|
|
62
51
|
|
|
63
|
-
|
|
64
|
-
const b = 2;
|
|
52
|
+
**Files:**
|
|
65
53
|
|
|
66
|
-
|
|
67
|
-
// This will run
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
if (isPromiseLike(b)) {
|
|
71
|
-
// This will not run
|
|
72
|
-
}
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## `getDeepProperty`
|
|
76
|
-
|
|
77
|
-
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.
|
|
78
|
-
|
|
79
|
-
```typescript
|
|
80
|
-
import { getDeepProperty } from '@venizia/ignis-helpers';
|
|
81
|
-
|
|
82
|
-
const obj = { a: { b: { c: 'hello' } } };
|
|
83
|
-
|
|
84
|
-
const value = getDeepProperty(obj, 'a.b.c'); // => 'hello'
|
|
85
|
-
|
|
86
|
-
// Throws: Cannot read property 'x' of undefined
|
|
87
|
-
getDeepProperty(obj, 'a.x.y');
|
|
88
|
-
```
|
|
54
|
+
- [`packages/helpers/src/utilities/promise.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/helpers/src/utilities/promise.utility.ts)
|