@venizia/ignis-docs 0.0.1-9 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +1 -0
- package/package.json +2 -2
- package/wiki/changelogs/{v0.0.1-7-initial-architecture.md → 2025-12-16-initial-architecture.md} +20 -12
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +300 -0
- package/wiki/changelogs/2025-12-17-refactor.md +80 -12
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +28 -90
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +101 -297
- package/wiki/changelogs/index.md +19 -8
- package/wiki/changelogs/planned-transaction-support.md +216 -0
- package/wiki/changelogs/template.md +123 -0
- package/wiki/get-started/best-practices/api-usage-examples.md +0 -2
- package/wiki/get-started/best-practices/architectural-patterns.md +2 -2
- package/wiki/get-started/best-practices/common-pitfalls.md +5 -3
- package/wiki/get-started/best-practices/contribution-workflow.md +2 -0
- package/wiki/get-started/best-practices/data-modeling.md +91 -34
- package/wiki/get-started/best-practices/security-guidelines.md +3 -1
- package/wiki/get-started/building-a-crud-api.md +3 -3
- package/wiki/get-started/core-concepts/application.md +72 -3
- package/wiki/get-started/core-concepts/bootstrapping.md +566 -0
- package/wiki/get-started/core-concepts/components.md +4 -2
- package/wiki/get-started/core-concepts/persistent.md +350 -378
- package/wiki/get-started/core-concepts/services.md +21 -27
- package/wiki/references/base/bootstrapping.md +789 -0
- package/wiki/references/base/components.md +1 -1
- package/wiki/references/base/dependency-injection.md +95 -2
- package/wiki/references/base/services.md +2 -2
- package/wiki/references/components/authentication.md +4 -3
- package/wiki/references/components/index.md +1 -1
- package/wiki/references/helpers/error.md +2 -2
- package/wiki/references/src-details/boot.md +379 -0
- package/wiki/references/src-details/core.md +2 -2
- package/wiki/changelogs/v0.0.1-8-model-repo-datasource-refactor.md +0 -278
package/LICENSE.md
CHANGED
|
@@ -33,6 +33,7 @@ This monorepo contains the following packages, all licensed under MIT:
|
|
|
33
33
|
| Package | Description |
|
|
34
34
|
|---------|-------------|
|
|
35
35
|
| `@venizia/ignis` | Core framework - controllers, services, decorators |
|
|
36
|
+
| `@venizia/ignis-boot` | Application bootstrapping & artifact auto-discovery |
|
|
36
37
|
| `@venizia/ignis-helpers` | Utility helpers - logging, cron, Redis, queues, storage |
|
|
37
38
|
| `@venizia/ignis-inversion` | Dependency Injection & IoC container |
|
|
38
39
|
| `@venizia/dev-configs` | Shared ESLint, Prettier, TypeScript configurations |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@venizia/ignis-docs",
|
|
3
|
-
"version": "0.0.1
|
|
3
|
+
"version": "0.0.1",
|
|
4
4
|
"description": "Documentation and MCP Server for Ignis Framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ignis",
|
|
@@ -111,7 +111,7 @@
|
|
|
111
111
|
"@braintree/sanitize-url": "^7.1.1",
|
|
112
112
|
"@types/bun": "^1.3.4",
|
|
113
113
|
"@types/glob": "^8.1.0",
|
|
114
|
-
"@venizia/dev-configs": "^0.0.
|
|
114
|
+
"@venizia/dev-configs": "^0.0.2",
|
|
115
115
|
"eslint": "^9.36.0",
|
|
116
116
|
"glob": "^10.4.2",
|
|
117
117
|
"prettier": "^3.6.2",
|
package/wiki/changelogs/{v0.0.1-7-initial-architecture.md → 2025-12-16-initial-architecture.md}
RENAMED
|
@@ -1,12 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
title: Initial Architecture
|
|
3
|
+
description: Documentation of the original Ignis architecture before the Model-Repository-DataSource refactor
|
|
4
|
+
---
|
|
2
5
|
|
|
3
|
-
|
|
4
|
-
**Status**: Superseded by v0.0.1-8
|
|
6
|
+
# Changelog - 2025-12-16
|
|
5
7
|
|
|
6
|
-
##
|
|
8
|
+
## Initial Architecture (Pre-Refactor)
|
|
7
9
|
|
|
8
10
|
This documents the original architecture of the Ignis framework before the Model-Repository-DataSource refactor. This version required manual schema registration and explicit constructor parameters.
|
|
9
11
|
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **Model Definition**: Three separate declarations (table, relations, class) for each model.
|
|
15
|
+
- **DataSource Definition**: Required manual schema registration.
|
|
16
|
+
- **Repository Definition**: Required explicit constructor injection.
|
|
17
|
+
|
|
10
18
|
## Architecture Pattern
|
|
11
19
|
|
|
12
20
|
### Model Definition
|
|
@@ -103,12 +111,12 @@ export class ConfigurationRepository extends DefaultCRUDRepository<typeof config
|
|
|
103
111
|
|
|
104
112
|
## Pain Points
|
|
105
113
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
114
|
+
- **Verbose Model Definition**: Three separate declarations (table, relations, class) for each model
|
|
115
|
+
- **Manual Schema Registration**: DataSource required explicit registration of every model and relation
|
|
116
|
+
- **Unclear Repository Role**: Repository just wrapped datasource without defining the model-datasource binding
|
|
117
|
+
- **Declaration Order Issues**: Had to declare table before relations, relations before class
|
|
118
|
+
- **No Auto-Discovery**: Adding a new model required updates in multiple places
|
|
119
|
+
- **Tight Coupling**: Changes to model structure required updates in datasource configuration
|
|
112
120
|
|
|
113
121
|
## File Structure
|
|
114
122
|
|
|
@@ -132,6 +140,6 @@ src/
|
|
|
132
140
|
- `drizzle-orm`: ORM layer
|
|
133
141
|
- `drizzle-zod`: Schema validation
|
|
134
142
|
|
|
135
|
-
|
|
143
|
+
## No Breaking Changes
|
|
136
144
|
|
|
137
|
-
|
|
145
|
+
This document describes the initial state of the architecture.
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Model-Repository-DataSource Refactor
|
|
3
|
+
description: Major architecture refactor following Loopback 4 patterns with auto-discovery
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Changelog - 2025-12-16
|
|
7
|
+
|
|
8
|
+
## Model-Repository-DataSource Architecture Refactor
|
|
9
|
+
|
|
10
|
+
Major architecture refactor following Loopback 4 patterns with auto-discovery.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **Self-Contained Models**: Model is self-contained with schema and relations.
|
|
15
|
+
- **Repository Auto-Resolution**: Repository connects Model to DataSource (defines the binding).
|
|
16
|
+
- **DataSource Auto-Discovery**: DataSource auto-discovers schemas from registered repositories.
|
|
17
|
+
|
|
18
|
+
## Breaking Changes
|
|
19
|
+
|
|
20
|
+
> [!WARNING]
|
|
21
|
+
> This section contains changes that require migration or manual updates to existing code.
|
|
22
|
+
|
|
23
|
+
### 1. Model Static Properties
|
|
24
|
+
|
|
25
|
+
**Before:**
|
|
26
|
+
```typescript
|
|
27
|
+
const userTable = pgTable('User', {...});
|
|
28
|
+
const userRelations = createRelations({...});
|
|
29
|
+
|
|
30
|
+
@model({ type: 'entity' })
|
|
31
|
+
export class User extends BaseEntity<typeof userTable> {
|
|
32
|
+
constructor() {
|
|
33
|
+
super({ name: 'User', schema: userTable });
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**After:**
|
|
39
|
+
```typescript
|
|
40
|
+
@model({ type: 'entity' })
|
|
41
|
+
export class User extends BaseEntity<typeof User.schema> {
|
|
42
|
+
static override schema = pgTable('User', {...});
|
|
43
|
+
static override relations = () => ({...});
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 2. Repository Constructor
|
|
48
|
+
|
|
49
|
+
**Before:**
|
|
50
|
+
```typescript
|
|
51
|
+
@repository({})
|
|
52
|
+
export class UserRepository extends DefaultCRUDRepository<typeof userTable> {
|
|
53
|
+
constructor(@inject({ key: 'datasources.PostgresDataSource' }) dataSource: IDataSource) {
|
|
54
|
+
super({ dataSource, entityClass: User, relations: userRelations.definitions });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**After:**
|
|
60
|
+
```typescript
|
|
61
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
62
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
63
|
+
// No constructor needed!
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. DataSource Schema
|
|
68
|
+
|
|
69
|
+
**Before:**
|
|
70
|
+
```typescript
|
|
71
|
+
@datasource({})
|
|
72
|
+
export class PostgresDataSource extends BaseDataSource {
|
|
73
|
+
constructor() {
|
|
74
|
+
super({
|
|
75
|
+
name: PostgresDataSource.name,
|
|
76
|
+
driver: 'node-postgres',
|
|
77
|
+
config: {...},
|
|
78
|
+
schema: { User: userTable, userRelations: userRelations.relations, ... },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**After:**
|
|
85
|
+
```typescript
|
|
86
|
+
@datasource({ driver: 'node-postgres' })
|
|
87
|
+
export class PostgresDataSource extends BaseDataSource {
|
|
88
|
+
constructor() {
|
|
89
|
+
super({
|
|
90
|
+
name: PostgresDataSource.name,
|
|
91
|
+
driver: 'node-postgres',
|
|
92
|
+
config: {...},
|
|
93
|
+
// NO schema - auto-discovered!
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## New Features
|
|
100
|
+
|
|
101
|
+
### Self-Contained Models
|
|
102
|
+
|
|
103
|
+
**File:** `packages/core/src/base/models/base.ts`
|
|
104
|
+
|
|
105
|
+
**Problem:** Models were defined in three separate declarations (table, relations, class).
|
|
106
|
+
|
|
107
|
+
**Solution:** Models now define schema and relations as static properties.
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
@model({ type: 'entity' })
|
|
111
|
+
export class Configuration extends BaseEntity<typeof Configuration.schema> {
|
|
112
|
+
static override schema = pgTable('Configuration', {
|
|
113
|
+
...generateIdColumnDefs({ id: { dataType: 'string' } }),
|
|
114
|
+
...generateTzColumnDefs(),
|
|
115
|
+
code: text('code').notNull(),
|
|
116
|
+
group: text('group').notNull(),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
static override relations = () => ({
|
|
120
|
+
creator: {
|
|
121
|
+
type: 'one' as const,
|
|
122
|
+
target: () => User,
|
|
123
|
+
fields: [Configuration.schema.createdBy],
|
|
124
|
+
references: () => [User.schema.id],
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Benefits:**
|
|
131
|
+
- Simplified Model Definition
|
|
132
|
+
- Better Type Safety
|
|
133
|
+
|
|
134
|
+
### Repository Auto-Resolution
|
|
135
|
+
|
|
136
|
+
**File:** `packages/core/src/base/repositories/core/base.ts`
|
|
137
|
+
|
|
138
|
+
**Problem:** Repositories required explicit constructor injection and parameter passing.
|
|
139
|
+
|
|
140
|
+
**Solution:** Repositories now use `@repository` decorator for model-datasource binding.
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
@repository({
|
|
144
|
+
model: Configuration,
|
|
145
|
+
dataSource: PostgresDataSource,
|
|
146
|
+
})
|
|
147
|
+
export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {
|
|
148
|
+
// No constructor needed!
|
|
149
|
+
|
|
150
|
+
async findByCode(code: string) {
|
|
151
|
+
return this.findOne({ filter: { where: { code } } });
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Benefits:**
|
|
157
|
+
- Reduced Boilerplate
|
|
158
|
+
- Clear Repository Role
|
|
159
|
+
|
|
160
|
+
### DataSource Auto-Discovery
|
|
161
|
+
|
|
162
|
+
**File:** `packages/core/src/base/datasources/base.ts`
|
|
163
|
+
|
|
164
|
+
**Problem:** DataSources required manual schema registration of every model and relation.
|
|
165
|
+
|
|
166
|
+
**Solution:** DataSources automatically discover their schema from repository bindings.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
@datasource({ driver: 'node-postgres' })
|
|
170
|
+
export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
|
|
171
|
+
constructor() {
|
|
172
|
+
super({
|
|
173
|
+
name: PostgresDataSource.name,
|
|
174
|
+
driver: 'node-postgres',
|
|
175
|
+
config: { /* connection config */ },
|
|
176
|
+
// NO schema property - auto-discovered!
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
override configure(): ValueOrPromise<void> {
|
|
181
|
+
const schema = this.getSchema(); // Auto-discovers from @repository bindings
|
|
182
|
+
this.connector = drizzle({ client: new Pool(this.settings), schema });
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
**Benefits:**
|
|
188
|
+
- No Manual Schema Registration
|
|
189
|
+
- Decoupled Models and DataSources
|
|
190
|
+
|
|
191
|
+
## Files Changed
|
|
192
|
+
|
|
193
|
+
### Core Package (`packages/core`)
|
|
194
|
+
|
|
195
|
+
| File | Changes |
|
|
196
|
+
|------|---------|
|
|
197
|
+
| `src/base/models/base.ts` | Added static `schema`, `relations`, `TABLE_NAME` |
|
|
198
|
+
| `src/base/datasources/base.ts` | Added auto-discovery via `buildAutoDiscoveredSchema()` |
|
|
199
|
+
| `src/base/repositories/core/base.ts` | Added lazy resolution, static container reference |
|
|
200
|
+
| `src/base/repositories/core/readable.ts` | Made constructor opts optional |
|
|
201
|
+
| `src/base/repositories/core/persistable.ts` | Made constructor opts optional |
|
|
202
|
+
| `src/base/repositories/core/default-crud.ts` | Added documentation |
|
|
203
|
+
| `src/base/metadata/persistents.ts` | Updated decorators for auto-discovery |
|
|
204
|
+
| `src/base/applications/base.ts` | Added `AbstractRepository.setContainer(this)` |
|
|
205
|
+
| `src/components/static-asset/models/base.model.ts` | Updated to new pattern |
|
|
206
|
+
|
|
207
|
+
### Helpers Package (`packages/helpers`)
|
|
208
|
+
|
|
209
|
+
| File | Changes |
|
|
210
|
+
|------|---------|
|
|
211
|
+
| `src/helpers/inversion/common/types.ts` | Added `IModelMetadata`, `IRelationDefinition`, `IModelStatic`, `IRepositoryMetadata`, `IRepositoryBinding` |
|
|
212
|
+
| `src/helpers/inversion/registry.ts` | Added `registerModel`, `registerRepositoryBinding`, `buildDataSourceSchema` |
|
|
213
|
+
|
|
214
|
+
### Examples (`examples/vert`)
|
|
215
|
+
|
|
216
|
+
| File | Changes |
|
|
217
|
+
|------|---------|
|
|
218
|
+
| `src/models/entities/user.model.ts` | Updated to static schema pattern |
|
|
219
|
+
| `src/models/entities/configuration.model.ts` | Updated to static schema pattern |
|
|
220
|
+
| `src/datasources/postgres.datasource.ts` | Removed manual schema registration |
|
|
221
|
+
| `src/repositories/user.repository.ts` | Updated to use `@repository` decorator |
|
|
222
|
+
| `src/repositories/configuration.repository.ts` | Updated to use `@repository` decorator |
|
|
223
|
+
|
|
224
|
+
## Migration Guide
|
|
225
|
+
|
|
226
|
+
> [!NOTE]
|
|
227
|
+
> Follow these steps if you're upgrading from a previous version.
|
|
228
|
+
|
|
229
|
+
### Step 1: Update Models
|
|
230
|
+
|
|
231
|
+
Update models to use static properties for schema and relations.
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// From
|
|
235
|
+
@model({ type: 'entity' })
|
|
236
|
+
export class User extends BaseEntity<typeof userTable> {
|
|
237
|
+
constructor() {
|
|
238
|
+
super({ name: 'User', schema: userTable });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// To
|
|
243
|
+
@model({ type: 'entity' })
|
|
244
|
+
export class User extends BaseEntity<typeof User.schema> {
|
|
245
|
+
static override schema = pgTable('User', {...});
|
|
246
|
+
static override relations = () => ({...});
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Step 2: Update Repositories
|
|
251
|
+
|
|
252
|
+
Update repositories to use the `@repository` decorator with `model` and `dataSource`.
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
// From
|
|
256
|
+
@repository({})
|
|
257
|
+
export class UserRepository extends DefaultCRUDRepository<typeof userTable> {
|
|
258
|
+
constructor(@inject({ key: 'datasources.PostgresDataSource' }) dataSource: IDataSource) {
|
|
259
|
+
super({ dataSource, entityClass: User, relations: userRelations.definitions });
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// To
|
|
264
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
265
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
266
|
+
// No constructor needed!
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### Step 3: Update DataSources
|
|
271
|
+
|
|
272
|
+
Remove manual schema registration from DataSources.
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
// From
|
|
276
|
+
@datasource({})
|
|
277
|
+
export class PostgresDataSource extends BaseDataSource {
|
|
278
|
+
constructor() {
|
|
279
|
+
super({
|
|
280
|
+
name: PostgresDataSource.name,
|
|
281
|
+
driver: 'node-postgres',
|
|
282
|
+
config: {...},
|
|
283
|
+
schema: { User: userTable, userRelations: userRelations.relations, ... },
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// To
|
|
289
|
+
@datasource({ driver: 'node-postgres' })
|
|
290
|
+
export class PostgresDataSource extends BaseDataSource {
|
|
291
|
+
constructor() {
|
|
292
|
+
super({
|
|
293
|
+
name: PostgresDataSource.name,
|
|
294
|
+
driver: 'node-postgres',
|
|
295
|
+
config: {...},
|
|
296
|
+
// NO schema - auto-discovered!
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
```
|
|
@@ -1,22 +1,90 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Inversion of Control Refactor
|
|
3
|
+
description: Dependency Injection system extracted to standalone package
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Changelog - 2025-12-17
|
|
2
7
|
|
|
3
|
-
##
|
|
8
|
+
## Inversion of Control Refactor
|
|
4
9
|
|
|
5
10
|
The Dependency Injection (DI) system has been extracted from `packages/helpers` into a standalone package `@venizia/ignis-inversion`.
|
|
6
11
|
|
|
7
|
-
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **New Package**: `@venizia/ignis-inversion` created.
|
|
15
|
+
- **Separation of Concerns**: `inversion` package has zero dependencies on the rest of the framework (except `lodash`, `reflect-metadata`, `zod`).
|
|
16
|
+
- **Refactor**: `packages/core` now imports DI primitives from `@venizia/ignis-inversion`.
|
|
17
|
+
|
|
18
|
+
## Breaking Changes
|
|
19
|
+
|
|
20
|
+
> [!WARNING]
|
|
21
|
+
> This section contains changes that require migration or manual updates to existing code.
|
|
22
|
+
|
|
23
|
+
### 1. Deep Imports
|
|
24
|
+
|
|
25
|
+
**Before:**
|
|
26
|
+
```typescript
|
|
27
|
+
import { inject } from '@venizia/ignis-helpers/src/helpers/inversion';
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**After:**
|
|
31
|
+
```typescript
|
|
32
|
+
import { inject } from '@venizia/ignis-inversion';
|
|
33
|
+
// OR via re-exports (if available)
|
|
34
|
+
import { inject } from '@venizia/ignis';
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Deep imports to `helpers/inversion` within `@venizia/ignis-helpers` will no longer work as the directory has been removed.
|
|
38
|
+
|
|
39
|
+
## Files Changed
|
|
40
|
+
|
|
41
|
+
### Core Package (`packages/core`)
|
|
42
|
+
|
|
43
|
+
| File | Changes |
|
|
44
|
+
|------|---------|
|
|
45
|
+
| `src/helpers/inversion/` | Refactored to re-export from `@venizia/ignis-inversion` and add core extensions |
|
|
46
|
+
| `src/mixins/server-config.mixin.ts` | Removed |
|
|
47
|
+
|
|
48
|
+
### Helpers Package (`packages/helpers`)
|
|
49
|
+
|
|
50
|
+
| File | Changes |
|
|
51
|
+
|------|---------|
|
|
52
|
+
| `src/helpers/inversion/` | **Deleted** (Moved to new package) |
|
|
53
|
+
|
|
54
|
+
### Inversion Package (`packages/inversion`)
|
|
55
|
+
|
|
56
|
+
| File | Changes |
|
|
57
|
+
|------|---------|
|
|
58
|
+
| `package.json` | **Created** |
|
|
59
|
+
| `src/` | **Created** (Content moved from helpers) |
|
|
60
|
+
|
|
61
|
+
### Examples (`examples/vert`)
|
|
62
|
+
|
|
63
|
+
| File | Changes |
|
|
64
|
+
|------|---------|
|
|
65
|
+
| `package.json` | Added `@venizia/ignis-inversion` dependency |
|
|
66
|
+
|
|
67
|
+
## Migration Guide
|
|
68
|
+
|
|
69
|
+
> [!NOTE]
|
|
70
|
+
> Follow these steps if you're upgrading from a previous version.
|
|
71
|
+
|
|
72
|
+
### Step 1: Update Imports
|
|
73
|
+
|
|
74
|
+
Check for any deep imports to `packages/helpers/src/helpers/inversion` and update them to point to `@venizia/ignis-inversion`.
|
|
8
75
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- **Refactored**: `packages/core/src/helpers/inversion` now re-exports from the new package and adds core-specific extensions.
|
|
76
|
+
```typescript
|
|
77
|
+
// Find
|
|
78
|
+
from '@venizia/ignis-helpers/src/helpers/inversion'
|
|
13
79
|
|
|
14
|
-
|
|
80
|
+
// Replace with
|
|
81
|
+
from '@venizia/ignis-inversion'
|
|
82
|
+
```
|
|
15
83
|
|
|
16
|
-
|
|
17
|
-
- **Structure**: Clearer separation of concerns. `inversion` package has zero dependencies on the rest of the framework (except `lodash`, `reflect-metadata`, `zod`).
|
|
84
|
+
### Step 2: Install New Package
|
|
18
85
|
|
|
19
|
-
|
|
86
|
+
If you are using the DI system directly, add the new package to your dependencies.
|
|
20
87
|
|
|
21
|
-
|
|
22
|
-
|
|
88
|
+
```bash
|
|
89
|
+
npm install @venizia/ignis-inversion
|
|
90
|
+
```
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Performance Optimizations
|
|
3
|
+
description: Repository layer performance improvements reducing GC pressure and improving query speed
|
|
4
|
+
---
|
|
5
|
+
|
|
1
6
|
# Changelog - 2025-12-18
|
|
2
7
|
|
|
3
8
|
## Performance Optimizations
|
|
@@ -6,10 +11,10 @@ This update focuses on performance improvements for the repository layer, reduci
|
|
|
6
11
|
|
|
7
12
|
## Overview
|
|
8
13
|
|
|
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
|
|
14
|
+
- **WeakMap Cache**: `DrizzleFilterBuilder` now caches `getTableColumns()` results.
|
|
15
|
+
- **Core API for Flat Queries**: `ReadableRepository` uses faster Drizzle Core API when possible.
|
|
16
|
+
- **Static schemaFactory Singleton**: `BaseEntity` shares a single `schemaFactory` instance across all entities.
|
|
17
|
+
- **Async/Await Refactor**: Removed redundant Promise wrappers from repository methods.
|
|
13
18
|
|
|
14
19
|
## Performance Improvements
|
|
15
20
|
|
|
@@ -19,7 +24,7 @@ This update focuses on performance improvements for the repository layer, reduci
|
|
|
19
24
|
|
|
20
25
|
**Problem:** `getTableColumns()` was called on every filter operation, causing repeated reflection overhead.
|
|
21
26
|
|
|
22
|
-
**Solution:** Added static WeakMap cache that stores column metadata per schema
|
|
27
|
+
**Solution:** Added static WeakMap cache that stores column metadata per schema.
|
|
23
28
|
|
|
24
29
|
```typescript
|
|
25
30
|
export class DrizzleFilterBuilder extends BaseHelper {
|
|
@@ -44,18 +49,14 @@ export class DrizzleFilterBuilder extends BaseHelper {
|
|
|
44
49
|
- First call: `getTableColumns(schema)` → cached
|
|
45
50
|
- Subsequent calls: Retrieved from WeakMap (O(1) lookup)
|
|
46
51
|
- 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
|
|
|
52
|
-
### 2. Core API for Flat Queries
|
|
53
|
+
### 2. Core API for Flat Queries
|
|
53
54
|
|
|
54
55
|
**File:** `packages/core/src/base/repositories/core/readable.ts`
|
|
55
56
|
|
|
56
57
|
**Problem:** All queries used Drizzle's Query API, which has overhead for relational mapping even when not needed.
|
|
57
58
|
|
|
58
|
-
**Solution:** Automatically use Drizzle Core API for flat queries (no relations, no field selection)
|
|
59
|
+
**Solution:** Automatically use Drizzle Core API for flat queries (no relations, no field selection).
|
|
59
60
|
|
|
60
61
|
```typescript
|
|
61
62
|
// Automatic optimization - no code changes needed
|
|
@@ -69,41 +70,17 @@ const users = await repo.find({
|
|
|
69
70
|
// Uses: db.select().from(table).where(...).orderBy(...).limit(10)
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
|
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
|
-
```
|
|
73
|
+
| Scenario | Improvement |
|
|
74
|
+
|----------|-------------|
|
|
75
|
+
| Simple `find()` queries | ~15-20% faster |
|
|
92
76
|
|
|
93
77
|
### 3. Static schemaFactory Singleton
|
|
94
78
|
|
|
95
79
|
**File:** `packages/core/src/base/models/base.ts`
|
|
96
80
|
|
|
97
|
-
**Problem:** New `schemaFactory` was created for every `BaseEntity` instance
|
|
81
|
+
**Problem:** New `schemaFactory` was created for every `BaseEntity` instance, causing memory overhead.
|
|
98
82
|
|
|
99
|
-
|
|
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:
|
|
83
|
+
**Solution:** Lazy singleton pattern shared across all instances.
|
|
107
84
|
|
|
108
85
|
```typescript
|
|
109
86
|
// After - shared singleton
|
|
@@ -120,22 +97,11 @@ protected static get schemaFactory(): ReturnType<typeof createSchemaFactory> {
|
|
|
120
97
|
|
|
121
98
|
### 4. Async/Await Refactor
|
|
122
99
|
|
|
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()`:
|
|
100
|
+
**Files:** `packages/core/src/base/repositories/core/readable.ts`, `packages/core/src/base/repositories/core/persistable.ts`
|
|
128
101
|
|
|
129
|
-
|
|
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
|
-
```
|
|
102
|
+
**Problem:** Every CRUD method wrapped existing promises in `new Promise()`.
|
|
137
103
|
|
|
138
|
-
**Solution:** Direct async/await
|
|
104
|
+
**Solution:** Direct async/await.
|
|
139
105
|
|
|
140
106
|
```typescript
|
|
141
107
|
// After - Clean and efficient
|
|
@@ -147,46 +113,18 @@ return { count };
|
|
|
147
113
|
- Eliminates extra microtask queue entries
|
|
148
114
|
- Reduces ~200-400 bytes memory per Promise on V8
|
|
149
115
|
- 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
116
|
|
|
169
117
|
## Files Changed
|
|
170
118
|
|
|
171
|
-
### Core Package
|
|
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
|
|
119
|
+
### Core Package (`packages/core`)
|
|
180
120
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
|
184
|
-
|
|
185
|
-
|
|
|
186
|
-
|
|
|
187
|
-
| Entity instantiation | Reduced memory per instance (schemaFactory) |
|
|
188
|
-
| All CRUD operations | Reduced GC pressure (async/await) |
|
|
121
|
+
| File | Changes |
|
|
122
|
+
|------|---------|
|
|
123
|
+
| `src/base/repositories/operators/filter.ts` | WeakMap cache for `getTableColumns()` |
|
|
124
|
+
| `src/base/repositories/core/readable.ts` | Core API optimization, async/await refactor |
|
|
125
|
+
| `src/base/repositories/core/persistable.ts` | Async/await refactor |
|
|
126
|
+
| `src/base/models/base.ts` | Static schemaFactory singleton |
|
|
189
127
|
|
|
190
128
|
## No Breaking Changes
|
|
191
129
|
|
|
192
|
-
All changes are internal optimizations. No API changes or migration required.
|
|
130
|
+
All changes are internal optimizations. No API changes or migration required.
|