@venizia/ignis-docs 0.0.2 → 0.0.4-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) 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 +647 -182
  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 +86 -0
  21. package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
  22. package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
  23. package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
  24. package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
  25. package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
  26. package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
  27. package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
  28. package/wiki/changelogs/index.md +8 -1
  29. package/wiki/changelogs/planned-schema-migrator.md +2 -10
  30. package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
  31. package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
  32. package/wiki/guides/core-concepts/components-guide.md +509 -0
  33. package/wiki/guides/core-concepts/components.md +122 -0
  34. package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
  35. package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
  36. package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
  37. package/wiki/guides/core-concepts/persistent/index.md +119 -0
  38. package/wiki/guides/core-concepts/persistent/models.md +241 -0
  39. package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
  40. package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
  41. package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
  42. package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
  43. package/wiki/guides/get-started/philosophy.md +682 -0
  44. package/wiki/guides/get-started/setup.md +157 -0
  45. package/wiki/guides/index.md +89 -0
  46. package/wiki/guides/reference/glossary.md +243 -0
  47. package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
  48. package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
  49. package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
  50. package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
  51. package/wiki/guides/tutorials/realtime-chat.md +1261 -0
  52. package/wiki/guides/tutorials/testing.md +723 -0
  53. package/wiki/index.md +176 -37
  54. package/wiki/references/base/application.md +27 -0
  55. package/wiki/references/base/bootstrapping.md +30 -26
  56. package/wiki/references/base/components.md +532 -31
  57. package/wiki/references/base/controllers.md +136 -38
  58. package/wiki/references/base/datasources.md +108 -5
  59. package/wiki/references/base/dependency-injection.md +39 -3
  60. package/wiki/references/base/filter-system/application-usage.md +224 -0
  61. package/wiki/references/base/filter-system/array-operators.md +132 -0
  62. package/wiki/references/base/filter-system/comparison-operators.md +109 -0
  63. package/wiki/references/base/filter-system/default-filter.md +428 -0
  64. package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
  65. package/wiki/references/base/filter-system/index.md +127 -0
  66. package/wiki/references/base/filter-system/json-filtering.md +197 -0
  67. package/wiki/references/base/filter-system/list-operators.md +71 -0
  68. package/wiki/references/base/filter-system/logical-operators.md +156 -0
  69. package/wiki/references/base/filter-system/null-operators.md +58 -0
  70. package/wiki/references/base/filter-system/pattern-matching.md +108 -0
  71. package/wiki/references/base/filter-system/quick-reference.md +431 -0
  72. package/wiki/references/base/filter-system/range-operators.md +63 -0
  73. package/wiki/references/base/filter-system/tips.md +190 -0
  74. package/wiki/references/base/filter-system/use-cases.md +452 -0
  75. package/wiki/references/base/index.md +90 -0
  76. package/wiki/references/base/middlewares.md +602 -0
  77. package/wiki/references/base/models.md +215 -23
  78. package/wiki/references/base/providers.md +732 -0
  79. package/wiki/references/base/repositories/advanced.md +555 -0
  80. package/wiki/references/base/repositories/index.md +228 -0
  81. package/wiki/references/base/repositories/mixins.md +331 -0
  82. package/wiki/references/base/repositories/relations.md +486 -0
  83. package/wiki/references/base/repositories.md +40 -549
  84. package/wiki/references/base/services.md +28 -4
  85. package/wiki/references/components/authentication.md +22 -2
  86. package/wiki/references/components/health-check.md +12 -0
  87. package/wiki/references/components/index.md +23 -0
  88. package/wiki/references/components/mail.md +687 -0
  89. package/wiki/references/components/request-tracker.md +16 -0
  90. package/wiki/references/components/socket-io.md +18 -0
  91. package/wiki/references/components/static-asset.md +14 -26
  92. package/wiki/references/components/swagger.md +17 -0
  93. package/wiki/references/configuration/environment-variables.md +427 -0
  94. package/wiki/references/configuration/index.md +73 -0
  95. package/wiki/references/helpers/cron.md +14 -0
  96. package/wiki/references/helpers/crypto.md +15 -0
  97. package/wiki/references/helpers/env.md +16 -0
  98. package/wiki/references/helpers/error.md +17 -0
  99. package/wiki/references/helpers/index.md +15 -0
  100. package/wiki/references/helpers/inversion.md +24 -4
  101. package/wiki/references/helpers/logger.md +19 -0
  102. package/wiki/references/helpers/network.md +11 -0
  103. package/wiki/references/helpers/queue.md +19 -0
  104. package/wiki/references/helpers/redis.md +21 -0
  105. package/wiki/references/helpers/socket-io.md +24 -5
  106. package/wiki/references/helpers/storage.md +18 -10
  107. package/wiki/references/helpers/testing.md +18 -0
  108. package/wiki/references/helpers/types.md +167 -0
  109. package/wiki/references/helpers/uid.md +167 -0
  110. package/wiki/references/helpers/worker-thread.md +16 -0
  111. package/wiki/references/index.md +177 -0
  112. package/wiki/references/quick-reference.md +634 -0
  113. package/wiki/references/src-details/boot.md +3 -3
  114. package/wiki/references/src-details/dev-configs.md +0 -4
  115. package/wiki/references/src-details/docs.md +2 -2
  116. package/wiki/references/src-details/index.md +86 -0
  117. package/wiki/references/src-details/inversion.md +1 -6
  118. package/wiki/references/src-details/mcp-server.md +3 -15
  119. package/wiki/references/utilities/index.md +86 -10
  120. package/wiki/references/utilities/jsx.md +577 -0
  121. package/wiki/references/utilities/request.md +0 -2
  122. package/wiki/references/utilities/statuses.md +740 -0
  123. package/wiki/changelogs/planned-transaction-support.md +0 -216
  124. package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
  125. package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
  126. package/wiki/get-started/best-practices/data-modeling.md +0 -177
  127. package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
  128. package/wiki/get-started/best-practices/performance-optimization.md +0 -88
  129. package/wiki/get-started/best-practices/security-guidelines.md +0 -99
  130. package/wiki/get-started/core-concepts/components.md +0 -98
  131. package/wiki/get-started/core-concepts/persistent.md +0 -543
  132. package/wiki/get-started/index.md +0 -65
  133. package/wiki/get-started/philosophy.md +0 -296
  134. package/wiki/get-started/prerequisites.md +0 -113
