@venizia/ignis-docs 0.0.8-3 → 0.1.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.
- package/README.md +7 -7
- package/{wiki → content}/best-practices/api-usage-examples.md +15 -12
- package/{wiki → content}/best-practices/architectural-patterns.md +70 -78
- package/{wiki → content}/best-practices/architecture-decisions.md +91 -60
- package/{wiki → content}/best-practices/code-style-standards/advanced-patterns.md +56 -44
- package/{wiki → content}/best-practices/code-style-standards/constants-configuration.md +11 -11
- package/{wiki → content}/best-practices/code-style-standards/control-flow.md +5 -2
- package/{wiki → content}/best-practices/code-style-standards/documentation.md +13 -13
- package/{wiki → content}/best-practices/code-style-standards/function-patterns.md +9 -10
- package/{wiki → content}/best-practices/code-style-standards/index.md +1 -1
- package/{wiki → content}/best-practices/code-style-standards/naming-conventions.md +10 -8
- package/{wiki → content}/best-practices/code-style-standards/route-definitions.md +30 -12
- package/{wiki → content}/best-practices/code-style-standards/tooling.md +8 -5
- package/{wiki → content}/best-practices/code-style-standards/type-safety.md +13 -12
- package/{wiki → content}/best-practices/common-pitfalls.md +56 -37
- package/{wiki → content}/best-practices/contribution-workflow.md +13 -14
- package/{wiki → content}/best-practices/data-modeling.md +44 -20
- package/{wiki → content}/best-practices/deployment-strategies.md +28 -27
- package/{wiki → content}/best-practices/error-handling.md +48 -24
- package/{wiki → content}/best-practices/index.md +5 -5
- package/{wiki → content}/best-practices/performance-optimization.md +36 -28
- package/{wiki → content}/best-practices/security-guidelines.md +52 -23
- package/{wiki → content}/best-practices/testing-strategies.md +65 -51
- package/{wiki → content}/best-practices/troubleshooting-tips.md +24 -24
- package/{wiki/extensions/components/swagger.md → content/extensions/components/api-reference.md} +40 -31
- package/{wiki → content}/extensions/components/authentication/api.md +19 -19
- package/{wiki → content}/extensions/components/authentication/errors.md +7 -7
- package/{wiki → content}/extensions/components/authentication/index.md +10 -8
- package/{wiki → content}/extensions/components/authentication/usage.md +101 -6
- package/{wiki → content}/extensions/components/authorization/api.md +45 -25
- package/{wiki → content}/extensions/components/authorization/errors.md +6 -6
- package/{wiki → content}/extensions/components/authorization/index.md +11 -10
- package/{wiki → content}/extensions/components/authorization/usage.md +21 -21
- package/{wiki → content}/extensions/components/health-check.md +1 -1
- package/{wiki → content}/extensions/components/index.md +5 -5
- package/{wiki → content}/extensions/components/mail/errors.md +15 -15
- package/{wiki → content}/extensions/components/mail/index.md +1 -2
- package/{wiki → content}/extensions/components/mail/usage.md +1 -1
- package/{wiki → content}/extensions/components/request-tracker.md +1 -1
- package/{wiki → content}/extensions/components/socket-io/api.md +9 -9
- package/{wiki → content}/extensions/components/socket-io/errors.md +5 -5
- package/{wiki → content}/extensions/components/socket-io/index.md +8 -8
- package/{wiki → content}/extensions/components/socket-io/usage.md +1 -1
- package/{wiki → content}/extensions/components/static-asset/api.md +17 -4
- package/{wiki → content}/extensions/components/static-asset/errors.md +4 -4
- package/{wiki → content}/extensions/components/static-asset/index.md +26 -28
- package/{wiki → content}/extensions/components/static-asset/usage.md +13 -12
- package/{wiki → content}/extensions/components/template/index.md +2 -2
- package/{wiki → content}/extensions/components/template/setup-page.md +1 -1
- package/{wiki → content}/extensions/components/websocket/api.md +3 -3
- package/{wiki → content}/extensions/components/websocket/errors.md +5 -5
- package/{wiki → content}/extensions/components/websocket/index.md +5 -5
- package/{wiki → content}/extensions/components/websocket/usage.md +3 -3
- package/{wiki → content}/extensions/helpers/cron/index.md +2 -2
- package/{wiki → content}/extensions/helpers/crypto/index.md +1 -1
- package/{wiki → content}/extensions/helpers/env/index.md +27 -12
- package/content/extensions/helpers/error/index.md +283 -0
- package/{wiki → content}/extensions/helpers/index.md +2 -3
- package/{wiki → content}/extensions/helpers/inversion/index.md +15 -7
- package/{wiki → content}/extensions/helpers/kafka/examples.md +1 -1
- package/{wiki → content}/extensions/helpers/logger/index.md +32 -2
- package/{wiki → content}/extensions/helpers/network/index.md +6 -0
- package/{wiki → content}/extensions/helpers/queue/index.md +14 -17
- package/content/extensions/helpers/redis/index.md +713 -0
- package/{wiki → content}/extensions/helpers/socket-io/index.md +14 -10
- package/{wiki → content}/extensions/helpers/storage/api.md +44 -8
- package/{wiki → content}/extensions/helpers/storage/index.md +43 -7
- package/{wiki → content}/extensions/helpers/template/index.md +6 -3
- package/{wiki → content}/extensions/helpers/types/index.md +11 -8
- package/{wiki → content}/extensions/helpers/websocket/api.md +9 -9
- package/{wiki → content}/extensions/helpers/websocket/index.md +7 -7
- package/{wiki → content}/extensions/helpers/worker-thread/index.md +2 -2
- package/{wiki → content}/extensions/index.md +3 -4
- package/{wiki → content}/extensions/src-details/mcp-server.md +18 -24
- package/{wiki → content}/guides/core-concepts/application/bootstrapping.md +11 -14
- package/{wiki → content}/guides/core-concepts/application/index.md +3 -3
- package/{wiki → content}/guides/core-concepts/components.md +19 -10
- package/{wiki → content}/guides/core-concepts/dependency-injection.md +6 -3
- package/{wiki → content}/guides/core-concepts/grpc-controllers.md +6 -5
- package/{wiki → content}/guides/core-concepts/persistent/datasources.md +33 -27
- package/{wiki → content}/guides/core-concepts/persistent/index.md +16 -5
- package/{wiki → content}/guides/core-concepts/persistent/models.md +24 -20
- package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
- package/{wiki → content}/guides/core-concepts/persistent/repositories.md +40 -23
- package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
- package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
- package/{wiki → content}/guides/core-concepts/persistent/transactions.md +61 -25
- package/{wiki → content}/guides/core-concepts/rest-controllers.md +12 -9
- package/content/guides/core-concepts/services.md +389 -0
- package/{wiki → content}/guides/get-started/5-minute-quickstart.md +19 -19
- package/{wiki → content}/guides/get-started/philosophy.md +36 -36
- package/{wiki → content}/guides/get-started/setup.md +3 -3
- package/{wiki → content}/guides/index.md +3 -3
- package/content/guides/migrations/redis-helpers-migration.md +177 -0
- package/{wiki → content}/guides/migrations/scoped-rbac-migration.md +17 -17
- package/content/guides/migrations/unified-connectors-migration.md +113 -0
- package/{wiki → content}/guides/reference/glossary.md +19 -12
- package/{wiki → content}/guides/reference/mcp-docs-server.md +22 -18
- package/{wiki → content}/guides/tutorials/building-a-crud-api.md +30 -33
- package/{wiki → content}/guides/tutorials/complete-installation.md +17 -17
- package/{wiki → content}/guides/tutorials/ecommerce-api.md +158 -119
- package/{wiki → content}/guides/tutorials/realtime-chat.md +176 -130
- package/content/guides/tutorials/testing.md +264 -0
- package/content/index.md +5 -0
- package/content/public/apple-touch-icon.png +0 -0
- package/content/public/og-image.png +0 -0
- package/content/public/site.webmanifest +11 -0
- package/{wiki → content}/references/base/application.md +4 -5
- package/{wiki → content}/references/base/bootstrapping.md +18 -5
- package/{wiki → content}/references/base/components.md +149 -120
- package/content/references/base/connectors.md +178 -0
- package/{wiki → content}/references/base/controllers.md +41 -30
- package/content/references/base/datasources.md +527 -0
- package/{wiki → content}/references/base/dependency-injection.md +34 -22
- package/{wiki → content}/references/base/filter-system/application-usage.md +17 -14
- package/{wiki → content}/references/base/filter-system/array-operators.md +7 -2
- package/{wiki → content}/references/base/filter-system/comparison-operators.md +3 -0
- package/{wiki → content}/references/base/filter-system/default-filter.md +89 -71
- package/{wiki → content}/references/base/filter-system/fields-order-pagination.md +22 -22
- package/{wiki → content}/references/base/filter-system/index.md +6 -3
- package/{wiki → content}/references/base/filter-system/json-filtering.md +20 -1
- package/{wiki → content}/references/base/filter-system/list-operators.md +1 -1
- package/{wiki → content}/references/base/filter-system/logical-operators.md +33 -1
- package/{wiki → content}/references/base/filter-system/null-operators.md +30 -1
- package/{wiki → content}/references/base/filter-system/quick-reference.md +23 -4
- package/{wiki → content}/references/base/filter-system/tips.md +5 -5
- package/{wiki → content}/references/base/filter-system/use-cases.md +12 -12
- package/{wiki → content}/references/base/grpc-controllers.md +13 -13
- package/{wiki → content}/references/base/index.md +24 -12
- package/{wiki/references/base/middleware.md → content/references/base/middlewares.md} +205 -24
- package/{wiki → content}/references/base/models.md +63 -49
- package/{wiki → content}/references/base/providers.md +136 -130
- package/{wiki → content}/references/base/repositories/advanced.md +59 -58
- package/{wiki → content}/references/base/repositories/index.md +115 -91
- package/content/references/base/repositories/mixins.md +99 -0
- package/{wiki → content}/references/base/repositories/relations.md +54 -64
- package/{wiki → content}/references/base/repositories/soft-deletable.md +31 -30
- package/content/references/base/services.md +404 -0
- package/{wiki → content}/references/configuration/environment-variables.md +46 -30
- package/{wiki → content}/references/configuration/index.md +6 -6
- package/{wiki → content}/references/index.md +17 -12
- package/{wiki → content}/references/quick-reference.md +65 -106
- package/content/references/utilities/crypto.md +98 -0
- package/{wiki → content}/references/utilities/index.md +3 -3
- package/{wiki → content}/references/utilities/jsx.md +6 -4
- package/content/references/utilities/module.md +90 -0
- package/{wiki → content}/references/utilities/parse.md +4 -14
- package/{wiki → content}/references/utilities/promise.md +9 -7
- package/{wiki → content}/references/utilities/schema.md +5 -3
- package/dist/mcp-server/common/guards.d.ts +8 -0
- package/dist/mcp-server/common/guards.d.ts.map +1 -0
- package/dist/mcp-server/common/guards.js +14 -0
- package/dist/mcp-server/common/guards.js.map +1 -0
- package/dist/mcp-server/common/index.d.ts +1 -0
- package/dist/mcp-server/common/index.d.ts.map +1 -1
- package/dist/mcp-server/common/index.js +1 -0
- package/dist/mcp-server/common/index.js.map +1 -1
- package/dist/mcp-server/common/paths.d.ts.map +1 -1
- package/dist/mcp-server/common/paths.js +2 -2
- package/dist/mcp-server/common/paths.js.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.js +4 -2
- package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
- package/dist/mcp-server/helpers/github.helper.js +1 -1
- package/dist/mcp-server/index.js +7 -2
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/mcp-server/tools/base.tool.d.ts +6 -2
- package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/base.tool.js.map +1 -1
- package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
- package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
- package/package.json +12 -12
- package/wiki/extensions/helpers/error/index.md +0 -227
- package/wiki/extensions/helpers/redis/index.md +0 -488
- package/wiki/extensions/helpers/testing/index.md +0 -510
- package/wiki/guides/core-concepts/services.md +0 -119
- package/wiki/guides/tutorials/testing.md +0 -722
- package/wiki/index.md +0 -183
- package/wiki/references/base/datasources.md +0 -454
- package/wiki/references/base/middlewares.md +0 -590
- package/wiki/references/base/repositories/mixins.md +0 -335
- package/wiki/references/base/services.md +0 -201
- package/wiki/references/utilities/crypto.md +0 -56
- package/wiki/references/utilities/module.md +0 -42
- /package/{wiki → content}/extensions/components/mail/api.md +0 -0
- /package/{wiki → content}/extensions/components/template/api-page.md +0 -0
- /package/{wiki → content}/extensions/components/template/errors-page.md +0 -0
- /package/{wiki → content}/extensions/components/template/single-page.md +0 -0
- /package/{wiki → content}/extensions/components/template/usage-page.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/admin.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/consumer.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/index.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/producer.md +0 -0
- /package/{wiki → content}/extensions/helpers/kafka/schema-registry.md +0 -0
- /package/{wiki → content}/extensions/helpers/network/api.md +0 -0
- /package/{wiki → content}/extensions/helpers/socket-io/api.md +0 -0
- /package/{wiki → content}/extensions/helpers/template/single-page.md +0 -0
- /package/{wiki → content}/extensions/helpers/uid/index.md +0 -0
- /package/{wiki → content}/guides/core-concepts/components-guide.md +0 -0
- /package/{wiki → content}/public/logo.svg +0 -0
- /package/{wiki → content}/references/base/filter-system/pattern-matching.md +0 -0
- /package/{wiki → content}/references/base/filter-system/range-operators.md +0 -0
- /package/{wiki → content}/references/utilities/date.md +0 -0
- /package/{wiki → content}/references/utilities/performance.md +0 -0
- /package/{wiki → content}/references/utilities/request.md +0 -0
- /package/{wiki → content}/references/utilities/statuses.md +0 -0
|
@@ -30,8 +30,6 @@ await app.start();
|
|
|
30
30
|
- `initialize()` - Bootstrap the application
|
|
31
31
|
- `start()` - Start HTTP server
|
|
32
32
|
- `stop()` - Stop server gracefully
|
|
33
|
-
- `get<T>(key)` - Resolve from DI container
|
|
34
|
-
- `mountControllers()` - Register controllers
|
|
35
33
|
|
|
36
34
|
### BaseRestController
|
|
37
35
|
|
|
@@ -85,7 +83,7 @@ class GreeterController extends BaseGrpcController {
|
|
|
85
83
|
```typescript
|
|
86
84
|
import { BaseService, injectable } from '@venizia/ignis';
|
|
87
85
|
|
|
88
|
-
@injectable()
|
|
86
|
+
@injectable({})
|
|
89
87
|
class UserService extends BaseService {
|
|
90
88
|
constructor() {
|
|
91
89
|
super({ scope: UserService.name });
|
|
@@ -93,7 +91,7 @@ class UserService extends BaseService {
|
|
|
93
91
|
|
|
94
92
|
async getUser(id: string) {
|
|
95
93
|
this.logger.info('Getting user', id);
|
|
96
|
-
return this.
|
|
94
|
+
return this.userRepository.findById({ id });
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
```
|
|
@@ -104,27 +102,25 @@ class UserService extends BaseService {
|
|
|
104
102
|
### DefaultCRUDRepository
|
|
105
103
|
|
|
106
104
|
```typescript
|
|
107
|
-
import { DefaultCRUDRepository } from '@venizia/ignis';
|
|
105
|
+
import { DefaultCRUDRepository, repository } from '@venizia/ignis';
|
|
108
106
|
import { User } from '../models';
|
|
107
|
+
import { PostgresDataSource } from '../datasources';
|
|
109
108
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
109
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
110
|
+
class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
111
|
+
// No constructor needed - dataSource auto-injected from @repository decorator
|
|
114
112
|
}
|
|
115
113
|
```
|
|
116
114
|
|
|
117
115
|
**Key Methods:**
|
|
118
|
-
- `
|
|
119
|
-
- `
|
|
120
|
-
- `
|
|
121
|
-
- `
|
|
122
|
-
- `
|
|
123
|
-
- `
|
|
124
|
-
- `
|
|
125
|
-
- `
|
|
126
|
-
- `delete(id)` - Delete by ID (soft/hard based on config)
|
|
127
|
-
- `deleteMany(filter)` - Delete multiple
|
|
116
|
+
- `find({ filter })` - Find many with filter, returns `T[]`
|
|
117
|
+
- `findById({ id })` - Find by ID
|
|
118
|
+
- `findOne({ filter })` - Find single entity
|
|
119
|
+
- `count({ where? })` - Count entities
|
|
120
|
+
- `create({ data })` - Create single entity, returns `{ count, data }`
|
|
121
|
+
- `createAll({ data: [] })` - Create multiple entities, returns `{ count, data[] }`
|
|
122
|
+
- `updateById({ id, data })` - Update by ID
|
|
123
|
+
- `deleteById({ id })` - Delete by ID
|
|
128
124
|
|
|
129
125
|
### BaseEntity
|
|
130
126
|
|
|
@@ -132,10 +128,10 @@ class UserRepository extends DefaultCRUDRepository<User> {
|
|
|
132
128
|
import { BaseEntity, model } from '@venizia/ignis';
|
|
133
129
|
import { integer, text, pgTable } from 'drizzle-orm/pg-core';
|
|
134
130
|
|
|
135
|
-
@model()
|
|
131
|
+
@model({ type: 'entity' })
|
|
136
132
|
class User extends BaseEntity {
|
|
137
|
-
static readonly
|
|
138
|
-
static readonly schema = pgTable(User.
|
|
133
|
+
static readonly TABLE_NAME = 'users';
|
|
134
|
+
static readonly schema = pgTable(User.TABLE_NAME, {
|
|
139
135
|
id: integer('id').primaryKey(),
|
|
140
136
|
name: text('name').notNull(),
|
|
141
137
|
email: text('email').notNull().unique(),
|
|
@@ -144,7 +140,7 @@ class User extends BaseEntity {
|
|
|
144
140
|
```
|
|
145
141
|
|
|
146
142
|
**Key Properties:**
|
|
147
|
-
- `static
|
|
143
|
+
- `static TABLE_NAME` - Database table name
|
|
148
144
|
- `static schema` - Drizzle schema definition
|
|
149
145
|
- `static AUTHORIZATION_SUBJECT` - Authorization principal (auto-set from `@model` settings `authorize.principal`)
|
|
150
146
|
|
|
@@ -226,7 +222,7 @@ class UserController extends BaseRestController {
|
|
|
226
222
|
| Operator | SQL | Example |
|
|
227
223
|
|----------|-----|---------|
|
|
228
224
|
| `in` | `IN` | `{ status: { in: ['active', 'pending'] } }` |
|
|
229
|
-
| `
|
|
225
|
+
| `nin` | `NOT IN` | `{ status: { nin: ['deleted'] } }` |
|
|
230
226
|
|
|
231
227
|
### Pattern Matching
|
|
232
228
|
|
|
@@ -234,17 +230,8 @@ class UserController extends BaseRestController {
|
|
|
234
230
|
|----------|-----|---------|
|
|
235
231
|
| `like` | `LIKE` | `{ name: { like: '%john%' } }` |
|
|
236
232
|
| `ilike` | `ILIKE` | `{ email: { ilike: '%@gmail.com' } }` |
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
| `startsWith` | `LIKE 'value%'` | `{ name: { startsWith: 'John' } }` |
|
|
240
|
-
| `endsWith` | `LIKE '%value'` | `{ email: { endsWith: '@example.com' } }` |
|
|
241
|
-
|
|
242
|
-
### Null Operators
|
|
243
|
-
|
|
244
|
-
| Operator | SQL | Example |
|
|
245
|
-
|----------|-----|---------|
|
|
246
|
-
| `isNull` | `IS NULL` | `{ deletedAt: { isNull: true } }` |
|
|
247
|
-
| `isNotNull` | `IS NOT NULL` | `{ email: { isNotNull: true } }` |
|
|
233
|
+
| `nlike` | `NOT LIKE` | `{ name: { nlike: '%test%' } }` |
|
|
234
|
+
| `nilike` | `NOT ILIKE` | `{ email: { nilike: '%spam%' } }` |
|
|
248
235
|
|
|
249
236
|
### Logical Operators
|
|
250
237
|
|
|
@@ -262,61 +249,59 @@ class UserController extends BaseRestController {
|
|
|
262
249
|
| `containedBy` | `<@` | `{ tags: { containedBy: ['ts', 'js', 'go'] } }` |
|
|
263
250
|
| `overlaps` | `&&` | `{ tags: { overlaps: ['react', 'vue'] } }` |
|
|
264
251
|
|
|
265
|
-
### JSON Operators (PostgreSQL)
|
|
266
|
-
|
|
267
|
-
| Operator | Description | Example |
|
|
268
|
-
|----------|-------------|---------|
|
|
269
|
-
| `jsonPath` | Query JSON field | `{ metadata: { jsonPath: '$.user.name', eq: 'John' } }` |
|
|
270
|
-
|
|
271
|
-
|
|
272
252
|
## Common Filters
|
|
273
253
|
|
|
274
254
|
### Basic Find
|
|
275
255
|
|
|
276
256
|
```typescript
|
|
277
|
-
const users = await
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
257
|
+
const users = await userRepository.find({
|
|
258
|
+
filter: {
|
|
259
|
+
where: { isActive: true },
|
|
260
|
+
order: ['createdAt DESC'],
|
|
261
|
+
limit: 10,
|
|
262
|
+
offset: 0,
|
|
263
|
+
},
|
|
282
264
|
});
|
|
283
265
|
```
|
|
284
266
|
|
|
285
267
|
### With Multiple Conditions
|
|
286
268
|
|
|
287
269
|
```typescript
|
|
288
|
-
const users = await
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
270
|
+
const users = await userRepository.find({
|
|
271
|
+
filter: {
|
|
272
|
+
where: {
|
|
273
|
+
and: [
|
|
274
|
+
{ age: { gte: 18 } },
|
|
275
|
+
{ status: { in: ['active', 'pending'] } },
|
|
276
|
+
{ email: { ilike: '%@company.com' } },
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
},
|
|
296
280
|
});
|
|
297
281
|
```
|
|
298
282
|
|
|
299
283
|
### With Relations
|
|
300
284
|
|
|
301
285
|
```typescript
|
|
302
|
-
const posts = await
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
where: { approved: true },
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
}
|
|
286
|
+
const posts = await postRepository.find({
|
|
287
|
+
filter: {
|
|
288
|
+
where: { published: true },
|
|
289
|
+
include: [
|
|
290
|
+
{ relation: 'author' },
|
|
291
|
+
{ relation: 'comments', scope: { where: { approved: true }, limit: 5 } },
|
|
292
|
+
],
|
|
293
|
+
},
|
|
311
294
|
});
|
|
312
295
|
```
|
|
313
296
|
|
|
314
297
|
### Selecting Fields
|
|
315
298
|
|
|
316
299
|
```typescript
|
|
317
|
-
const users = await
|
|
318
|
-
|
|
319
|
-
|
|
300
|
+
const users = await userRepository.find({
|
|
301
|
+
filter: {
|
|
302
|
+
where: { isActive: true },
|
|
303
|
+
fields: ['id', 'name', 'email'],
|
|
304
|
+
},
|
|
320
305
|
});
|
|
321
306
|
```
|
|
322
307
|
|
|
@@ -328,7 +313,7 @@ const users = await userRepo.find({
|
|
|
328
313
|
```typescript
|
|
329
314
|
import { injectable } from '@venizia/ignis';
|
|
330
315
|
|
|
331
|
-
@injectable()
|
|
316
|
+
@injectable({})
|
|
332
317
|
class MyService extends BaseService {
|
|
333
318
|
// ...
|
|
334
319
|
}
|
|
@@ -348,13 +333,6 @@ class UserController extends BaseRestController {
|
|
|
348
333
|
}
|
|
349
334
|
```
|
|
350
335
|
|
|
351
|
-
### Manual Resolution
|
|
352
|
-
|
|
353
|
-
```typescript
|
|
354
|
-
const userService = app.get<UserService>('services.UserService');
|
|
355
|
-
```
|
|
356
|
-
|
|
357
|
-
|
|
358
336
|
## Common Imports
|
|
359
337
|
|
|
360
338
|
### Core Framework
|
|
@@ -408,21 +386,20 @@ import {
|
|
|
408
386
|
ApplicationLogger,
|
|
409
387
|
|
|
410
388
|
// Caching
|
|
411
|
-
|
|
389
|
+
RedisSingleHelper,
|
|
412
390
|
|
|
413
391
|
// Queues
|
|
414
392
|
QueueHelper,
|
|
415
393
|
|
|
416
394
|
// Crypto
|
|
417
395
|
hash,
|
|
418
|
-
compare,
|
|
419
396
|
|
|
420
397
|
// HTTP
|
|
421
398
|
HTTP,
|
|
422
399
|
} from '@venizia/ignis-helpers';
|
|
423
400
|
import { BullMQHelper } from '@venizia/ignis-helpers/bullmq';
|
|
424
401
|
import { CronHelper } from '@venizia/ignis-helpers/cron';
|
|
425
|
-
import {
|
|
402
|
+
import { MinioHelper } from '@venizia/ignis-helpers/minio';
|
|
426
403
|
```
|
|
427
404
|
|
|
428
405
|
### Dependency Injection
|
|
@@ -487,14 +464,17 @@ getDashboard(c: Context) {
|
|
|
487
464
|
import { Statuses } from '@venizia/ignis';
|
|
488
465
|
|
|
489
466
|
// Create with status
|
|
490
|
-
const order = await
|
|
491
|
-
|
|
492
|
-
|
|
467
|
+
const { data: order } = await orderRepository.create({
|
|
468
|
+
data: {
|
|
469
|
+
items: [...],
|
|
470
|
+
status: Statuses.PENDING,
|
|
471
|
+
},
|
|
493
472
|
});
|
|
494
473
|
|
|
495
474
|
// Update status
|
|
496
|
-
await
|
|
497
|
-
|
|
475
|
+
await orderRepository.updateById({
|
|
476
|
+
id: orderId,
|
|
477
|
+
data: { status: Statuses.COMPLETED },
|
|
498
478
|
});
|
|
499
479
|
|
|
500
480
|
// Check status
|
|
@@ -556,27 +536,6 @@ app.notFound(notFoundHandler({ logger: app.logger }));
|
|
|
556
536
|
```
|
|
557
537
|
|
|
558
538
|
|
|
559
|
-
## Environment Variables
|
|
560
|
-
|
|
561
|
-
### Loading Environment
|
|
562
|
-
|
|
563
|
-
```typescript
|
|
564
|
-
import { EnvHelper } from '@venizia/ignis-helpers';
|
|
565
|
-
|
|
566
|
-
// Load from .env file
|
|
567
|
-
EnvHelper.load();
|
|
568
|
-
|
|
569
|
-
// Get variable
|
|
570
|
-
const dbUrl = EnvHelper.get('DATABASE_URL');
|
|
571
|
-
|
|
572
|
-
// Get with default
|
|
573
|
-
const port = EnvHelper.get('PORT', '3000');
|
|
574
|
-
|
|
575
|
-
// Get required (throws if missing)
|
|
576
|
-
const apiKey = EnvHelper.getRequired('API_KEY');
|
|
577
|
-
```
|
|
578
|
-
|
|
579
|
-
|
|
580
539
|
## Common Patterns
|
|
581
540
|
|
|
582
541
|
### Controller → Service → Repository
|
|
@@ -605,7 +564,7 @@ class UserController extends BaseRestController {
|
|
|
605
564
|
class UserService extends BaseService {
|
|
606
565
|
constructor(
|
|
607
566
|
@inject({ key: 'repositories.UserRepository' })
|
|
608
|
-
private
|
|
567
|
+
private userRepository: UserRepository,
|
|
609
568
|
) {
|
|
610
569
|
super({ scope: UserService.name });
|
|
611
570
|
}
|
|
@@ -614,7 +573,7 @@ class UserService extends BaseService {
|
|
|
614
573
|
// Business logic
|
|
615
574
|
const hashedPassword = await hash({ value: data.password });
|
|
616
575
|
|
|
617
|
-
return this.
|
|
576
|
+
return this.userRepository.create({
|
|
618
577
|
data: {
|
|
619
578
|
...data,
|
|
620
579
|
password: hashedPassword,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# Crypto Utility
|
|
2
|
+
|
|
3
|
+
The Crypto utility provides a single stateless `hash` function built on Node's built-in `node:crypto` module. It covers the two most common lightweight hashing needs - MD5 digests and HMAC-SHA256 signatures - without any external dependencies.
|
|
4
|
+
|
|
5
|
+
::: tip Full AES / RSA / ECDH encryption
|
|
6
|
+
This page covers only the utility-level `hash` function. For full symmetric/asymmetric encryption (AES-256-CBC, AES-256-GCM, RSA, ECDH key exchange) see the [Crypto helper](/extensions/helpers/crypto/).
|
|
7
|
+
:::
|
|
8
|
+
|
|
9
|
+
## `hash`
|
|
10
|
+
|
|
11
|
+
Creates a hash or HMAC digest of a string and returns it as a text-encoded string.
|
|
12
|
+
|
|
13
|
+
### Signature
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
hash(
|
|
17
|
+
text: string,
|
|
18
|
+
options: {
|
|
19
|
+
algorithm: 'SHA256' | 'MD5';
|
|
20
|
+
secret?: string;
|
|
21
|
+
outputType: BinaryToTextEncoding; // 'hex' | 'base64' | 'base64url' | 'latin1'
|
|
22
|
+
},
|
|
23
|
+
): string
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Parameters**
|
|
27
|
+
|
|
28
|
+
| Parameter | Type | Description |
|
|
29
|
+
|-----------|------|-------------|
|
|
30
|
+
| `text` | `string` | The input string to hash. |
|
|
31
|
+
| `options.algorithm` | `'SHA256' \| 'MD5'` | Hashing algorithm. |
|
|
32
|
+
| `options.secret` | `string` (optional) | Secret key for HMAC. Only used when `algorithm` is `'SHA256'`. |
|
|
33
|
+
| `options.outputType` | `BinaryToTextEncoding` | Encoding of the output string - typically `'hex'` or `'base64'`. |
|
|
34
|
+
|
|
35
|
+
### Behavior
|
|
36
|
+
|
|
37
|
+
| Algorithm | `secret` provided | Result |
|
|
38
|
+
|-----------|-------------------|--------|
|
|
39
|
+
| `'MD5'` | ignored | MD5 digest of `text` |
|
|
40
|
+
| `'SHA256'` | yes | HMAC-SHA256 of `text` signed with `secret` |
|
|
41
|
+
| `'SHA256'` | no / `undefined` | `text` returned unchanged (no-op) |
|
|
42
|
+
| any other | - | `text` returned unchanged (no-op) |
|
|
43
|
+
|
|
44
|
+
The SHA256 pass-through is intentional: it lets callers skip hashing conditionally (for example, when a secret is not yet configured) without adding an extra `if` at the call site. If you need an unconditional SHA256 hash without a secret, use MD5 or the full [Crypto helper](/extensions/helpers/crypto/).
|
|
45
|
+
|
|
46
|
+
### Examples
|
|
47
|
+
|
|
48
|
+
**MD5 digest (hex)**
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { hash } from '@venizia/ignis-helpers';
|
|
52
|
+
|
|
53
|
+
const digest = hash('user@example.com', { algorithm: 'MD5', outputType: 'hex' });
|
|
54
|
+
// => 'b58996c504c5638798eb6b511e6f49af'
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**MD5 digest (base64) - useful for HTTP ETags**
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const etag = hash(JSON.stringify(payload), { algorithm: 'MD5', outputType: 'base64' });
|
|
61
|
+
// => 'tYlsUExWOHeY62a1EW9Jr...'
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**HMAC-SHA256 - signing a webhook payload**
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { hash } from '@venizia/ignis-helpers';
|
|
68
|
+
|
|
69
|
+
const signature = hash(rawBody, {
|
|
70
|
+
algorithm: 'SHA256',
|
|
71
|
+
secret: process.env.WEBHOOK_SECRET,
|
|
72
|
+
outputType: 'hex',
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// Compare against the value in the X-Hub-Signature-256 header
|
|
76
|
+
const expected = `sha256=${signature}`;
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**HMAC-SHA256 in base64 - API request signing**
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const hmac = hash(`${timestamp}.${body}`, {
|
|
83
|
+
algorithm: 'SHA256',
|
|
84
|
+
secret: apiKey,
|
|
85
|
+
outputType: 'base64',
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## When to use
|
|
90
|
+
|
|
91
|
+
Use `hash` for lightweight, one-off hashing that does not require key management or IV handling:
|
|
92
|
+
|
|
93
|
+
- Building cache keys or ETags from response bodies (MD5)
|
|
94
|
+
- Verifying webhook signatures (HMAC-SHA256)
|
|
95
|
+
- Signing API requests with a shared secret (HMAC-SHA256)
|
|
96
|
+
- Anonymising personally identifiable data before storing in logs (MD5)
|
|
97
|
+
|
|
98
|
+
For encryption, decryption, or asymmetric operations (AES, RSA, ECDH) use the [Crypto helper](/extensions/helpers/crypto/) instead.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Utilities
|
|
2
2
|
|
|
3
|
-
Pure, standalone functions providing common, reusable logic for the
|
|
3
|
+
Pure, standalone functions providing common, reusable logic for the IGNIS framework. All utilities are stateless and easy to use.
|
|
4
4
|
|
|
5
5
|
## Quick Reference
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ Pure, standalone functions providing common, reusable logic for the Ignis framew
|
|
|
10
10
|
| **Date** | `ignis-helpers` | Date/time manipulation | `dayjs`, `sleep()`, `isWeekday()`, `getDateTz()`, `hrTime()` |
|
|
11
11
|
| **JSX** | `ignis` | HTML/JSX responses | `htmlContent()`, `htmlResponse()` |
|
|
12
12
|
| **Module** | `ignis-helpers` | Module validation | `validateModule()` |
|
|
13
|
-
| **Parse** | `ignis-helpers` | Data type conversion | `int()`, `float()`, `toBoolean()`, `toCamel()
|
|
13
|
+
| **Parse** | `ignis-helpers` | Data type conversion | `int()`, `float()`, `toBoolean()`, `toCamel()` |
|
|
14
14
|
| **Performance** | `ignis-helpers` | Execution timing | `executeWithPerformanceMeasure()`, `getPerformanceCheckpoint()` |
|
|
15
15
|
| **Promise** | `ignis-helpers` | Promise helpers | `executePromiseWithLimit()`, `isPromiseLike()`, `getDeepProperty()` |
|
|
16
16
|
| **Request** | `ignis-helpers` | HTTP utilities | `parseMultipartBody()`, `sanitizeFilename()`, `createContentDispositionHeader()` |
|
|
@@ -47,7 +47,7 @@ Utilities are imported from `@venizia/ignis` (schema, JSX, and status helpers) o
|
|
|
47
47
|
|
|
48
48
|
```typescript
|
|
49
49
|
import { jsonContent, jsonResponse, htmlResponse, requiredString, Statuses } from '@venizia/ignis';
|
|
50
|
-
import { hash, dayjs, sleep, int, float, toBoolean
|
|
50
|
+
import { hash, dayjs, sleep, int, float, toBoolean } from '@venizia/ignis-helpers';
|
|
51
51
|
|
|
52
52
|
// Crypto
|
|
53
53
|
const md5Hash = hash('some text', { algorithm: 'MD5', outputType: 'hex' });
|
|
@@ -186,7 +186,8 @@ export class PageController extends BaseRestController {
|
|
|
186
186
|
### HTML Email Preview
|
|
187
187
|
|
|
188
188
|
```typescript
|
|
189
|
-
import { BaseRestController, get, htmlResponse, TRouteContext
|
|
189
|
+
import { BaseRestController, get, htmlResponse, TRouteContext } from '@venizia/ignis';
|
|
190
|
+
import { z } from '@hono/zod-openapi';
|
|
190
191
|
import { HTTP } from '@venizia/ignis-helpers';
|
|
191
192
|
|
|
192
193
|
const EmailRoutes = {
|
|
@@ -225,7 +226,8 @@ export class EmailController extends BaseRestController {
|
|
|
225
226
|
### Documentation Page
|
|
226
227
|
|
|
227
228
|
```typescript
|
|
228
|
-
import { BaseRestController, get, htmlResponse, TRouteContext
|
|
229
|
+
import { BaseRestController, get, htmlResponse, TRouteContext } from '@venizia/ignis';
|
|
230
|
+
import { z } from '@hono/zod-openapi';
|
|
229
231
|
import { HTTP } from '@venizia/ignis-helpers';
|
|
230
232
|
|
|
231
233
|
const DocsRoutes = {
|
|
@@ -355,7 +357,7 @@ async getUserProfile(c: TRouteContext) {
|
|
|
355
357
|
return c.html(<UserProfile user={user} />);
|
|
356
358
|
}
|
|
357
359
|
|
|
358
|
-
// Bad: Don't use htmlResponse for API endpoints
|
|
360
|
+
// Bad: Don't use htmlResponse for API endpoints - use jsonResponse instead
|
|
359
361
|
```
|
|
360
362
|
|
|
361
363
|
### 2. Combine with Authentication
|
|
@@ -564,7 +566,7 @@ async getUsers() {
|
|
|
564
566
|
- **Related References:**
|
|
565
567
|
- [Schema Utility](./schema.md) - JSON content and response helpers
|
|
566
568
|
- [Controllers](../base/controllers.md) - Defining routes and handlers
|
|
567
|
-
- [OpenAPI Component](/extensions/components/
|
|
569
|
+
- [OpenAPI Component](/extensions/components/api-reference) - API documentation
|
|
568
570
|
|
|
569
571
|
- **External Resources:**
|
|
570
572
|
- [Hono JSX Documentation](https://hono.dev/guides/jsx)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# Module Utility
|
|
2
|
+
|
|
3
|
+
The Module utility provides a pre-flight check for optional peer dependencies. IGNIS helpers and components often depend on packages that are not bundled with the framework (for example, `@connectrpc/connect` for gRPC, `@hono/swagger-ui` for the Swagger component). Calling `validateModule` at the start of a lazy-loaded code path ensures a clear, actionable error is thrown before any import is attempted, rather than a cryptic "Cannot find module" crash.
|
|
4
|
+
|
|
5
|
+
Resolution is rooted at `process.cwd()/node_modules` via Node's `createRequire`, so peer dependencies installed in the consuming application are found correctly even though this utility lives inside `packages/helpers/dist/`.
|
|
6
|
+
|
|
7
|
+
## `validateModule`
|
|
8
|
+
|
|
9
|
+
Resolves each module name in sequence using `require.resolve`. If any module cannot be found it logs the failure and throws an `ApplicationError` with an install instruction. Returns a `Promise<void>` - it is async to support consistent `await` usage at call sites, though resolution itself is synchronous.
|
|
10
|
+
|
|
11
|
+
### Signature
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
validateModule(opts: {
|
|
15
|
+
scope?: string;
|
|
16
|
+
modules: Array<string>;
|
|
17
|
+
}): Promise<void>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Options**
|
|
21
|
+
|
|
22
|
+
| Option | Type | Default | Description |
|
|
23
|
+
|--------|------|---------|-------------|
|
|
24
|
+
| `modules` | `Array<string>` | `[]` | Module names to check. Evaluated in order - the first missing module stops the loop and throws. |
|
|
25
|
+
| `scope` | `string` | `''` | Human-readable label for the calling feature (e.g. the component or helper class name). Included in the error message to tell the developer which feature needs the package. |
|
|
26
|
+
|
|
27
|
+
### Error message format
|
|
28
|
+
|
|
29
|
+
When a module is missing the thrown error reads:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
[validateModule] <module> is required for <scope>. Please install '<module>'
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
If `scope` is omitted:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
[validateModule] <module> is required. Please install '<module>'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Example - gRPC controller (optional dep guard)
|
|
42
|
+
|
|
43
|
+
The `GrpcRequestAdapter` uses `validateModule` to gate the ConnectRPC import. The check runs once before the adapter is wired up, so the error surfaces at startup rather than on the first request.
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { validateModule } from '@venizia/ignis-helpers';
|
|
47
|
+
|
|
48
|
+
const GRPC_MODULES = ['@connectrpc/connect'];
|
|
49
|
+
|
|
50
|
+
export class MyGrpcController extends BaseGrpcController {
|
|
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 });
|
|
54
|
+
|
|
55
|
+
const { ConnectRouter } = await import('@connectrpc/connect');
|
|
56
|
+
// ... register routes
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Example - custom helper with multiple optional deps
|
|
62
|
+
|
|
63
|
+
When a feature requires several packages, list them all. The first missing one stops the check.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
import { validateModule } from '@venizia/ignis-helpers';
|
|
67
|
+
import { BaseHelper } from '@venizia/ignis-helpers';
|
|
68
|
+
|
|
69
|
+
export class KafkaQueueHelper extends BaseHelper {
|
|
70
|
+
async configure() {
|
|
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
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## When to use
|
|
83
|
+
|
|
84
|
+
Use `validateModule` whenever your code does a dynamic `import()` of a package that is listed as an optional peer dependency in `package.json`. The recommended pattern is:
|
|
85
|
+
|
|
86
|
+
1. Declare the dep as `peerDependenciesMeta` with `optional: true` in `package.json`.
|
|
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.
|
|
89
|
+
|
|
90
|
+
Avoid calling `validateModule` on every request. Place it in an initialisation hook (`configure`, `binding`, `boot`) that runs once at startup.
|
|
@@ -12,16 +12,13 @@ The Parse utility provides a collection of functions for data type checking, con
|
|
|
12
12
|
- **`int(input)`**: Parses a value to an integer. Handles strings with commas and defaults to `0` if the input is invalid.
|
|
13
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
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
15
|
|
|
17
16
|
```typescript
|
|
18
|
-
import { int, float, toBoolean
|
|
17
|
+
import { int, float, toBoolean } from '@venizia/ignis-helpers';
|
|
19
18
|
|
|
20
19
|
const myInt = int('1,000'); // => 1000
|
|
21
20
|
const myFloat = float('1,234.567', 2); // => 1234.57
|
|
22
21
|
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'
|
|
25
22
|
```
|
|
26
23
|
|
|
27
24
|
## String and Object Transformation
|
|
@@ -41,17 +38,12 @@ const camelObject = keysToCamel({ 'first-name': 'John', 'last_name': 'Doe' });
|
|
|
41
38
|
|
|
42
39
|
## Number Parsing
|
|
43
40
|
|
|
44
|
-
- **`getNumberValue(input, opts?)`**: Parses a string to a number with locale support (US or EU format).
|
|
45
41
|
|
|
46
42
|
```typescript
|
|
47
|
-
import { getNumberValue } from '@venizia/ignis-helpers';
|
|
48
43
|
|
|
49
|
-
// US format (default)
|
|
50
|
-
getNumberValue('1,234.56', { method: 'float' }); // => 1234.56
|
|
51
|
-
getNumberValue('1,234', { method: 'int' }); // => 1234
|
|
44
|
+
// US format (default) - comma is thousands separator
|
|
52
45
|
|
|
53
|
-
// EU format
|
|
54
|
-
getNumberValue('1.234,56', { method: 'float', locale: 'eu' }); // => 1234.56
|
|
46
|
+
// EU format - dot is thousands separator, comma is decimal
|
|
55
47
|
```
|
|
56
48
|
|
|
57
49
|
| Option | Type | Default | Description |
|
|
@@ -61,11 +53,10 @@ getNumberValue('1.234,56', { method: 'float', locale: 'eu' }); // => 1234.56
|
|
|
61
53
|
|
|
62
54
|
## Array Transformation
|
|
63
55
|
|
|
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
56
|
- **`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.
|
|
66
57
|
|
|
67
58
|
```typescript
|
|
68
|
-
import {
|
|
59
|
+
import { parseArrayToMapWithKey } from '@venizia/ignis-helpers';
|
|
69
60
|
|
|
70
61
|
const users = [
|
|
71
62
|
{ id: 1, name: 'Alice' },
|
|
@@ -73,7 +64,6 @@ const users = [
|
|
|
73
64
|
];
|
|
74
65
|
|
|
75
66
|
// Record (options object pattern)
|
|
76
|
-
const usersRecord = parseArrayToRecordWithKey({ arr: users, keyMap: 'id' });
|
|
77
67
|
// => { 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } }
|
|
78
68
|
|
|
79
69
|
// Map (positional arguments)
|
|
@@ -38,17 +38,19 @@ const results = await executePromiseWithLimit({
|
|
|
38
38
|
console.log('All tasks finished:', results);
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
## `
|
|
41
|
+
## `toError`
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
Normalizes an unknown thrown value into an `Error` instance. Useful in `catch` blocks where the caught value is typed `unknown` and isn't guaranteed to already be an `Error`.
|
|
44
44
|
|
|
45
45
|
```typescript
|
|
46
|
-
import {
|
|
46
|
+
import { toError } from '@venizia/ignis-helpers';
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
48
|
+
try {
|
|
49
|
+
await riskyOperation();
|
|
50
|
+
} catch (caught) {
|
|
51
|
+
const error = toError(caught);
|
|
52
|
+
console.error(error.message);
|
|
53
|
+
}
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
## `isPromiseLike`
|