@venizia/ignis-docs 0.0.3 → 0.0.4-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +1 -1
  2. package/package.json +4 -2
  3. package/wiki/best-practices/api-usage-examples.md +591 -0
  4. package/wiki/best-practices/architectural-patterns.md +415 -0
  5. package/wiki/best-practices/architecture-decisions.md +488 -0
  6. package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +406 -17
  7. package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
  8. package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
  9. package/wiki/best-practices/data-modeling.md +376 -0
  10. package/wiki/best-practices/deployment-strategies.md +698 -0
  11. package/wiki/best-practices/index.md +27 -0
  12. package/wiki/best-practices/performance-optimization.md +196 -0
  13. package/wiki/best-practices/security-guidelines.md +218 -0
  14. package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
  15. package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
  16. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
  17. package/wiki/changelogs/2025-12-17-refactor.md +1 -1
  18. package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
  19. package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
  20. package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +2 -2
  21. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  22. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  23. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  24. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  25. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  26. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  27. package/wiki/changelogs/index.md +6 -0
  28. package/wiki/changelogs/planned-schema-migrator.md +0 -8
  29. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  30. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  31. package/wiki/guides/core-concepts/components-guide.md +509 -0
  32. package/wiki/{get-started → guides}/core-concepts/components.md +24 -17
  33. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  34. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  35. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  36. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  37. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  38. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  39. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  40. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  41. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  42. package/wiki/guides/get-started/philosophy.md +682 -0
  43. package/wiki/guides/get-started/setup.md +157 -0
  44. package/wiki/guides/index.md +89 -0
  45. package/wiki/guides/reference/glossary.md +243 -0
  46. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  47. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  48. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  49. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  50. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  51. package/wiki/guides/tutorials/testing.md +723 -0
  52. package/wiki/index.md +176 -37
  53. package/wiki/references/base/application.md +27 -0
  54. package/wiki/references/base/bootstrapping.md +31 -26
  55. package/wiki/references/base/components.md +24 -7
  56. package/wiki/references/base/controllers.md +50 -20
  57. package/wiki/references/base/datasources.md +30 -0
  58. package/wiki/references/base/dependency-injection.md +39 -3
  59. package/wiki/references/base/filter-system/application-usage.md +224 -0
  60. package/wiki/references/base/filter-system/array-operators.md +132 -0
  61. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  62. package/wiki/references/base/filter-system/default-filter.md +428 -0
  63. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  64. package/wiki/references/base/filter-system/index.md +127 -0
  65. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  66. package/wiki/references/base/filter-system/list-operators.md +71 -0
  67. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  68. package/wiki/references/base/filter-system/null-operators.md +58 -0
  69. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  70. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  71. package/wiki/references/base/filter-system/range-operators.md +63 -0
  72. package/wiki/references/base/filter-system/tips.md +190 -0
  73. package/wiki/references/base/filter-system/use-cases.md +452 -0
  74. package/wiki/references/base/index.md +90 -0
  75. package/wiki/references/base/middlewares.md +604 -0
  76. package/wiki/references/base/models.md +215 -23
  77. package/wiki/references/base/providers.md +731 -0
  78. package/wiki/references/base/repositories/advanced.md +555 -0
  79. package/wiki/references/base/repositories/index.md +228 -0
  80. package/wiki/references/base/repositories/mixins.md +331 -0
  81. package/wiki/references/base/repositories/relations.md +486 -0
  82. package/wiki/references/base/repositories.md +40 -635
  83. package/wiki/references/base/services.md +28 -4
  84. package/wiki/references/components/authentication.md +22 -2
  85. package/wiki/references/components/health-check.md +12 -0
  86. package/wiki/references/components/index.md +23 -0
  87. package/wiki/references/components/mail.md +687 -0
  88. package/wiki/references/components/request-tracker.md +16 -0
  89. package/wiki/references/components/socket-io.md +18 -0
  90. package/wiki/references/components/static-asset.md +14 -26
  91. package/wiki/references/components/swagger.md +17 -0
  92. package/wiki/references/configuration/environment-variables.md +427 -0
  93. package/wiki/references/configuration/index.md +73 -0
  94. package/wiki/references/helpers/cron.md +14 -0
  95. package/wiki/references/helpers/crypto.md +15 -0
  96. package/wiki/references/helpers/env.md +16 -0
  97. package/wiki/references/helpers/error.md +17 -0
  98. package/wiki/references/helpers/index.md +14 -0
  99. package/wiki/references/helpers/inversion.md +24 -4
  100. package/wiki/references/helpers/logger.md +19 -0
  101. package/wiki/references/helpers/network.md +11 -0
  102. package/wiki/references/helpers/queue.md +19 -0
  103. package/wiki/references/helpers/redis.md +21 -0
  104. package/wiki/references/helpers/socket-io.md +24 -5
  105. package/wiki/references/helpers/storage.md +18 -10
  106. package/wiki/references/helpers/testing.md +18 -0
  107. package/wiki/references/helpers/types.md +16 -0
  108. package/wiki/references/helpers/uid.md +167 -0
  109. package/wiki/references/helpers/worker-thread.md +16 -0
  110. package/wiki/references/index.md +177 -0
  111. package/wiki/references/quick-reference.md +634 -0
  112. package/wiki/references/src-details/boot.md +3 -3
  113. package/wiki/references/src-details/dev-configs.md +0 -4
  114. package/wiki/references/src-details/docs.md +2 -2
  115. package/wiki/references/src-details/index.md +86 -0
  116. package/wiki/references/src-details/inversion.md +1 -6
  117. package/wiki/references/src-details/mcp-server.md +3 -15
  118. package/wiki/references/utilities/index.md +86 -10
  119. package/wiki/references/utilities/jsx.md +577 -0
  120. package/wiki/references/utilities/request.md +0 -2
  121. package/wiki/references/utilities/statuses.md +740 -0
  122. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  123. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  124. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  125. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  126. package/wiki/get-started/best-practices/performance-optimization.md +0 -97
  127. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  128. package/wiki/get-started/core-concepts/persistent.md +0 -539
  129. package/wiki/get-started/index.md +0 -65
  130. package/wiki/get-started/philosophy.md +0 -296
  131. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,196 @@
