@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.
- package/README.md +1 -1
- package/package.json +4 -2
- package/wiki/best-practices/api-usage-examples.md +591 -0
- package/wiki/best-practices/architectural-patterns.md +415 -0
- package/wiki/best-practices/architecture-decisions.md +488 -0
- package/wiki/{get-started/best-practices → best-practices}/code-style-standards.md +647 -182
- package/wiki/{get-started/best-practices → best-practices}/common-pitfalls.md +109 -4
- package/wiki/{get-started/best-practices → best-practices}/contribution-workflow.md +34 -7
- package/wiki/best-practices/data-modeling.md +376 -0
- package/wiki/best-practices/deployment-strategies.md +698 -0
- package/wiki/best-practices/index.md +27 -0
- package/wiki/best-practices/performance-optimization.md +196 -0
- package/wiki/best-practices/security-guidelines.md +218 -0
- package/wiki/{get-started/best-practices → best-practices}/troubleshooting-tips.md +97 -1
- package/wiki/changelogs/2025-12-16-initial-architecture.md +1 -1
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +1 -1
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +5 -5
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +13 -7
- package/wiki/changelogs/2025-12-26-nested-relations-and-generics.md +86 -0
- package/wiki/changelogs/2025-12-26-transaction-support.md +57 -0
- package/wiki/changelogs/2025-12-29-dynamic-binding-registration.md +104 -0
- package/wiki/changelogs/2025-12-29-snowflake-uid-helper.md +100 -0
- package/wiki/changelogs/2025-12-30-repository-enhancements.md +214 -0
- package/wiki/changelogs/2025-12-31-json-path-filtering-array-operators.md +214 -0
- package/wiki/changelogs/2025-12-31-string-id-custom-generator.md +137 -0
- package/wiki/changelogs/2026-01-02-default-filter-and-repository-mixins.md +418 -0
- package/wiki/changelogs/index.md +8 -1
- package/wiki/changelogs/planned-schema-migrator.md +2 -10
- package/wiki/{get-started/core-concepts → guides/core-concepts/application}/bootstrapping.md +18 -5
- package/wiki/{get-started/core-concepts/application.md → guides/core-concepts/application/index.md} +47 -104
- package/wiki/guides/core-concepts/components-guide.md +509 -0
- package/wiki/guides/core-concepts/components.md +122 -0
- package/wiki/{get-started → guides}/core-concepts/controllers.md +30 -13
- package/wiki/{get-started → guides}/core-concepts/dependency-injection.md +97 -0
- package/wiki/guides/core-concepts/persistent/datasources.md +179 -0
- package/wiki/guides/core-concepts/persistent/index.md +119 -0
- package/wiki/guides/core-concepts/persistent/models.md +241 -0
- package/wiki/guides/core-concepts/persistent/repositories.md +219 -0
- package/wiki/guides/core-concepts/persistent/transactions.md +170 -0
- package/wiki/{get-started → guides}/core-concepts/services.md +26 -3
- package/wiki/{get-started → guides/get-started}/5-minute-quickstart.md +59 -14
- package/wiki/guides/get-started/philosophy.md +682 -0
- package/wiki/guides/get-started/setup.md +157 -0
- package/wiki/guides/index.md +89 -0
- package/wiki/guides/reference/glossary.md +243 -0
- package/wiki/{get-started → guides/reference}/mcp-docs-server.md +0 -10
- package/wiki/{get-started → guides/tutorials}/building-a-crud-api.md +134 -132
- package/wiki/{get-started/quickstart.md → guides/tutorials/complete-installation.md} +107 -71
- package/wiki/guides/tutorials/ecommerce-api.md +1399 -0
- package/wiki/guides/tutorials/realtime-chat.md +1261 -0
- package/wiki/guides/tutorials/testing.md +723 -0
- package/wiki/index.md +176 -37
- package/wiki/references/base/application.md +27 -0
- package/wiki/references/base/bootstrapping.md +30 -26
- package/wiki/references/base/components.md +532 -31
- package/wiki/references/base/controllers.md +136 -38
- package/wiki/references/base/datasources.md +108 -5
- package/wiki/references/base/dependency-injection.md +39 -3
- package/wiki/references/base/filter-system/application-usage.md +224 -0
- package/wiki/references/base/filter-system/array-operators.md +132 -0
- package/wiki/references/base/filter-system/comparison-operators.md +109 -0
- package/wiki/references/base/filter-system/default-filter.md +428 -0
- package/wiki/references/base/filter-system/fields-order-pagination.md +155 -0
- package/wiki/references/base/filter-system/index.md +127 -0
- package/wiki/references/base/filter-system/json-filtering.md +197 -0
- package/wiki/references/base/filter-system/list-operators.md +71 -0
- package/wiki/references/base/filter-system/logical-operators.md +156 -0
- package/wiki/references/base/filter-system/null-operators.md +58 -0
- package/wiki/references/base/filter-system/pattern-matching.md +108 -0
- package/wiki/references/base/filter-system/quick-reference.md +431 -0
- package/wiki/references/base/filter-system/range-operators.md +63 -0
- package/wiki/references/base/filter-system/tips.md +190 -0
- package/wiki/references/base/filter-system/use-cases.md +452 -0
- package/wiki/references/base/index.md +90 -0
- package/wiki/references/base/middlewares.md +602 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +732 -0
- package/wiki/references/base/repositories/advanced.md +555 -0
- package/wiki/references/base/repositories/index.md +228 -0
- package/wiki/references/base/repositories/mixins.md +331 -0
- package/wiki/references/base/repositories/relations.md +486 -0
- package/wiki/references/base/repositories.md +40 -549
- package/wiki/references/base/services.md +28 -4
- package/wiki/references/components/authentication.md +22 -2
- package/wiki/references/components/health-check.md +12 -0
- package/wiki/references/components/index.md +23 -0
- package/wiki/references/components/mail.md +687 -0
- package/wiki/references/components/request-tracker.md +16 -0
- package/wiki/references/components/socket-io.md +18 -0
- package/wiki/references/components/static-asset.md +14 -26
- package/wiki/references/components/swagger.md +17 -0
- package/wiki/references/configuration/environment-variables.md +427 -0
- package/wiki/references/configuration/index.md +73 -0
- package/wiki/references/helpers/cron.md +14 -0
- package/wiki/references/helpers/crypto.md +15 -0
- package/wiki/references/helpers/env.md +16 -0
- package/wiki/references/helpers/error.md +17 -0
- package/wiki/references/helpers/index.md +15 -0
- package/wiki/references/helpers/inversion.md +24 -4
- package/wiki/references/helpers/logger.md +19 -0
- package/wiki/references/helpers/network.md +11 -0
- package/wiki/references/helpers/queue.md +19 -0
- package/wiki/references/helpers/redis.md +21 -0
- package/wiki/references/helpers/socket-io.md +24 -5
- package/wiki/references/helpers/storage.md +18 -10
- package/wiki/references/helpers/testing.md +18 -0
- package/wiki/references/helpers/types.md +167 -0
- package/wiki/references/helpers/uid.md +167 -0
- package/wiki/references/helpers/worker-thread.md +16 -0
- package/wiki/references/index.md +177 -0
- package/wiki/references/quick-reference.md +634 -0
- package/wiki/references/src-details/boot.md +3 -3
- package/wiki/references/src-details/dev-configs.md +0 -4
- package/wiki/references/src-details/docs.md +2 -2
- package/wiki/references/src-details/index.md +86 -0
- package/wiki/references/src-details/inversion.md +1 -6
- package/wiki/references/src-details/mcp-server.md +3 -15
- package/wiki/references/utilities/index.md +86 -10
- package/wiki/references/utilities/jsx.md +577 -0
- package/wiki/references/utilities/request.md +0 -2
- package/wiki/references/utilities/statuses.md +740 -0
- package/wiki/changelogs/planned-transaction-support.md +0 -216
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -266
- package/wiki/get-started/best-practices/architectural-patterns.md +0 -170
- package/wiki/get-started/best-practices/data-modeling.md +0 -177
- package/wiki/get-started/best-practices/deployment-strategies.md +0 -121
- package/wiki/get-started/best-practices/performance-optimization.md +0 -88
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/components.md +0 -98
- package/wiki/get-started/core-concepts/persistent.md +0 -543
- package/wiki/get-started/index.md +0 -65
- package/wiki/get-started/philosophy.md +0 -296
- 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.
|