@@ -0,0 +1,86 @@
1
+ ---
2
+ title: Nested Relations & Generic Types
3
+ description: Added support for deep nested inclusions and generic return types in repositories
4
+ ---
5
+
6
+ # Changelog - 2025-12-26
7
+
8
+ ## Nested Relations Support & Generic Repository Types
9
+
10
+ This update introduces support for deeply nested relation queries in repositories and enables stronger type safety via generic return types.
11
+
12
+ ## Overview
13
+
14
+ - **Nested Inclusions**: `include` filters now work recursively to any depth.
15
+ - **Generic Repository Methods**: `find<R>`, `findOne<R>`, etc. now support custom return types.
16
+ - **FilterBuilder Decoupling**: Decoupled filter builder from MetadataRegistry for cleaner architecture.
17
+
18
+ ## New Features
19
+
20
+ ### Nested Relations Support
21
+
22
+ **File:** `packages/core/src/base/repositories/operators/filter.ts`
23
+
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
+
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
+
28
+ ```typescript
29
+ // Now works recursively!
30
+ const result = await repo.findOne({
31
+ filter: {
32
+ include: [{
33
+ relation: 'orders',
34
+ scope: {
35
+ include: [{ relation: 'items' }] // Level 2
36
+ }
37
+ }]
38
+ }
39
+ });
40
+ ```
41
+
42
+ ### Generic Repository Methods
43
+
44
+ **File:** `packages/core/src/base/repositories/common/types.ts`
45
+
46
+ **Problem:** Repository methods like `findOne` were hardcoded to return the base `DataObject`. When using `include`, the return type didn't reflect the added relations, forcing users to use `as any` or unsafe casts.
47
+
48
+ **Solution:** All read and write methods now accept a generic type parameter `<R>` which defaults to the entity's schema but can be overridden.
49
+
50
+ ```typescript
51
+ // Define expected shape
52
+ type UserWithPosts = User & { posts: Post[] };
53
+
54
+ // Type-safe call
55
+ const user = await userRepo.findOne<UserWithPosts>({
56
+ filter: { include: [{ relation: 'posts' }] }
57
+ });
58
+
59
+ // TypeScript knows 'posts' exists!
60
+ console.log(user?.posts.length);
61
+ ```
62
+
63
+ **Benefits:**
64
+ - Stronger type safety in application code.
65
+ - Reduces need for `as any` casting.
66
+ - Better IDE auto-completion.
67
+
68
+ ## Files Changed
69
+
70
+ ### Core Package (`packages/core`)
71
+
72
+ | File | Changes |
73
+ |------|---------|
74
+ | `src/base/repositories/common/types.ts` | Added generic `<R>` to all repository interface methods |
75
+ | `src/base/repositories/core/base.ts` | Implemented `getRelationResolver` and updated abstract signatures |
76
+ | `src/base/repositories/core/readable.ts` | Updated `find`, `findOne`, `findById` to support generics |
77
+ | `src/base/repositories/core/persistable.ts` | Updated `create`, `update`, `delete` methods to support generics |
78
+ | `src/base/repositories/operators/filter.ts` | Added `relationResolver` support for recursive includes |
79
+
80
+ ## Migration Guide
81
+
82
+ ### No Breaking Changes
83
+
84
+ This update is fully backward compatible.
85
+ - Existing calls to repository methods will continue to work (using the default `DataObject` return type).
86
+ - The new generic type parameter is optional.
@@ -0,0 +1,57 @@
1
+ ---
2
+ title: Transaction Support
3
+ description: Explicit transaction support for atomic operations across repositories
4
+ ---
5
+
6
+ # Changelog - 2025-12-26
7
+
8
+ ## Transaction Support
9
+
10
+ Added explicit transaction support to the repository layer, enabling atomic operations across multiple services and repositories.
11
+
12
+ ## Overview
13
+
14
+ - **Explicit Transactions**: `beginTransaction()` method on repositories.
15
+ - **Isolation Levels**: Support for `READ COMMITTED`, `REPEATABLE READ`, and `SERIALIZABLE`.
16
+ - **Pass-through Context**: Transaction objects can be passed via `options`.
17
+
18
+ ## New Features
19
+
20
+ ### Repository Transactions
21
+
22
+ **File:** `packages/core/src/base/repositories/core/base.ts`
23
+
24
+ **Problem:** Previously, transactions relied on Drizzle's callback API, which made it difficult to share a transaction context across different services or decoupled components.
25
+
26
+ **Solution:** Introduced explicit `ITransaction` objects managed by the repository/datasource.
27
+
28
+ ```typescript
29
+ const tx = await repo.beginTransaction();
30
+
31
+ try {
32
+ await repo.create({ data: {...}, options: { transaction: tx } });
33
+ await otherRepo.create({ data: {...}, options: { transaction: tx } });
34
+
35
+ await tx.commit();
36
+ } catch (err) {
37
+ await tx.rollback();
38
+ }
39
+ ```
40
+
41
+ ## Files Changed
42
+
43
+ ### Core Package (`packages/core`)
44
+
45
+ | File | Changes |
46
+ |------|---------|
47
+ | `src/base/repositories/core/base.ts` | Added `beginTransaction` and transaction handling logic |
48
+ | `src/base/repositories/core/readable.ts` | Updated read methods to respect transaction context |
49
+ | `src/base/repositories/core/persistable.ts` | Updated write methods to respect transaction context |
50
+ | `src/base/datasources/common/types.ts` | Added `ITransaction`, `ITransactionOptions`, and `IsolationLevels` |
51
+ | `src/base/datasources/base.ts` | Implemented `beginTransaction` on `BaseDataSource` |
52
+
53
+ ## Migration Guide
54
+
55
+ ### No Breaking Changes
56
+
57
+ This is an additive feature. Existing code using standard CRUD methods will continue to work without modification (using implicit auto-commit transactions).
@@ -0,0 +1,104 @@
1
+ ---
2
+ title: Dynamic Binding Registration Fix
3
+ description: Fix components registering datasources/controllers during configure() phase
4
+ ---
5
+
6
+ # Changelog - 2025-12-29
7
+
8
+ ## Dynamic Binding Registration Fix
9
+
10
+ Components can now register datasources and controllers during their `configure()` phase, and those bindings will be properly configured.
11
+
12
+ ## Overview
13
+
14
+ - **Simple Fix**: Re-run `registerDataSources()` after `registerComponents()`
15
+ - **Future Enhancement**: Multi-pass configuration loop for comprehensive dynamic registration
16
+
17
+ ## Problem
18
+
19
+ When components register datasources during `configure()`, those datasources were never configured because `registerDataSources()` had already completed.
20
+
21
+ **Initialization Order (Before):**
22
+ 1. `registerDataSources()` → configures initial datasources
23
+ 2. `registerComponents()` → component registers NEW datasource (too late!)
24
+ 3. `registerControllers()` → runs normally
25
+
26
+ The new datasource never gets its `configure()` called.
27
+
28
+ ## Solution (Simple Fix)
29
+
30
+ **File:** `packages/core/src/base/applications/base.ts`
31
+
32
+ Call `registerDataSources()` again after `registerComponents()`:
33
+
34
+ ```typescript
35
+ // Before:
36
+ await this.registerDataSources();
37
+ await this.registerComponents();
38
+ await this.registerControllers();
39
+
40
+ // After:
41
+ await this.registerDataSources();
42
+ await this.registerComponents();
43
+ await this.registerDataSources(); // Re-run for datasources added by components
44
+ await this.registerControllers();
45
+ ```
46
+
47
+ ## Files Changed
48
+
49
+ ### Core Package (`packages/core`)
50
+
51
+ | File | Changes |
52
+ |------|---------|
53
+ | `src/base/applications/base.ts` | Added second `registerDataSources()` call after `registerComponents()` |
54
+
55
+ ## No Breaking Changes
56
+
57
+ All changes are internal. Components that register datasources will now work correctly.
58
+
59
+ ## Future Enhancement
60
+
61
+ For comprehensive dynamic registration support (components registering other components, controllers registering datasources, etc.), a multi-pass configuration loop can be implemented:
62
+
63
+ ### Multi-Pass Configuration Loop (Future)
64
+
65
+ Instead of sequential registration, use a unified loop that continues until no new bindings are discovered:
66
+
67
+ ```typescript
68
+ protected async runConfigurationLoop(): Promise<void> {
69
+ const configured = {
70
+ datasources: new Set<string>(),
71
+ components: new Set<string>(),
72
+ controllers: new Set<string>(),
73
+ };
74
+
75
+ let hasNewBindings = true;
76
+ let iteration = 0;
77
+ const MAX_ITERATIONS = 100;
78
+
79
+ while (hasNewBindings && iteration < MAX_ITERATIONS) {
80
+ iteration++;
81
+ hasNewBindings = false;
82
+
83
+ // Order: datasources -> components -> controllers
84
+ const newDs = await this.configureNewBindings('datasources', configured.datasources);
85
+ const newComp = await this.configureNewBindings('components', configured.components);
86
+ const newCtrl = await this.configureNewBindings('controllers', configured.controllers);
87
+
88
+ hasNewBindings = newDs || newComp || newCtrl;
89
+ }
90
+ }
91
+ ```
92
+
93
+ **Benefits of Multi-Pass:**
94
+ - Handles arbitrary nesting depth
95
+ - Components can register datasources, controllers, or other components
96
+ - Datasources configured before components that depend on them
97
+ - MAX_ITERATIONS prevents infinite loops from circular dependencies
98
+
99
+ **LoopBack 4 Insights Applied:**
100
+ - Three-phase execution pattern (configure → discover → load)
101
+ - Observer ordering (datasources before components before controllers)
102
+ - Stabilization detection (loop until no new bindings)
103
+
104
+ See full implementation plan in architecture documentation.
@@ -0,0 +1,100 @@
1
+ ---
2
+ title: Snowflake UID Helper
3
+ description: New Snowflake ID generator with Base62 encoding for distributed systems
4
+ ---
5
+
6
+ # Changelog - 2025-12-29
7
+
8
+ ## Snowflake UID Helper
9
+
10
+ New unique ID generator using Twitter's Snowflake algorithm with Base62 encoding, suitable for distributed systems.
11
+
12
+ ## Overview
13
+
14
+ - **New Helper**: `SnowflakeUidHelper` for generating unique, time-sortable IDs
15
+ - **Base62 Encoding**: Compact string output (10-12 characters)
16
+ - **Configurable**: Optional `workerId` and `epoch` parameters
17
+ - **No Environment Variables**: Clean constructor-based configuration
18
+
19
+ ## New Features
20
+
21
+ ### SnowflakeUidHelper
22
+
23
+ **File:** `packages/helpers/src/helpers/uid/helper.ts`
24
+
25
+ **Problem:** Need unique, time-sortable IDs for distributed systems without external dependencies.
26
+
27
+ **Solution:** Snowflake ID generator with 70-bit structure (48-10-12):
28
+ - 48 bits: timestamp (~8,919 years from epoch)
29
+ - 10 bits: worker ID (1024 workers max)
30
+ - 12 bits: sequence (4096 per ms per worker)
31
+
32
+ ```typescript
33
+ import { SnowflakeUidHelper, SnowflakeConfig } from '@venizia/ignis-helpers';
34
+
35
+ // Use defaults (workerId: 199, epoch: 2025-01-01 00:00:00 UTC)
36
+ const generator = new SnowflakeUidHelper();
37
+
38
+ // Or with custom values
39
+ const customGenerator = new SnowflakeUidHelper({
40
+ workerId: 123,
41
+ epoch: BigInt(1735689600000),
42
+ });
43
+
44
+ // Generate IDs
45
+ const id = generator.nextId(); // Base62: "9du1sJXO88"
46
+ const snowflake = generator.nextSnowflake(); // BigInt: 130546360012247045n
47
+
48
+ // Parse IDs
49
+ const parsed = generator.parseId("9du1sJXO88");
50
+ // => { raw, timestamp, workerId, sequence }
51
+ ```
52
+
53
+ **Benefits:**
54
+ - High throughput: 4,096,000 IDs/second/worker
55
+ - Time-sortable: IDs are chronologically ordered
56
+ - Compact: 10-12 character Base62 strings
57
+ - No collisions: Worker ID + sequence ensures uniqueness
58
+ - Long lifespan: Until ~10,944 AD
59
+
60
+ ### Configuration Options
61
+
62
+ | Option | Type | Default | Description |
63
+ |--------|------|---------|-------------|
64
+ | `workerId` | `number` | `199` | Worker ID (0-1023) |
65
+ | `epoch` | `bigint` | `1735689600000n` | Custom epoch (2025-01-01 UTC) |
66
+
67
+ ### Available Methods
68
+
69
+ | Method | Returns | Description |
70
+ |--------|---------|-------------|
71
+ | `nextId()` | `string` | Generate Base62 encoded ID |
72
+ | `nextSnowflake()` | `bigint` | Generate raw Snowflake ID |
73
+ | `encodeBase62(num)` | `string` | Encode bigint to Base62 |
74
+ | `decodeBase62(str)` | `bigint` | Decode Base62 to bigint |
75
+ | `parseId(base62Id)` | `ISnowflakeParsedId` | Parse and extract components |
76
+ | `extractTimestamp(id)` | `Date` | Extract timestamp from ID |
77
+ | `extractWorkerId(id)` | `number` | Extract worker ID from ID |
78
+ | `extractSequence(id)` | `number` | Extract sequence from ID |
79
+ | `getWorkerId()` | `number` | Get current worker ID |
80
+
81
+ ## Files Changed
82
+
83
+ ### Helpers Package (`packages/helpers`)
84
+
85
+ | File | Changes |
86
+ |------|---------|
87
+ | `src/helpers/uid/helper.ts` | New Snowflake ID generator class |
88
+ | `src/helpers/uid/index.ts` | Export helper |
89
+ | `src/helpers/index.ts` | Added uid export |
90
+
91
+ ### Docs Package (`packages/docs`)
92
+
93
+ | File | Changes |
94
+ |------|---------|
95
+ | `wiki/references/helpers/uid.md` | New documentation |
96
+ | `wiki/references/helpers/index.md` | Added UID to helper list |
97
+
98
+ ## No Breaking Changes
99
+
100
+ This is a new feature addition. No existing APIs were modified.
@@ -0,0 +1,214 @@
1
+ ---
2
+ title: Repository Enhancements
3
+ description: Hidden properties, filter improvements, and code quality updates
4
+ ---
5
+
6
+ # Changelog - 2025-12-30
7
+
8
+ ## Repository Enhancements
9
+
10
+ This release introduces hidden properties configuration for models, array-based field selection, JSON path ordering, and several code quality improvements.
11
+
12
+ ## Overview
13
+
14
+ - **Hidden Properties**: Configure properties that are never returned through repository queries (SQL-level exclusion)
15
+ - **Array Fields Format**: Simpler syntax for field selection using arrays
16
+ - **JSON Path Ordering**: Order by nested fields within JSON/JSONB columns
17
+ - **Code Quality**: Refactored validation logic, improved caching patterns, consistent resolver pattern
18
+
19
+
20
+ ## Hidden Properties
21
+
22
+ ### Configuration
23
+
24
+ Configure hidden properties in the `@model` decorator. These properties are excluded at the SQL level, never leaving the database in repository operations.
25
+
26
+ ```typescript
27
+ import { pgTable, text } from 'drizzle-orm/pg-core';
28
+ import { BaseEntity, model, generateIdColumnDefs } from '@venizia/ignis';
29
+
30
+ @model({
31
+ type: 'entity',
32
+ settings: {
33
+ hiddenProperties: ['password', 'secret'],
34
+ },
35
+ })
36
+ export class User extends BaseEntity<typeof User.schema> {
37
+ static override schema = pgTable('User', {
38
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
39
+ email: text('email').notNull(),
40
+ password: text('password'),
41
+ secret: text('secret'),
42
+ });
43
+ }
44
+ ```
45
+
46
+ ### Behavior
47
+
48
+ | Operation | Behavior |
49
+ |-----------|----------|
50
+ | `find()`, `findOne()`, `findById()` | Hidden excluded from SELECT |
51
+ | `create()`, `createAll()` | Hidden excluded from RETURNING |
52
+ | `updateById()`, `updateAll()` | Hidden excluded from RETURNING |
53
+ | `deleteById()`, `deleteAll()` | Hidden excluded from RETURNING |
54
+ | `count()`, `existsWith()` | Can filter by hidden fields |
55
+ | Where clause filtering | Hidden fields usable in filters |
56
+ | Direct connector query | Hidden fields **included** (bypasses repository) |
57
+
58
+ ### Accessing Hidden Data
59
+
60
+ When you need to access hidden properties, use the connector directly:
61
+
62
+ ```typescript
63
+ // Repository - excludes hidden
64
+ const user = await userRepo.findById({ id: '123' });
65
+ // { id: '123', email: 'john@example.com' }
66
+
67
+ // Connector - includes all fields
68
+ const connector = userRepo.getConnector();
69
+ const [fullUser] = await connector
70
+ .select()
71
+ .from(User.schema)
72
+ .where(eq(User.schema.id, '123'));
73
+ // { id: '123', email: 'john@example.com', password: '...', secret: '...' }
74
+ ```
75
+
76
+ ### Relations Support
77
+
78
+ Hidden properties are recursively excluded from included relations:
79
+
80
+ ```typescript
81
+ const post = await postRepo.findOne({
82
+ filter: {
83
+ include: [{ relation: 'author' }]
84
+ }
85
+ });
86
+ // post.author excludes password and secret if User model has them configured
87
+ ```
88
+
89
+
90
+ ## Array Fields Format
91
+
92
+ **Before:** Only object format was supported:
93
+ ```typescript
94
+ fields: { id: true, email: true, name: true }
95
+ ```
96
+
97
+ **After:** Array format is now supported (recommended):
98
+ ```typescript
99
+ fields: ['id', 'email', 'name']
100
+ ```
101
+
102
+ Both formats produce the same result. The array format is more concise and easier to read.
103
+
104
+ **Type Definition:**
105
+ ```typescript
106
+ type TFields<T> = Partial<{ [K in keyof T]: boolean }> | Array<keyof T>;
107
+ ```
108
+
109
+
110
+ ## JSON Path Ordering
111
+
112
+ Order by nested fields within JSON/JSONB columns using dot notation and array indices.
113
+
114
+ ```typescript
115
+ // Simple nested field
116
+ order: ['metadata.priority DESC']
117
+ // SQL: ORDER BY "metadata" #> '{priority}' DESC
118
+
119
+ // Deeply nested field
120
+ order: ['settings.display.theme ASC']
121
+ // SQL: ORDER BY "settings" #> '{display,theme}' ASC
122
+
123
+ // Array element
124
+ order: ['tags[0] ASC']
125
+ // SQL: ORDER BY "tags" #> '{0}' ASC
126
+
127
+ // Complex path with array
128
+ order: ['data.items[2].name DESC']
129
+ // SQL: ORDER BY "data" #> '{items,2,name}' DESC
130
+ ```
131
+
132
+ **JSONB Sort Order:**
133
+
134
+ | Type | Sort Order |
135
+ |------|------------|
136
+ | `null` | First (lowest) |
137
+ | `boolean` | `false` < `true` |
138
+ | `number` | Numeric order |
139
+ | `string` | Lexicographic |
140
+ | `array` | Element-wise |
141
+ | `object` | Key-value |
142
+
143
+ **Security:** Built-in SQL injection prevention via regex validation for path components.
144
+
145
+
146
+ ## Code Quality Improvements
147
+
148
+ ### 1. Consistent Resolver Pattern
149
+
150
+ Added `THiddenPropertiesResolver` type and renamed method to match `getRelationResolver()` pattern:
151
+
152
+ ```typescript
153
+ // Consistent pattern
154
+ relationResolver: this.getRelationResolver(),
155
+ hiddenPropertiesResolver: this.getHiddenPropertiesResolver(),
156
+ ```
157
+
158
+ ### 2. Early Return Pattern
159
+
160
+ Refactored `resolveConnector()` from nested conditionals to early return:
161
+
162
+ ```typescript
163
+ // Before
164
+ if (transaction) {
165
+ if (!transaction.isActive) { throw... }
166
+ return transaction.connector;
167
+ }
168
+ return this.dataSource.connector;
169
+
170
+ // After
171
+ if (!transaction) {
172
+ return this.dataSource.connector;
173
+ }
174
+ if (!transaction.isActive) { throw... }
175
+ return transaction.connector;
176
+ ```
177
+
178
+ ### 3. Extracted Validation Helper
179
+
180
+ Created shared `validateWhereCondition()` method to eliminate duplicate validation logic.
181
+
182
+ ### 4. Improved Caching Pattern
183
+
184
+ Fixed caching by initializing `_visibleColumns` to `null` as sentinel for "not computed yet".
185
+
186
+ ### 5. Added Null Check
187
+
188
+ Added defensive check for `connector.query` in `getQueryInterface()`.
189
+
190
+
191
+ ## Files Changed
192
+
193
+ ### Core Package (`packages/core`)
194
+
195
+ | File | Changes |
196
+ |------|---------|
197
+ | `src/helpers/inversion/common/types.ts` | Added `IModelSettings` with `hiddenProperties` |
198
+ | `src/base/repositories/common/types.ts` | Updated `TFields` to support array format |
199
+ | `src/base/repositories/core/base.ts` | Added `getHiddenPropertiesResolver()`, caching, refactored `resolveConnector` |
200
+ | `src/base/repositories/core/readable.ts` | Added null check in `getQueryInterface` |
201
+ | `src/base/repositories/core/persistable.ts` | Added `validateWhereCondition`, updated CRUD methods |
202
+ | `src/base/repositories/operators/filter.ts` | Added `THiddenPropertiesResolver`, JSON ordering, array fields |
203
+
204
+ ### Examples (`examples/vert`)
205
+
206
+ | File | Changes |
207
+ |------|---------|
208
+ | `src/models/entities/user.model.ts` | Added hiddenProperties config |
209
+ | `src/services/repository-test.service.ts` | Added 21 hidden properties test cases |
210
+
211
+
212
+ ## No Breaking Changes
213
+
214
+ All changes are additive. Existing code continues to work without modification.