@venizia/ignis-docs 0.0.1-8 → 0.0.1-9
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/package.json +1 -1
- package/wiki/changelogs/2025-12-17-refactor.md +22 -0
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +192 -0
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +445 -0
- package/wiki/changelogs/index.md +22 -0
- package/wiki/changelogs/v0.0.1-7-initial-architecture.md +137 -0
- package/wiki/changelogs/v0.0.1-8-model-repo-datasource-refactor.md +278 -0
- package/wiki/get-started/5-minute-quickstart.md +1 -1
- package/wiki/get-started/best-practices/api-usage-examples.md +12 -8
- package/wiki/get-started/best-practices/common-pitfalls.md +2 -2
- package/wiki/get-started/best-practices/data-modeling.md +14 -20
- package/wiki/get-started/building-a-crud-api.md +60 -75
- package/wiki/get-started/core-concepts/controllers.md +14 -14
- package/wiki/get-started/core-concepts/persistent.md +110 -130
- package/wiki/get-started/quickstart.md +1 -1
- package/wiki/references/base/controllers.md +40 -16
- package/wiki/references/base/datasources.md +195 -33
- package/wiki/references/base/dependency-injection.md +5 -5
- package/wiki/references/base/models.md +398 -28
- package/wiki/references/base/repositories.md +475 -22
- package/wiki/references/components/authentication.md +224 -7
- package/wiki/references/components/health-check.md +1 -1
- package/wiki/references/components/swagger.md +1 -1
- package/wiki/references/helpers/inversion.md +8 -3
- package/wiki/references/src-details/core.md +6 -5
- package/wiki/references/src-details/inversion.md +4 -4
- package/wiki/references/utilities/request.md +16 -7
package/package.json
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog - 2025-12-17
|
|
2
|
+
|
|
3
|
+
## Major Refactor: Inversion of Control
|
|
4
|
+
|
|
5
|
+
The Dependency Injection (DI) system has been extracted from `packages/helpers` into a standalone package `@venizia/ignis-inversion`.
|
|
6
|
+
|
|
7
|
+
### Changes
|
|
8
|
+
|
|
9
|
+
- **New Package**: `@venizia/ignis-inversion` created.
|
|
10
|
+
- **Removed**: `packages/helpers/src/helpers/inversion` deleted.
|
|
11
|
+
- **Updated**: `packages/core` now imports DI primitives from `@venizia/ignis-inversion`.
|
|
12
|
+
- **Refactored**: `packages/core/src/helpers/inversion` now re-exports from the new package and adds core-specific extensions.
|
|
13
|
+
|
|
14
|
+
### Impact
|
|
15
|
+
|
|
16
|
+
- **Imports**: Imports from `@venizia/ignis` or `@venizia/ignis-helpers` relating to `Binding`, `Container`, `inject` should generally remain compatible via re-exports, but deep imports to `helpers/inversion` will break.
|
|
17
|
+
- **Structure**: Clearer separation of concerns. `inversion` package has zero dependencies on the rest of the framework (except `lodash`, `reflect-metadata`, `zod`).
|
|
18
|
+
|
|
19
|
+
## Other Changes
|
|
20
|
+
|
|
21
|
+
- **Mixins**: `server-config.mixin.ts` removed from `packages/core`.
|
|
22
|
+
- **Examples**: `examples/vert` updated to reflect latest core changes.
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# Changelog - 2025-12-18
|
|
2
|
+
|
|
3
|
+
## Performance Optimizations
|
|
4
|
+
|
|
5
|
+
This update focuses on performance improvements for the repository layer, reducing GC pressure and improving query execution speed.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
- **WeakMap Cache**: `DrizzleFilterBuilder` now caches `getTableColumns()` results
|
|
10
|
+
- **Core API for Flat Queries**: `ReadableRepository` uses faster Drizzle Core API when possible
|
|
11
|
+
- **Static schemaFactory Singleton**: `BaseEntity` shares a single `schemaFactory` instance across all entities
|
|
12
|
+
- **Async/Await Refactor**: Removed redundant Promise wrappers from repository methods
|
|
13
|
+
|
|
14
|
+
## Performance Improvements
|
|
15
|
+
|
|
16
|
+
### 1. WeakMap Cache for Filter Builder
|
|
17
|
+
|
|
18
|
+
**File:** `packages/core/src/base/repositories/operators/filter.ts`
|
|
19
|
+
|
|
20
|
+
**Problem:** `getTableColumns()` was called on every filter operation, causing repeated reflection overhead.
|
|
21
|
+
|
|
22
|
+
**Solution:** Added static WeakMap cache that stores column metadata per schema:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
export class DrizzleFilterBuilder extends BaseHelper {
|
|
26
|
+
// Static cache shared across all instances
|
|
27
|
+
private static columnCache = new WeakMap<
|
|
28
|
+
TTableSchemaWithId,
|
|
29
|
+
ReturnType<typeof getTableColumns>
|
|
30
|
+
>();
|
|
31
|
+
|
|
32
|
+
private getColumns<Schema extends TTableSchemaWithId>(schema: Schema) {
|
|
33
|
+
let columns = DrizzleFilterBuilder.columnCache.get(schema);
|
|
34
|
+
if (!columns) {
|
|
35
|
+
columns = getTableColumns(schema);
|
|
36
|
+
DrizzleFilterBuilder.columnCache.set(schema, columns);
|
|
37
|
+
}
|
|
38
|
+
return columns;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Benefits:**
|
|
44
|
+
- First call: `getTableColumns(schema)` → cached
|
|
45
|
+
- Subsequent calls: Retrieved from WeakMap (O(1) lookup)
|
|
46
|
+
- WeakMap allows garbage collection when schema is no longer referenced
|
|
47
|
+
- Especially beneficial for:
|
|
48
|
+
- High-concurrency environments
|
|
49
|
+
- Queries with nested AND/OR conditions (each recursion reuses cache)
|
|
50
|
+
- Multiple queries to the same table
|
|
51
|
+
|
|
52
|
+
### 2. Core API for Flat Queries (~15-20% Faster)
|
|
53
|
+
|
|
54
|
+
**File:** `packages/core/src/base/repositories/core/readable.ts`
|
|
55
|
+
|
|
56
|
+
**Problem:** All queries used Drizzle's Query API, which has overhead for relational mapping even when not needed.
|
|
57
|
+
|
|
58
|
+
**Solution:** Automatically use Drizzle Core API for flat queries (no relations, no field selection):
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// Automatic optimization - no code changes needed
|
|
62
|
+
const users = await repo.find({
|
|
63
|
+
filter: {
|
|
64
|
+
where: { status: 'active' },
|
|
65
|
+
limit: 10,
|
|
66
|
+
order: ['createdAt DESC'],
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
// Uses: db.select().from(table).where(...).orderBy(...).limit(10)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**When Core API is used:**
|
|
73
|
+
|
|
74
|
+
| Filter Options | API Used | Reason |
|
|
75
|
+
|----------------|----------|--------|
|
|
76
|
+
| `where`, `limit`, `order`, `offset` only | Core API | Flat query, no overhead |
|
|
77
|
+
| Has `include` (relations) | Query API | Needs relational mapper |
|
|
78
|
+
| Has `fields` selection | Query API | Core API field syntax differs |
|
|
79
|
+
|
|
80
|
+
**New Protected Methods:**
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// Check if Core API can be used
|
|
84
|
+
protected canUseCoreAPI(filter: TFilter<DataObject>): boolean;
|
|
85
|
+
|
|
86
|
+
// Execute flat query using Core API
|
|
87
|
+
protected async findWithCoreAPI(opts: {
|
|
88
|
+
filter: TFilter<DataObject>;
|
|
89
|
+
findOne?: boolean;
|
|
90
|
+
}): Promise<Array<DataObject>>;
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### 3. Static schemaFactory Singleton
|
|
94
|
+
|
|
95
|
+
**File:** `packages/core/src/base/models/base.ts`
|
|
96
|
+
|
|
97
|
+
**Problem:** New `schemaFactory` was created for every `BaseEntity` instance:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Before - created on every instantiation
|
|
101
|
+
constructor(opts?: { name?: string; schema?: Schema }) {
|
|
102
|
+
this.schemaFactory = createSchemaFactory(); // Memory overhead!
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Solution:** Lazy singleton pattern shared across all instances:
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// After - shared singleton
|
|
110
|
+
private static _schemaFactory?: ReturnType<typeof createSchemaFactory>;
|
|
111
|
+
protected static get schemaFactory(): ReturnType<typeof createSchemaFactory> {
|
|
112
|
+
return (BaseEntity._schemaFactory ??= createSchemaFactory());
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Benefits:**
|
|
117
|
+
- Single instance for all entities
|
|
118
|
+
- Lazy initialization (created on first use)
|
|
119
|
+
- Reduced memory footprint
|
|
120
|
+
|
|
121
|
+
### 4. Async/Await Refactor
|
|
122
|
+
|
|
123
|
+
**Files:**
|
|
124
|
+
- `packages/core/src/base/repositories/core/readable.ts`
|
|
125
|
+
- `packages/core/src/base/repositories/core/persistable.ts`
|
|
126
|
+
|
|
127
|
+
**Problem:** Every CRUD method wrapped existing promises in `new Promise()`:
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Before - Anti-pattern
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
this.connector.$count(this.entity.schema, where)
|
|
133
|
+
.then((count: number) => resolve({ count }))
|
|
134
|
+
.catch(reject);
|
|
135
|
+
});
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Solution:** Direct async/await:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// After - Clean and efficient
|
|
142
|
+
const count = await this.connector.$count(this.entity.schema, where);
|
|
143
|
+
return { count };
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Benefits:**
|
|
147
|
+
- Eliminates extra microtask queue entries
|
|
148
|
+
- Reduces ~200-400 bytes memory per Promise on V8
|
|
149
|
+
- Cleaner stack traces for debugging
|
|
150
|
+
- For bulk operations, overhead reduction multiplies
|
|
151
|
+
|
|
152
|
+
## Implementation Details
|
|
153
|
+
|
|
154
|
+
### Type Safety in Core API
|
|
155
|
+
|
|
156
|
+
The Core API implementation uses a controlled type assertion at the boundary:
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
// Type assertion to PgTable is safe: EntitySchema extends TTableSchemaWithId which extends PgTable
|
|
160
|
+
const table = schema as unknown as PgTable;
|
|
161
|
+
let query = this.connector.select().from(table).$dynamic();
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
This approach:
|
|
165
|
+
- Maintains type safety within the method
|
|
166
|
+
- Uses `$dynamic()` for query building with proper types
|
|
167
|
+
- Returns correctly typed `Promise<Array<DataObject>>`
|
|
168
|
+
|
|
169
|
+
## Files Changed
|
|
170
|
+
|
|
171
|
+
### Core Package - Repositories
|
|
172
|
+
- `packages/core/src/base/repositories/operators/filter.ts` - WeakMap cache for `getTableColumns()`
|
|
173
|
+
- `packages/core/src/base/repositories/core/readable.ts` - Core API optimization, async/await refactor
|
|
174
|
+
- `packages/core/src/base/repositories/core/persistable.ts` - Async/await refactor
|
|
175
|
+
|
|
176
|
+
### Core Package - Models
|
|
177
|
+
- `packages/core/src/base/models/base.ts` - Static schemaFactory singleton
|
|
178
|
+
|
|
179
|
+
## Benchmarks
|
|
180
|
+
|
|
181
|
+
These optimizations target the following scenarios:
|
|
182
|
+
|
|
183
|
+
| Scenario | Improvement |
|
|
184
|
+
|----------|-------------|
|
|
185
|
+
| Simple `find()` queries | ~15-20% faster (Core API) |
|
|
186
|
+
| Repeated filter builds | Eliminates reflection overhead (WeakMap) |
|
|
187
|
+
| Entity instantiation | Reduced memory per instance (schemaFactory) |
|
|
188
|
+
| All CRUD operations | Reduced GC pressure (async/await) |
|
|
189
|
+
|
|
190
|
+
## No Breaking Changes
|
|
191
|
+
|
|
192
|
+
All changes are internal optimizations. No API changes or migration required.
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# Changelog - 2025-12-18
|
|
2
|
+
|
|
3
|
+
## Repository Validation & Security Improvements
|
|
4
|
+
|
|
5
|
+
This update adds strict validation to the `@repository` decorator and fixes several security vulnerabilities in the filter operators.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
- **@repository Decorator**: Now requires both `model` AND `dataSource` for schema auto-discovery
|
|
10
|
+
- **Constructor Validation**: First parameter must extend `AbstractDataSource` (enforced via reflection)
|
|
11
|
+
- **DataSource Auto-Discovery**: Schema is automatically built from `@repository` bindings - no manual merging needed!
|
|
12
|
+
- **Filter Security**: Fixed empty IN array bypass, invalid column handling, BETWEEN validation
|
|
13
|
+
- **PostgreSQL Compatibility**: REGEXP now uses PostgreSQL POSIX operators
|
|
14
|
+
- **UUID Generation**: Now uses native PostgreSQL `uuid` type with `gen_random_uuid()`
|
|
15
|
+
|
|
16
|
+
## Breaking Changes
|
|
17
|
+
|
|
18
|
+
### 1. Repository Constructor Signature Changed
|
|
19
|
+
|
|
20
|
+
**This is a major breaking change.** The constructor signature for all repository classes has changed.
|
|
21
|
+
|
|
22
|
+
**Before:**
|
|
23
|
+
```typescript
|
|
24
|
+
constructor(opts: {
|
|
25
|
+
entityClass: TClass<BaseEntity<EntitySchema>>;
|
|
26
|
+
relations: { [relationName: string]: TRelationConfig };
|
|
27
|
+
dataSource: IDataSource;
|
|
28
|
+
})
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**After:**
|
|
32
|
+
```typescript
|
|
33
|
+
constructor(ds?: IDataSource, opts?: { entityClass?: TClass<BaseEntity<EntitySchema>> })
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
**Key changes:**
|
|
37
|
+
- DataSource is now the first parameter (auto-injected from `@repository` decorator)
|
|
38
|
+
- `relations` parameter removed - now auto-resolved from entity's static `relations` property
|
|
39
|
+
- Both parameters are optional when using `@repository` decorator with `model` and `dataSource`
|
|
40
|
+
|
|
41
|
+
**Migration:**
|
|
42
|
+
```typescript
|
|
43
|
+
// Before
|
|
44
|
+
@repository({})
|
|
45
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
46
|
+
constructor(
|
|
47
|
+
@inject({ key: 'datasources.PostgresDataSource' }) ds: PostgresDataSource,
|
|
48
|
+
) {
|
|
49
|
+
super({
|
|
50
|
+
entityClass: User,
|
|
51
|
+
relations: userRelations.definitions,
|
|
52
|
+
dataSource: ds,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// After - Zero boilerplate
|
|
58
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
59
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
60
|
+
// No constructor needed!
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// After - With explicit constructor
|
|
64
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
65
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
66
|
+
constructor(
|
|
67
|
+
@inject({ key: 'datasources.PostgresDataSource' }) ds: PostgresDataSource,
|
|
68
|
+
) {
|
|
69
|
+
super(ds); // Just pass dataSource, entity and relations auto-resolved
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2. @repository Decorator Requires Both `model` AND `dataSource`
|
|
75
|
+
|
|
76
|
+
**Before (would silently fail to register model):**
|
|
77
|
+
```typescript
|
|
78
|
+
// ❌ This would not register User for schema auto-discovery
|
|
79
|
+
@repository({ model: User })
|
|
80
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**After (throws error):**
|
|
84
|
+
```typescript
|
|
85
|
+
// Error: [@repository][UserRepository] Invalid metadata | Missing 'dataSource'
|
|
86
|
+
@repository({ model: User })
|
|
87
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
88
|
+
|
|
89
|
+
// ✅ Correct usage
|
|
90
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
91
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 2. Constructor First Parameter Must Be DataSource Type
|
|
95
|
+
|
|
96
|
+
When using explicit `@inject` in constructor, the first parameter **must** extend `AbstractDataSource`:
|
|
97
|
+
|
|
98
|
+
**Before (would work with wrong type):**
|
|
99
|
+
```typescript
|
|
100
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
101
|
+
export class UserRepository extends ReadableRepository<typeof User.schema> {
|
|
102
|
+
constructor(
|
|
103
|
+
@inject({ key: 'datasources.PostgresDataSource' })
|
|
104
|
+
dataSource: any, // ❌ This would compile but is wrong
|
|
105
|
+
) {
|
|
106
|
+
super(dataSource);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**After (enforced at decorator time):**
|
|
112
|
+
```typescript
|
|
113
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
114
|
+
export class UserRepository extends ReadableRepository<typeof User.schema> {
|
|
115
|
+
constructor(
|
|
116
|
+
@inject({ key: 'datasources.PostgresDataSource' })
|
|
117
|
+
dataSource: PostgresDataSource, // ✅ Must be concrete DataSource type
|
|
118
|
+
) {
|
|
119
|
+
super(dataSource);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 3. Filter Column Validation
|
|
125
|
+
|
|
126
|
+
Invalid column names now throw errors instead of being silently ignored:
|
|
127
|
+
|
|
128
|
+
**Before:**
|
|
129
|
+
```typescript
|
|
130
|
+
// Would silently ignore 'invalidColumn'
|
|
131
|
+
await repo.find({ filter: { where: { invalidColumn: 'value' } } });
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**After:**
|
|
135
|
+
```typescript
|
|
136
|
+
// Error: [toWhere] Table: User | Column NOT FOUND | key: 'invalidColumn'
|
|
137
|
+
await repo.find({ filter: { where: { invalidColumn: 'value' } } });
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Security Fixes
|
|
141
|
+
|
|
142
|
+
### Empty IN Array Bypass (CVE-like)
|
|
143
|
+
|
|
144
|
+
**Vulnerability:** Empty `IN` arrays would return `true`, bypassing security filters.
|
|
145
|
+
|
|
146
|
+
**Fix:** Empty `IN` arrays now return `sql\`false\``, correctly matching no records.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
// Before: WHERE id IN () => true (security bypass!)
|
|
150
|
+
// After: WHERE false => no records (correct)
|
|
151
|
+
await repo.find({ filter: { where: { id: { IN: [] } } } });
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### BETWEEN Validation
|
|
155
|
+
|
|
156
|
+
**Vulnerability:** Invalid `BETWEEN` values could cause unexpected behavior.
|
|
157
|
+
|
|
158
|
+
**Fix:** BETWEEN now validates input is array of exactly 2 elements.
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Throws: [BETWEEN] Invalid value: expected array of 2 elements
|
|
162
|
+
await repo.find({ filter: { where: { age: { BETWEEN: [10] } } } });
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### PostgreSQL REGEXP Compatibility
|
|
166
|
+
|
|
167
|
+
**Issue:** MySQL-style REGEXP operator doesn't work in PostgreSQL.
|
|
168
|
+
|
|
169
|
+
**Fix:** Now uses PostgreSQL POSIX regex operators:
|
|
170
|
+
- `REGEXP` → `~` (case-sensitive)
|
|
171
|
+
- `IREGEXP` → `~*` (case-insensitive, new!)
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
// PostgreSQL-compatible regex
|
|
175
|
+
await repo.find({ filter: { where: { name: { REGEXP: '^John' } } } });
|
|
176
|
+
await repo.find({ filter: { where: { name: { IREGEXP: '^john' } } } }); // Case-insensitive
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## New Features
|
|
180
|
+
|
|
181
|
+
### DataSource Schema Auto-Discovery
|
|
182
|
+
|
|
183
|
+
DataSources can now automatically discover their schema from registered `@repository` decorators:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Before: Manual schema merging required
|
|
187
|
+
@datasource({ driver: 'node-postgres' })
|
|
188
|
+
export class PostgresDataSource extends BaseDataSource<...> {
|
|
189
|
+
constructor() {
|
|
190
|
+
super({
|
|
191
|
+
name: PostgresDataSource.name,
|
|
192
|
+
config: { /* ... */ },
|
|
193
|
+
schema: Object.assign({}, // Manual merge!
|
|
194
|
+
{ [User.TABLE_NAME]: userTable },
|
|
195
|
+
userRelations.relations,
|
|
196
|
+
),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// After: Auto-discovery - no schema needed!
|
|
202
|
+
@datasource({ driver: 'node-postgres' })
|
|
203
|
+
export class PostgresDataSource extends BaseDataSource<...> {
|
|
204
|
+
constructor() {
|
|
205
|
+
super({
|
|
206
|
+
name: PostgresDataSource.name,
|
|
207
|
+
config: { /* ... */ },
|
|
208
|
+
// Schema auto-discovered from @repository decorators!
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
When repositories are defined with `@repository({ model: User, dataSource: PostgresDataSource })`, the framework automatically builds the schema.
|
|
215
|
+
|
|
216
|
+
### Schema Key Mismatch Validation
|
|
217
|
+
|
|
218
|
+
Added helpful error when entity name doesn't match schema keys:
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
// Error: [UserRepository] Schema key mismatch | Entity name 'User' not found in connector.query | Available keys: [Configuration, Post]
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### UUID Type Improvement
|
|
225
|
+
|
|
226
|
+
Changed from `text` type with JS-generated UUID to native PostgreSQL `uuid`:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// Before: text('id').primaryKey().$defaultFn(() => crypto.randomUUID())
|
|
230
|
+
// After: uuid('id').defaultRandom().primaryKey()
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Benefits:
|
|
234
|
+
- Native PostgreSQL UUID type (16 bytes vs 36 bytes)
|
|
235
|
+
- Uses `gen_random_uuid()` - no extension required
|
|
236
|
+
- Better indexing performance
|
|
237
|
+
|
|
238
|
+
### Case-Insensitive REGEXP (IREGEXP)
|
|
239
|
+
|
|
240
|
+
Added new `IREGEXP` operator for case-insensitive regex matching:
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
// Case-sensitive (existing)
|
|
244
|
+
await repo.find({ filter: { where: { name: { REGEXP: '^John' } } } });
|
|
245
|
+
|
|
246
|
+
// Case-insensitive (new!)
|
|
247
|
+
await repo.find({ filter: { where: { name: { IREGEXP: '^john' } } } });
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### BaseEntity Static Schema & Relations
|
|
251
|
+
|
|
252
|
+
Models can now define schema and relations as static properties, enabling cleaner syntax:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// Before - Constructor-based schema
|
|
256
|
+
@model({ type: 'entity' })
|
|
257
|
+
export class User extends BaseEntity<typeof userTable> {
|
|
258
|
+
constructor() {
|
|
259
|
+
super({ name: 'User', schema: userTable });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// After - Static schema (recommended)
|
|
264
|
+
@model({ type: 'entity' })
|
|
265
|
+
export class User extends BaseEntity<typeof User.schema> {
|
|
266
|
+
static override schema = userTable;
|
|
267
|
+
static override relations = () => userRelations.definitions;
|
|
268
|
+
static override TABLE_NAME = 'User';
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
Relations are now auto-resolved from the entity's static `relations` property - no need to pass them in repository constructor.
|
|
273
|
+
|
|
274
|
+
### Repository Log Option
|
|
275
|
+
|
|
276
|
+
All CRUD operations now support a `log` option for debugging:
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
// Enable logging for a specific operation
|
|
280
|
+
await repo.create({
|
|
281
|
+
data: { name: 'John' },
|
|
282
|
+
options: {
|
|
283
|
+
log: { use: true, level: 'debug' }
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Log output: [_create] Executing with opts: { data: [...], options: {...} }
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
**Available on:** `create`, `createAll`, `updateById`, `updateAll`, `deleteById`, `deleteAll`
|
|
291
|
+
|
|
292
|
+
### Improved TypeScript Return Types
|
|
293
|
+
|
|
294
|
+
Repository methods now have better type inference based on `shouldReturn`:
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
// When shouldReturn: false - returns null
|
|
298
|
+
const result1 = await repo.create({
|
|
299
|
+
data: { name: 'John' },
|
|
300
|
+
options: { shouldReturn: false }
|
|
301
|
+
});
|
|
302
|
+
// Type: Promise<TCount & { data: null }>
|
|
303
|
+
|
|
304
|
+
// When shouldReturn: true (default) - returns the entity
|
|
305
|
+
const result2 = await repo.create({
|
|
306
|
+
data: { name: 'John' },
|
|
307
|
+
options: { shouldReturn: true }
|
|
308
|
+
});
|
|
309
|
+
// Type: Promise<TCount & { data: User }>
|
|
310
|
+
|
|
311
|
+
// TypeScript now correctly infers the return type!
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
### IEntity Interface
|
|
315
|
+
|
|
316
|
+
New interface for model classes with static schema and relations:
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
interface IEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId> {
|
|
320
|
+
TABLE_NAME?: string;
|
|
321
|
+
schema: Schema;
|
|
322
|
+
relations?: TValueOrResolver<Array<TRelationConfig>>;
|
|
323
|
+
}
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Files Changed
|
|
327
|
+
|
|
328
|
+
### Core Package - Models
|
|
329
|
+
- `packages/core/src/base/models/base.ts` - Static schema/relations support, IEntity interface
|
|
330
|
+
- `packages/core/src/base/models/common/types.ts` - IEntity interface definition
|
|
331
|
+
- `packages/core/src/base/models/enrichers/id.enricher.ts` - Native PostgreSQL UUID type
|
|
332
|
+
- `packages/core/src/base/models/enrichers/tz.enricher.ts` - Type naming fix
|
|
333
|
+
|
|
334
|
+
### Core Package - Repositories
|
|
335
|
+
- `packages/core/src/base/repositories/core/base.ts` - Constructor signature change, relations auto-resolution
|
|
336
|
+
- `packages/core/src/base/repositories/core/readable.ts` - Constructor change, getQueryInterface validation
|
|
337
|
+
- `packages/core/src/base/repositories/core/persistable.ts` - Constructor change, log option, TypeScript overloads
|
|
338
|
+
- `packages/core/src/base/repositories/core/default-crud.ts` - Documentation update
|
|
339
|
+
- `packages/core/src/base/repositories/common/types.ts` - TRepositoryLogOptions, TypeScript overloads
|
|
340
|
+
- `packages/core/src/base/repositories/operators/filter.ts` - Column validation, error throwing
|
|
341
|
+
- `packages/core/src/base/repositories/operators/query.ts` - REGEXP/IREGEXP fix, BETWEEN validation, IN empty array fix
|
|
342
|
+
|
|
343
|
+
### Core Package - DataSources & Metadata
|
|
344
|
+
- `packages/core/src/base/datasources/base.ts` - Schema auto-discovery feature
|
|
345
|
+
- `packages/core/src/base/metadata/persistents.ts` - Repository decorator validation, constructor type validation
|
|
346
|
+
|
|
347
|
+
### Examples
|
|
348
|
+
- `examples/vert/src/repositories/user.repository.ts` - Updated to proper types
|
|
349
|
+
- `examples/vert/src/repositories/configuration.repository.ts` - Updated patterns
|
|
350
|
+
- `examples/vert/src/datasources/postgres.datasource.ts` - Using auto-discovery
|
|
351
|
+
- `examples/vert/src/models/entities/user.model.ts` - Static schema pattern
|
|
352
|
+
- `examples/vert/src/models/entities/configuration.model.ts` - Static schema pattern
|
|
353
|
+
|
|
354
|
+
## Migration Guide
|
|
355
|
+
|
|
356
|
+
### Step 1: Update Repository Constructors (BREAKING)
|
|
357
|
+
|
|
358
|
+
The repository constructor signature has changed:
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
// Before
|
|
362
|
+
@repository({})
|
|
363
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
364
|
+
constructor(
|
|
365
|
+
@inject({ key: 'datasources.PostgresDataSource' }) ds: PostgresDataSource,
|
|
366
|
+
) {
|
|
367
|
+
super({
|
|
368
|
+
entityClass: User,
|
|
369
|
+
relations: userRelations.definitions,
|
|
370
|
+
dataSource: ds,
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// After - Option A: Zero boilerplate (recommended)
|
|
376
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
377
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
378
|
+
// No constructor needed!
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// After - Option B: With explicit constructor
|
|
382
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
383
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
384
|
+
constructor(
|
|
385
|
+
@inject({ key: 'datasources.PostgresDataSource' }) ds: PostgresDataSource,
|
|
386
|
+
) {
|
|
387
|
+
super(ds); // Just pass dataSource
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Step 2: Update Model Definitions (Optional but Recommended)
|
|
393
|
+
|
|
394
|
+
Add static properties to your models for relations auto-resolution:
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// Before
|
|
398
|
+
@model({ type: 'entity' })
|
|
399
|
+
export class User extends BaseEntity<typeof userTable> {
|
|
400
|
+
constructor() {
|
|
401
|
+
super({ name: 'User', schema: userTable });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// After - Static properties
|
|
406
|
+
@model({ type: 'entity' })
|
|
407
|
+
export class User extends BaseEntity<typeof User.schema> {
|
|
408
|
+
static override schema = userTable;
|
|
409
|
+
static override relations = () => userRelations.definitions;
|
|
410
|
+
static override TABLE_NAME = 'User';
|
|
411
|
+
}
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Step 3: Update @repository Decorators
|
|
415
|
+
|
|
416
|
+
Ensure all `@repository` decorators have both `model` and `dataSource`:
|
|
417
|
+
|
|
418
|
+
```typescript
|
|
419
|
+
// Find and update all occurrences
|
|
420
|
+
@repository({ model: YourModel, dataSource: YourDataSource })
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### Step 4: Fix Constructor Types
|
|
424
|
+
|
|
425
|
+
If using explicit `@inject`, ensure first parameter has correct type:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
constructor(
|
|
429
|
+
@inject({ key: 'datasources.YourDataSource' })
|
|
430
|
+
dataSource: YourDataSource, // Not 'any' or 'object'
|
|
431
|
+
) {}
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Step 5: Review Filter Queries
|
|
435
|
+
|
|
436
|
+
Check for any queries using:
|
|
437
|
+
- Empty `IN` arrays (now return no results instead of all)
|
|
438
|
+
- Invalid column names (now throw errors)
|
|
439
|
+
- `BETWEEN` with non-array or wrong-length values
|
|
440
|
+
|
|
441
|
+
### Step 6: REGEXP Migration (PostgreSQL Users)
|
|
442
|
+
|
|
443
|
+
Replace MySQL-style REGEXP with PostgreSQL syntax:
|
|
444
|
+
- REGEXP already works (uses `~` operator)
|
|
445
|
+
- For case-insensitive, use new `IREGEXP` (uses `~*` operator)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelogs
|
|
2
|
+
|
|
3
|
+
This section tracks the history of significant changes, refactors, and updates to the Ignis framework.
|
|
4
|
+
|
|
5
|
+
## Version History
|
|
6
|
+
|
|
7
|
+
| Version | Date | Description |
|
|
8
|
+
|---------|------|-------------|
|
|
9
|
+
| [2025-12-18 Performance](./2025-12-18-performance-optimizations.md) | 2025-12-18 | Performance Optimizations (Core API, WeakMap Cache) |
|
|
10
|
+
| [2025-12-18](./2025-12-18-repository-validation-security.md) | 2025-12-18 | Repository Validation & Security Improvements |
|
|
11
|
+
| [2025-12-17](./2025-12-17-refactor.md) | 2025-12-17 | Inversion of Control Refactor |
|
|
12
|
+
| [v0.0.1-8](./v0.0.1-8-model-repo-datasource-refactor.md) | 2025-12-16 | Model-Repository-DataSource Architecture Refactor |
|
|
13
|
+
| [v0.0.1-7](./v0.0.1-7-initial-architecture.md) | 2025-12-16 | Initial Architecture (Pre-Refactor) |
|
|
14
|
+
|
|
15
|
+
## How to Read Changelogs
|
|
16
|
+
|
|
17
|
+
Each changelog entry includes:
|
|
18
|
+
- **Overview**: Summary of changes
|
|
19
|
+
- **Breaking Changes**: Any changes that require migration
|
|
20
|
+
- **New Features**: New capabilities added
|
|
21
|
+
- **Files Changed**: List of modified files
|
|
22
|
+
- **Migration Guide**: Steps to update existing code (if applicable)
|