1
+ # Performance Optimization
2
+
3
+ Optimize your Ignis application for speed and scalability.
4
+
5
+ ## 1. Measure Performance
6
+
7
+ Identify bottlenecks before optimizing:
8
+
9
+ ```typescript
10
+ import { executeWithPerformanceMeasure } from '@venizia/ignis';
11
+
12
+ await executeWithPerformanceMeasure({
13
+ logger: this.logger,
14
+ scope: 'DataProcessing',
15
+ description: 'Process large dataset',
16
+ task: async () => {
17
+ await processLargeDataset();
18
+ },
19
+ });
20
+ ```
21
+
22
+ Logs execution time automatically.
23
+
24
+ > **Deep Dive:** See [Performance Utility](../references/utilities/performance.md) for advanced profiling.
25
+
26
+ ## 2. Offload CPU-Intensive Tasks
27
+
28
+ Prevent blocking the event loop with Worker Threads:
29
+
30
+ **Use Worker Threads for:**
31
+ - Complex calculations or crypto operations
32
+ - Large file/data processing
33
+ - Any synchronous task > 5ms
34
+
35
+ > **Deep Dive:** See [Worker Thread Helper](../references/helpers/worker-thread.md) for implementation guide.
36
+
37
+ ## 3. Optimize Database Queries
38
+
39
+ | Technique | Example | Impact |
40
+ |-----------|---------|--------|
41
+ | **Select specific fields** | `fields: { id: true, name: true }` | Reduce data transfer |
42
+ | **Use indexes** | Create indexes on WHERE/JOIN columns | 10-100x faster queries |
43
+ | **Mandatory Limit** | `limit: 20` | Prevent fetching massive datasets |
44
+ | **Paginate results** | `limit: 20, offset: 0` | Prevent memory overflow |
45
+ | **Eager load relations** | `include: [{ relation: 'creator' }]` | Solve N+1 problem |
46
+
47
+ ### Query Operators Reference
48
+
49
+ Ignis supports extensive query operators for filtering:
50
+
51
+ | Operator | Description | Example |
52
+ |----------|-------------|---------|
53
+ | `eq` | Equal (handles null) | `{ status: { eq: 'ACTIVE' } }` |
54
+ | `ne`, `neq` | Not equal | `{ status: { ne: 'DELETED' } }` |
55
+ | `gt`, `gte` | Greater than (or equal) | `{ age: { gte: 18 } }` |
56
+ | `lt`, `lte` | Less than (or equal) | `{ price: { lt: 100 } }` |
57
+ | `like` | SQL LIKE (case-sensitive) | `{ name: { like: '%john%' } }` |
58
+ | `ilike` | Case-insensitive LIKE | `{ email: { ilike: '%@gmail%' } }` |
59
+ | `nlike`, `nilike` | NOT LIKE variants | `{ name: { nlike: '%test%' } }` |
60
+ | `regexp` | PostgreSQL regex (`~`) | `{ code: { regexp: '^[A-Z]+$' } }` |
61
+ | `iregexp` | Case-insensitive regex (`~*`) | `{ name: { iregexp: '^john' } }` |
62
+ | `in`, `inq` | Value in array | `{ status: { in: ['A', 'B'] } }` |
63
+ | `nin` | Value NOT in array | `{ role: { nin: ['guest'] } }` |
64
+ | `between` | Range (inclusive) | `{ age: { between: [18, 65] } }` |
65
+ | `is`, `isn` | IS NULL / IS NOT NULL | `{ deletedAt: { is: null } }` |
66
+ | `and`, `or` | Logical operators | `{ or: [{ a: 1 }, { b: 2 }] }` |
67
+
68
+ **Complex Filter Example:**
69
+
70
+ ```typescript
71
+ await repo.find({
72
+ filter: {
73
+ where: {
74
+ and: [
75
+ { status: { in: ['ACTIVE', 'PENDING'] } },
76
+ { createdAt: { gte: new Date('2024-01-01') } },
77
+ { or: [
78
+ { role: 'admin' },
79
+ { permissions: { ilike: '%manage%' } }
80
+ ]}
81
+ ]
82
+ },
83
+ limit: 50,
84
+ },
85
+ });
86
+ ```
87
+
88
+ ### JSON Path Filtering
89
+
90
+ Filter by nested JSON/JSONB fields using PostgreSQL's `#>` operator:
91
+
92
+ ```typescript
93
+ // Order by nested JSON path
94
+ await repo.find({
95
+ filter: {
96
+ order: ['metadata.nested[0].field ASC'],
97
+ },
98
+ });
99
+
100
+ // The framework uses PostgreSQL #> operator for path extraction
101
+ // metadata #> '{nested,0,field}'
102
+ ```
103
+
104
+ > [!TIP]
105
+ > **Avoid Deep Nesting:** While Ignis supports deeply nested `include` filters, each level adds significant overhead to query construction and result mapping. We strongly recommend a **maximum of 2 levels** (e.g., `User -> Orders -> Items`). For more complex data fetching, consider separate queries.
106
+
107
+ **Example:**
108
+ ```typescript
109
+ await userRepository.find({
110
+ filter: {
111
+ fields: { id: true, name: true, email: true }, // ✅ Specific fields
112
+ where: { status: 'ACTIVE' },
113
+ limit: 20, // ✅ Mandatory limit
114
+ include: [{
115
+ relation: 'orders',
116
+ scope: {
117
+ include: [{ relation: 'items' }] // ✅ Level 2 (Recommended limit)
118
+ }
119
+ }],
120
+ },
121
+ });
122
+ ```
123
+
124
+ ## 4. Implement Caching
125
+
126
+ Reduce database load with caching:
127
+
128
+ | Cache Type | Use Case | Implementation |
129
+ |-----------|----------|----------------|
130
+ | **Redis** | Distributed cache, session storage | [Redis Helper](../references/helpers/redis.md) |
131
+ | **In-Memory** | Single-process cache | `MemoryStorageHelper` |
132
+
133
+ **Example:**
134
+ ```typescript
135
+ // Cache expensive query results
136
+ const cached = await redis.get('users:active');
137
+ if (!cached) {
138
+ const users = await userRepository.find({ where: { active: true } });
139
+ await redis.set('users:active', users, 300); // 5 min TTL
140
+ }
141
+ ```
142
+
143
+ ## 5. Production Settings
144
+
145
+ | Setting | Value | Why |
146
+ |---------|-------|-----|
147
+ | `NODE_ENV` | `production` | Enables library optimizations |
148
+ | Process Manager | PM2, systemd, Docker | Auto-restart, cluster mode |
149
+ | Cluster Mode | CPU cores | Utilize all CPUs |
150
+
151
+ **PM2 Cluster Mode:**
152
+ ```bash
153
+ pm2 start dist/index.js -i max # Use all CPU cores
154
+ ```
155
+
156
+ ## 6. Transaction Support
157
+
158
+ Use transactions to ensure atomicity across multiple database operations:
159
+
160
+ ```typescript
161
+ // Start a transaction
162
+ const tx = await userRepository.beginTransaction({
163
+ isolationLevel: 'READ COMMITTED', // or 'REPEATABLE READ' | 'SERIALIZABLE'
164
+ });
165
+
166
+ try {
167
+ // Pass transaction to all operations
168
+ const user = await userRepository.create({
169
+ data: { name: 'John', email: 'john@example.com' },
170
+ options: { transaction: tx },
171
+ });
172
+
173
+ await orderRepository.create({
174
+ data: { userId: user.data.id, total: 100 },
175
+ options: { transaction: tx },
176
+ });
177
+
178
+ // Commit if all operations succeed
179
+ await tx.commit();
180
+ } catch (error) {
181
+ // Rollback on any failure
182
+ await tx.rollback();
183
+ throw error;
184
+ }
185
+ ```
186
+
187
+ **Transaction Isolation Levels:**
188
+
189
+ | Level | Description | Use Case |
190
+ |-------|-------------|----------|
191
+ | `READ COMMITTED` | See committed data only (default) | Most CRUD operations |
192
+ | `REPEATABLE READ` | Consistent reads within transaction | Reports, aggregations |
193
+ | `SERIALIZABLE` | Full isolation, may retry | Financial transactions |
194
+
195
+ > [!WARNING]
196
+ > Higher isolation levels reduce concurrency. Use `READ COMMITTED` unless you have specific consistency requirements.
@@ -0,0 +1,218 @@
1
+ # Security Guidelines
2
+
3
+ Critical security practices to protect your Ignis application.
4
+
5
+ ## 1. Secret Management
6
+
7
+ **Never hard-code secrets.** Use environment variables for all sensitive data.
8
+
9
+ | Environment | Where to Store Secrets |
10
+ |-------------|----------------------|
11
+ | Development | `.env` file (add to `.gitignore`) |
12
+ | Production | Cloud provider's secret manager (AWS Secrets Manager, Azure Key Vault, etc.) |
13
+
14
+ **Example `.env`:**
15
+ ```bash
16
+ APP_ENV_APPLICATION_SECRET=your_strong_random_secret_here
17
+ APP_ENV_JWT_SECRET=another_strong_random_secret_here
18
+ APP_ENV_POSTGRES_PASSWORD=database_password_here
19
+ ```
20
+
21
+ **Generate strong secrets:**
22
+ ```bash
23
+ node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
24
+ ```
25
+
26
+ ## 2. Input Validation
27
+
28
+ **Always validate incoming data** with Zod schemas. Ignis automatically rejects invalid requests.
29
+
30
+ ```typescript
31
+ import { z } from '@hono/zod-openapi';
32
+ import { jsonContent, jsonResponse } from '@venizia/ignis';
33
+
34
+ const CreateUserRoute = {
35
+ method: 'post',
36
+ path: '/users',
37
+ request: {
38
+ body: jsonContent({
39
+ schema: z.object({
40
+ email: z.string().email(), // Valid email
41
+ age: z.number().int().min(18), // Adult only
42
+ role: z.enum(['user', 'admin']), // Whitelist
43
+ }),
44
+ }),
45
+ },
46
+ responses: jsonResponse({ /* ... */ }),
47
+ } as const;
48
+ ```
49
+
50
+ **Validation happens automatically** - invalid requests never reach your handler.
51
+
52
+ ## 3. Authentication & Authorization
53
+
54
+ Protect sensitive endpoints with `AuthenticateComponent`.
55
+
56
+ **Setup:**
57
+ ```typescript
58
+ // application.ts
59
+ this.component(AuthenticateComponent);
60
+ ```
61
+
62
+ **Protect routes:**
63
+ ```typescript
64
+ const SecureRoute = {
65
+ path: '/admin/users',
66
+ authStrategies: [Authentication.STRATEGY_JWT], // Requires JWT
67
+ // ...
68
+ };
69
+ ```
70
+
71
+ > **Deep Dive:** See [Authentication Component](../references/components/authentication.md) for full setup guide.
72
+
73
+ **Access user in protected routes:**
74
+ ```typescript
75
+ import { Authentication, IJWTTokenPayload, ApplicationError, getError } from '@venizia/ignis';
76
+
77
+ const user = c.get(Authentication.CURRENT_USER) as IJWTTokenPayload;
78
+ if (!user.roles.includes('admin')) {
79
+ throw getError({ statusCode: 403, message: 'Forbidden' });
80
+ }
81
+ ```
82
+
83
+ ## 4. Protecting Sensitive Data with Hidden Properties
84
+
85
+ Configure model properties that should **never be returned** through repository queries. Hidden properties are excluded at the SQL level - they never leave the database.
86
+
87
+ ```typescript
88
+ @model({
89
+ type: 'entity',
90
+ settings: {
91
+ hiddenProperties: ['password', 'apiSecret', 'internalToken'],
92
+ },
93
+ })
94
+ export class User extends BaseEntity<typeof User.schema> {
95
+ static override schema = pgTable('User', {
96
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
97
+ email: text('email').notNull(),
98
+ password: text('password'), // Never returned via repository
99
+ apiSecret: text('api_secret'), // Never returned via repository
100
+ });
101
+ }
102
+ ```
103
+
104
+ **Why SQL-level exclusion matters:**
105
+
106
+ | Approach | Security Level | Data Exposure Risk |
107
+ |----------|---------------|-------------------|
108
+ | Post-query filtering (JS) | Low | Data passes through network/memory |
109
+ | **SQL-level exclusion** | **High** | **Data never leaves database** |
110
+
111
+ **When you legitimately need hidden data** (e.g., password verification), use the connector directly:
112
+
113
+ ```typescript
114
+ // For authentication - access password via connector
115
+ const connector = userRepo.getConnector();
116
+ const [user] = await connector
117
+ .select({ id: User.schema.id, password: User.schema.password })
118
+ .from(User.schema)
119
+ .where(eq(User.schema.email, email));
120
+ ```
121
+
122
+ > **Reference:** See [Hidden Properties](../references/base/models.md#hidden-properties) for complete documentation.
123
+
124
+ ## 5. File Upload Security
125
+
126
+ When handling file uploads, prevent **path traversal attacks** and ensure safe file handling.
127
+
128
+ ### Path Traversal Prevention
129
+
130
+ **Problem:** Malicious filenames like `../../../etc/passwd` can write files outside intended directories.
131
+
132
+ **Solution:** Use `sanitizeFilename()` to strip dangerous patterns:
133
+
134
+ ```typescript
135
+ import { sanitizeFilename } from '@venizia/ignis';
136
+
137
+ // ❌ DANGEROUS - User-controlled filename
138
+ const unsafeFilename = req.body.filename; // Could be "../../../etc/passwd"
139
+ fs.writeFileSync(`./uploads/${unsafeFilename}`, data);
140
+
141
+ // ✅ SAFE - Sanitized filename
142
+ const safeFilename = sanitizeFilename(req.body.filename);
143
+ fs.writeFileSync(`./uploads/${safeFilename}`, data);
144
+ ```
145
+
146
+ **What `sanitizeFilename()` does:**
147
+ - Extracts basename (removes directory paths)
148
+ - Removes dangerous characters (`../`, special chars)
149
+ - Replaces consecutive dots with single dot
150
+ - Returns `'download'` for empty/suspicious patterns
151
+
152
+ ### Safe File Download Headers
153
+
154
+ Use `createContentDispositionHeader()` for secure download responses:
155
+
156
+ ```typescript
157
+ import { createContentDispositionHeader, sanitizeFilename } from '@venizia/ignis';
158
+
159
+ async downloadFile(c: Context) {
160
+ const filename = sanitizeFilename(c.req.param('filename'));
161
+ const fileBuffer = await fs.readFile(`./uploads/${filename}`);
162
+
163
+ return new Response(fileBuffer, {
164
+ headers: {
165
+ 'Content-Type': 'application/octet-stream',
166
+ 'Content-Disposition': createContentDispositionHeader({
167
+ filename: filename,
168
+ type: 'attachment',
169
+ }),
170
+ },
171
+ });
172
+ }
173
+ ```
174
+
175
+ ### Built-in Multipart Parsing
176
+
177
+ Use `parseMultipartBody()` for safe file uploads with automatic sanitization:
178
+
179
+ ```typescript
180
+ import { parseMultipartBody } from '@venizia/ignis';
181
+
182
+ async uploadFile(c: Context) {
183
+ const files = await parseMultipartBody({
184
+ context: c,
185
+ storage: 'disk', // or 'memory' for buffer
186
+ uploadDir: './uploads', // Target directory
187
+ });
188
+
189
+ // Files are saved with sanitized names: timestamp-random-sanitized_name.ext
190
+ return c.json({ uploaded: files.map(f => f.filename) });
191
+ }
192
+ ```
193
+
194
+ **Security features:**
195
+ - Automatic filename sanitization
196
+ - Creates upload directory if missing
197
+ - Generates unique filenames (prevents overwrites)
198
+ - Returns file metadata (size, mimetype) for validation
199
+
200
+ > **Reference:** See [Request Utility](../references/utilities/request.md) for full API documentation.
201
+
202
+ ## 6. Secure Dependencies
203
+
204
+ Regularly audit and update dependencies:
205
+
206
+ ```bash
207
+ # Check for vulnerabilities
208
+ bun audit
209
+
210
+ # Update dependencies
211
+ bun update
212
+ ```
213
+
214
+ **Critical packages to keep updated:**
215
+ - `hono` - Web framework
216
+ - `jose` - JWT handling
217
+ - `drizzle-orm` - Database ORM
218
+ - `@venizia/ignis` - Framework core
@@ -97,4 +97,100 @@ cat .env | grep APP_ENV
97
97
  - Use `try-catch` blocks to catch and log errors
98
98
  - Check database queries with Drizzle's logging: `{ logger: true }`
99
99
 
100
- > **Deep Dive:** See [Logger Helper](../../references/helpers/logger.md) for advanced logging configuration.
100
+ > **Deep Dive:** See [Logger Helper](../references/helpers/logger.md) for advanced logging configuration.
101
+
102
+ ## 6. Request ID Tracking
103
+
104
+ Every request in Ignis is automatically assigned a unique `requestId` for log correlation. The `RequestSpyMiddleware` logs this ID at the start and end of each request.
105
+
106
+ **Log output format:**
107
+ ```
108
+ [spy][abc123] START | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET
109
+ [spy][abc123] DONE | Handling Request | forwardedIp: 192.168.1.1 | path: /api/users | method: GET | Took: 45.2 (ms)
110
+ ```
111
+
112
+ **Access request ID in handlers:**
113
+ ```typescript
114
+ import { RequestSpyMiddleware } from '@venizia/ignis';
115
+
116
+ // Inside a controller method
117
+ async getUser(c: Context) {
118
+ const requestId = c.get(RequestSpyMiddleware.REQUEST_ID_KEY);
119
+ this.logger.info('[%s] Processing user request', requestId);
120
+ // ...
121
+ }
122
+ ```
123
+
124
+ **Filtering logs by request:**
125
+ ```bash
126
+ # Find all logs for a specific request
127
+ grep "abc123" logs/app.log
128
+
129
+ # Extract request timing
130
+ grep "\[spy\]\[abc123\]" logs/app.log
131
+ ```
132
+
133
+ **Why this matters:**
134
+ - Correlate logs across services in distributed systems
135
+ - Debug specific user issues by their request ID
136
+ - Measure request duration from START to DONE timestamps
137
+
138
+ ## 7. Validation Error Debugging
139
+
140
+ When Zod validation fails, Ignis returns a structured error response. Understanding this format helps debug client-side issues.
141
+
142
+ **Error response structure:**
143
+ ```json
144
+ {
145
+ "statusCode": 422,
146
+ "message": "ValidationError",
147
+ "requestId": "abc123",
148
+ "details": {
149
+ "cause": [
150
+ {
151
+ "path": "email",
152
+ "message": "Invalid email",
153
+ "code": "invalid_string",
154
+ "expected": "email",
155
+ "received": "string"
156
+ }
157
+ ]
158
+ }
159
+ }
160
+ ```
161
+
162
+ **Common validation error codes:**
163
+
164
+ | Code | Meaning | Example |
165
+ |------|---------|---------|
166
+ | `invalid_type` | Wrong data type | Expected `number`, got `string` |
167
+ | `invalid_string` | String format invalid | Invalid email or UUID format |
168
+ | `too_small` | Value below minimum | String shorter than min length |
169
+ | `too_big` | Value above maximum | Number exceeds max value |
170
+ | `invalid_enum_value` | Value not in enum | Status must be 'ACTIVE' or 'INACTIVE' |
171
+ | `unrecognized_keys` | Extra fields in request | Strict schema rejects unknown fields |
172
+
173
+ **Debugging tips:**
174
+
175
+ 1. **Check the `path` field** - Shows which field failed validation
176
+ 2. **Compare `expected` vs `received`** - Identifies type mismatches
177
+ 3. **Review schema definition** - Ensure client sends correct format
178
+
179
+ **Example: Debugging nested validation errors:**
180
+ ```json
181
+ {
182
+ "details": {
183
+ "cause": [
184
+ {
185
+ "path": "address.zipCode",
186
+ "message": "Expected string, received number",
187
+ "code": "invalid_type",
188
+ "expected": "string",
189
+ "received": "number"
190
+ }
191
+ ]
192
+ }
193
+ }
194
+ ```
195
+
196
+ The `path` uses dot notation for nested objects. Here, `address.zipCode` means the `zipCode` field inside the `address` object is invalid.
@@ -142,4 +142,4 @@ src/
142
142
 
143
143
  ## No Breaking Changes
144
144
 
145
- This document describes the initial state of the architecture.
145
+ This document describes the initial state of the architecture.
@@ -297,4 +297,4 @@ export class PostgresDataSource extends BaseDataSource {
297
297
  });
298
298
  }
299
299
  }
300
- ```
300
+ ```
@@ -87,4 +87,4 @@ If you are using the DI system directly, add the new package to your dependencie
87
87
 
88
88
  ```bash
89
89
  npm install @venizia/ignis-inversion
90
- ```
90
+ ```
@@ -11,7 +11,7 @@ This update focuses on performance improvements for the repository layer, reduci
11
11
 
12
12
  ## Overview
13
13
 
14
- - **WeakMap Cache**: `DrizzleFilterBuilder` now caches `getTableColumns()` results.
14
+ - **WeakMap Cache**: `FilterBuilder` now caches `getTableColumns()` results.
15
15
  - **Core API for Flat Queries**: `ReadableRepository` uses faster Drizzle Core API when possible.
16
16
  - **Static schemaFactory Singleton**: `BaseEntity` shares a single `schemaFactory` instance across all entities.
17
17
  - **Async/Await Refactor**: Removed redundant Promise wrappers from repository methods.
@@ -27,7 +27,7 @@ This update focuses on performance improvements for the repository layer, reduci
27
27
  **Solution:** Added static WeakMap cache that stores column metadata per schema.
28
28
 
29
29
  ```typescript
30
- export class DrizzleFilterBuilder extends BaseHelper {
30
+ export class FilterBuilder extends BaseHelper {
31
31
  // Static cache shared across all instances
32
32
  private static columnCache = new WeakMap<
33
33
  TTableSchemaWithId,
@@ -35,10 +35,10 @@ export class DrizzleFilterBuilder extends BaseHelper {
35
35
  >();
36
36
 
37
37
  private getColumns<Schema extends TTableSchemaWithId>(schema: Schema) {
38
- let columns = DrizzleFilterBuilder.columnCache.get(schema);
38
+ let columns = FilterBuilder.columnCache.get(schema);
39
39
  if (!columns) {
40
40
  columns = getTableColumns(schema);
41
- DrizzleFilterBuilder.columnCache.set(schema, columns);
41
+ FilterBuilder.columnCache.set(schema, columns);
42
42
  }
43
43
  return columns;
44
44
  }
@@ -127,4 +127,4 @@ return { count };
127
127
 
128
128
  ## No Breaking Changes
129
129
 
130
- All changes are internal optimizations. No API changes or migration required.
130
+ All changes are internal optimizations. No API changes or migration required.
@@ -16,7 +16,7 @@ This update adds strict validation to the `@repository` decorator and fixes seve
16
16
  - **DataSource Auto-Discovery**: Schema is automatically built from `@repository` bindings.
17
17
  - **Filter Security**: Fixed empty IN array bypass, invalid column handling, BETWEEN validation.
18
18
  - **PostgreSQL Compatibility**: REGEXP now uses PostgreSQL POSIX operators.
19
- - **UUID Generation**: Now uses native PostgreSQL `uuid` type with `gen_random_uuid()`.
19
+ - **String ID Generation**: Uses `text` column with customizable ID generator (default: `crypto.randomUUID()`).
20
20
 
21
21
  ## Breaking Changes
22
22
 
@@ -114,18 +114,24 @@ export class PostgresDataSource extends BaseDataSource<...> {
114
114
  }
115
115
  ```
116
116
 
117
- ### UUID Type Improvement
117
+ ### String ID with Custom Generator
118
118
 
119
119
  **File:** `packages/core/src/base/models/enrichers/id.enricher.ts`
120
120
 
121
- **Problem:** JS-generated UUIDs were stored as text, which is less efficient.
121
+ **Problem:** Need flexible ID generation with maximum database compatibility.
122
122
 
123
- **Solution:** Changed to native PostgreSQL `uuid` type with `gen_random_uuid()`.
123
+ **Solution:** Uses `text` column with customizable ID generator (default: `crypto.randomUUID()`).
124
124
 
125
125
  ```typescript
126
- // After: uuid('id').defaultRandom().primaryKey()
126
+ // Default: text('id').primaryKey().$defaultFn(() => crypto.randomUUID())
127
+ // Custom: text('id').primaryKey().$defaultFn(() => nanoid())
127
128
  ```
128
129
 
130
+ **Benefits:**
131
+ - Maximum database compatibility with `text` column type
132
+ - Customizable ID generation (UUID, nanoid, cuid, etc.)
133
+ - Application-level ID generation via `$defaultFn()`
134
+
129
135
  ### Case-Insensitive REGEXP (IREGEXP)
130
136
 
131
137
  **File:** `packages/core/src/base/repositories/operators/query.ts`
@@ -188,7 +194,7 @@ await repo.find({ filter: { where: { age: { BETWEEN: [10] } } } });
188
194
  |------|---------|
189
195
  | `src/base/models/base.ts` | Static schema/relations support, IEntity interface |
190
196
  | `src/base/models/common/types.ts` | IEntity interface definition |
191
- | `src/base/models/enrichers/id.enricher.ts` | Native PostgreSQL UUID type |
197
+ | `src/base/models/enrichers/id.enricher.ts` | Text column with customizable ID generator |
192
198
  | `src/base/repositories/core/base.ts` | Constructor signature change, relations auto-resolution |
193
199
  | `src/base/repositories/core/readable.ts` | Constructor change, getQueryInterface validation |
194
200
  | `src/base/repositories/core/persistable.ts` | Constructor change, log option, TypeScript overloads |
@@ -246,4 +252,4 @@ Replace MySQL-style REGEXP with PostgreSQL syntax:
246
252
  - `REGEXP` → uses `~` (case-sensitive)
247
253
  - `IREGEXP` → uses `~*` (case-insensitive)
248
254
 
249
- ```
255
+ ```
@@ -13,7 +13,7 @@ This update introduces support for deeply nested relation queries in repositorie
13
13
 
14
14
  - **Nested Inclusions**: `include` filters now work recursively to any depth.
15
15
  - **Generic Repository Methods**: `find<R>`, `findOne<R>`, etc. now support custom return types.
16
- - **DrizzleFilterBuilder Decoupling**: Decoupled filter builder from MetadataRegistry for cleaner architecture.
16
+ - **FilterBuilder Decoupling**: Decoupled filter builder from MetadataRegistry for cleaner architecture.
17
17
 
18
18
  ## New Features
19
19
 
@@ -21,7 +21,7 @@ This update introduces support for deeply nested relation queries in repositorie
21
21
 
22
22
  **File:** `packages/core/src/base/repositories/operators/filter.ts`
23
23
 
24
- **Problem:** Previously, the `DrizzleFilterBuilder` could only resolve relations for the root entity. Nested includes (e.g., `include: [{ relation: 'a', scope: { include: [{ relation: 'b' }] } }]`) failed because it didn't know the schema of relation 'a'.
24
+ **Problem:** Previously, the `FilterBuilder` could only resolve relations for the root entity. Nested includes (e.g., `include: [{ relation: 'a', scope: { include: [{ relation: 'b' }] } }]`) failed because it didn't know the schema of relation 'a'.
25
25
 
26
26
  **Solution:** The builder now accepts a `relationResolver` function (injected from the Repository) which allows it to dynamically lookup schemas and relations for any entity during recursive traversal.
27
27