@venizia/ignis-docs 0.0.1-8 → 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/2025-12-16-initial-architecture.md +145 -0
- package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +300 -0
- package/wiki/changelogs/2025-12-17-refactor.md +90 -0
- package/wiki/changelogs/2025-12-18-performance-optimizations.md +130 -0
- package/wiki/changelogs/2025-12-18-repository-validation-security.md +249 -0
- package/wiki/changelogs/index.md +33 -0
- package/wiki/changelogs/planned-transaction-support.md +216 -0
- package/wiki/changelogs/template.md +123 -0
- package/wiki/get-started/5-minute-quickstart.md +1 -1
- package/wiki/get-started/best-practices/api-usage-examples.md +12 -10
- package/wiki/get-started/best-practices/architectural-patterns.md +2 -2
- package/wiki/get-started/best-practices/common-pitfalls.md +7 -5
- package/wiki/get-started/best-practices/contribution-workflow.md +2 -0
- package/wiki/get-started/best-practices/data-modeling.md +91 -40
- package/wiki/get-started/best-practices/security-guidelines.md +3 -1
- package/wiki/get-started/building-a-crud-api.md +63 -78
- 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/controllers.md +14 -14
- package/wiki/get-started/core-concepts/persistent.md +383 -431
- package/wiki/get-started/core-concepts/services.md +21 -27
- package/wiki/get-started/quickstart.md +1 -1
- package/wiki/references/base/bootstrapping.md +789 -0
- package/wiki/references/base/components.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 +98 -5
- package/wiki/references/base/models.md +398 -28
- package/wiki/references/base/repositories.md +475 -22
- package/wiki/references/base/services.md +2 -2
- package/wiki/references/components/authentication.md +228 -10
- package/wiki/references/components/health-check.md +1 -1
- package/wiki/references/components/index.md +1 -1
- package/wiki/references/components/swagger.md +1 -1
- package/wiki/references/helpers/error.md +2 -2
- package/wiki/references/helpers/inversion.md +8 -3
- package/wiki/references/src-details/boot.md +379 -0
- package/wiki/references/src-details/core.md +8 -7
- package/wiki/references/src-details/inversion.md +4 -4
- package/wiki/references/utilities/request.md +16 -7
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Repository Validation & Security
|
|
3
|
+
description: Strict validation for @repository decorator and security fixes for filter operators
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Changelog - 2025-12-18
|
|
7
|
+
|
|
8
|
+
## Repository Validation & Security Improvements
|
|
9
|
+
|
|
10
|
+
This update adds strict validation to the `@repository` decorator and fixes several security vulnerabilities in the filter operators.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **@repository Decorator**: Now requires both `model` AND `dataSource` for schema auto-discovery.
|
|
15
|
+
- **Constructor Validation**: First parameter must extend `AbstractDataSource` (enforced via reflection).
|
|
16
|
+
- **DataSource Auto-Discovery**: Schema is automatically built from `@repository` bindings.
|
|
17
|
+
- **Filter Security**: Fixed empty IN array bypass, invalid column handling, BETWEEN validation.
|
|
18
|
+
- **PostgreSQL Compatibility**: REGEXP now uses PostgreSQL POSIX operators.
|
|
19
|
+
- **UUID Generation**: Now uses native PostgreSQL `uuid` type with `gen_random_uuid()`.
|
|
20
|
+
|
|
21
|
+
## Breaking Changes
|
|
22
|
+
|
|
23
|
+
> [!WARNING]
|
|
24
|
+
> This section contains changes that require migration or manual updates to existing code.
|
|
25
|
+
|
|
26
|
+
### 1. Repository Constructor Signature
|
|
27
|
+
|
|
28
|
+
**Before:**
|
|
29
|
+
```typescript
|
|
30
|
+
constructor(opts: {
|
|
31
|
+
entityClass: TClass<BaseEntity<EntitySchema>>;
|
|
32
|
+
relations: { [relationName: string]: TRelationConfig };
|
|
33
|
+
dataSource: IDataSource;
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**After:**
|
|
38
|
+
```typescript
|
|
39
|
+
constructor(ds?: IDataSource, opts?: { entityClass?: TClass<BaseEntity<EntitySchema>> })
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. @repository Decorator Requirements
|
|
43
|
+
|
|
44
|
+
**Before:**
|
|
45
|
+
```typescript
|
|
46
|
+
// Would silently fail to register model
|
|
47
|
+
@repository({ model: User })
|
|
48
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**After:**
|
|
52
|
+
```typescript
|
|
53
|
+
// Throws Error: Invalid metadata | Missing 'dataSource'
|
|
54
|
+
@repository({ model: User })
|
|
55
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
56
|
+
|
|
57
|
+
// Correct
|
|
58
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
59
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### 3. Constructor Parameter Type
|
|
63
|
+
|
|
64
|
+
**Before:**
|
|
65
|
+
```typescript
|
|
66
|
+
constructor(
|
|
67
|
+
@inject({ key: 'datasources.PostgresDataSource' })
|
|
68
|
+
dataSource: any, // Would compile but is wrong
|
|
69
|
+
)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**After:**
|
|
73
|
+
```typescript
|
|
74
|
+
constructor(
|
|
75
|
+
@inject({ key: 'datasources.PostgresDataSource' })
|
|
76
|
+
dataSource: PostgresDataSource, // Must be concrete DataSource type
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### 4. Filter Column Validation
|
|
81
|
+
|
|
82
|
+
**Before:**
|
|
83
|
+
```typescript
|
|
84
|
+
// Silently ignored 'invalidColumn'
|
|
85
|
+
await repo.find({ filter: { where: { invalidColumn: 'value' } } });
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**After:**
|
|
89
|
+
```typescript
|
|
90
|
+
// Error: [toWhere] Table: User | Column NOT FOUND | key: 'invalidColumn'
|
|
91
|
+
await repo.find({ filter: { where: { invalidColumn: 'value' } } });
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## New Features
|
|
95
|
+
|
|
96
|
+
### DataSource Schema Auto-Discovery
|
|
97
|
+
|
|
98
|
+
**File:** `packages/core/src/base/datasources/base.ts`
|
|
99
|
+
|
|
100
|
+
**Problem:** Manual schema merging was error-prone and verbose.
|
|
101
|
+
|
|
102
|
+
**Solution:** DataSources automatically discover their schema from `@repository` bindings.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
@datasource({ driver: 'node-postgres' })
|
|
106
|
+
export class PostgresDataSource extends BaseDataSource<...> {
|
|
107
|
+
constructor() {
|
|
108
|
+
super({
|
|
109
|
+
name: PostgresDataSource.name,
|
|
110
|
+
config: { /* ... */ },
|
|
111
|
+
// Schema auto-discovered from @repository decorators!
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### UUID Type Improvement
|
|
118
|
+
|
|
119
|
+
**File:** `packages/core/src/base/models/enrichers/id.enricher.ts`
|
|
120
|
+
|
|
121
|
+
**Problem:** JS-generated UUIDs were stored as text, which is less efficient.
|
|
122
|
+
|
|
123
|
+
**Solution:** Changed to native PostgreSQL `uuid` type with `gen_random_uuid()`.
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// After: uuid('id').defaultRandom().primaryKey()
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Case-Insensitive REGEXP (IREGEXP)
|
|
130
|
+
|
|
131
|
+
**File:** `packages/core/src/base/repositories/operators/query.ts`
|
|
132
|
+
|
|
133
|
+
**Problem:** PostgreSQL regex matching is case-sensitive by default.
|
|
134
|
+
|
|
135
|
+
**Solution:** Added `IREGEXP` operator for case-insensitive matching (`~*`).
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
await repo.find({ filter: { where: { name: { IREGEXP: '^john' } } } });
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Repository Log Option
|
|
142
|
+
|
|
143
|
+
**File:** `packages/core/src/base/repositories/core/persistable.ts`
|
|
144
|
+
|
|
145
|
+
**Problem:** Debugging repository operations was difficult without logging.
|
|
146
|
+
|
|
147
|
+
**Solution:** Added `log` option to all CRUD operations.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
await repo.create({
|
|
151
|
+
data: { name: 'John' },
|
|
152
|
+
options: {
|
|
153
|
+
log: { use: true, level: 'debug' }
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Security Fixes
|
|
159
|
+
|
|
160
|
+
### Empty IN Array Bypass
|
|
161
|
+
|
|
162
|
+
**Vulnerability:** Empty `IN` arrays would return `true`, bypassing security filters.
|
|
163
|
+
|
|
164
|
+
**Fix:** Empty `IN` arrays now return `sql\`false\``.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// Before: WHERE id IN () => true (bypass)
|
|
168
|
+
// After: WHERE false => no records
|
|
169
|
+
await repo.find({ filter: { where: { id: { IN: [] } } } });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### BETWEEN Validation
|
|
173
|
+
|
|
174
|
+
**Vulnerability:** Invalid `BETWEEN` values could cause unexpected behavior.
|
|
175
|
+
|
|
176
|
+
**Fix:** `BETWEEN` now validates input is an array of exactly 2 elements.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
// Throws: [BETWEEN] Invalid value: expected array of 2 elements
|
|
180
|
+
await repo.find({ filter: { where: { age: { BETWEEN: [10] } } } });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Files Changed
|
|
184
|
+
|
|
185
|
+
### Core Package (`packages/core`)
|
|
186
|
+
|
|
187
|
+
| File | Changes |
|
|
188
|
+
|------|---------|
|
|
189
|
+
| `src/base/models/base.ts` | Static schema/relations support, IEntity interface |
|
|
190
|
+
| `src/base/models/common/types.ts` | IEntity interface definition |
|
|
191
|
+
| `src/base/models/enrichers/id.enricher.ts` | Native PostgreSQL UUID type |
|
|
192
|
+
| `src/base/repositories/core/base.ts` | Constructor signature change, relations auto-resolution |
|
|
193
|
+
| `src/base/repositories/core/readable.ts` | Constructor change, getQueryInterface validation |
|
|
194
|
+
| `src/base/repositories/core/persistable.ts` | Constructor change, log option, TypeScript overloads |
|
|
195
|
+
| `src/base/repositories/operators/filter.ts` | Column validation, error throwing |
|
|
196
|
+
| `src/base/repositories/operators/query.ts` | REGEXP/IREGEXP fix, BETWEEN validation, IN empty array fix |
|
|
197
|
+
| `src/base/datasources/base.ts` | Schema auto-discovery feature |
|
|
198
|
+
| `src/base/metadata/persistents.ts` | Repository decorator validation, constructor type validation |
|
|
199
|
+
|
|
200
|
+
### Examples (`examples/vert`)
|
|
201
|
+
|
|
202
|
+
| File | Changes |
|
|
203
|
+
|------|---------|
|
|
204
|
+
| `src/repositories/user.repository.ts` | Updated to proper types |
|
|
205
|
+
| `src/repositories/configuration.repository.ts` | Updated patterns |
|
|
206
|
+
| `src/datasources/postgres.datasource.ts` | Using auto-discovery |
|
|
207
|
+
|
|
208
|
+
## Migration Guide
|
|
209
|
+
|
|
210
|
+
> [!NOTE]
|
|
211
|
+
> Follow these steps if you're upgrading from a previous version.
|
|
212
|
+
|
|
213
|
+
### Step 1: Update Repository Constructors
|
|
214
|
+
|
|
215
|
+
The repository constructor signature has changed. Update all repository classes.
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// Option A: Zero boilerplate (recommended)
|
|
219
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
220
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
221
|
+
// No constructor needed!
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Option B: With explicit constructor
|
|
225
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
226
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
227
|
+
constructor(
|
|
228
|
+
@inject({ key: 'datasources.PostgresDataSource' }) ds: PostgresDataSource,
|
|
229
|
+
) {
|
|
230
|
+
super(ds); // Just pass dataSource
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Step 2: Update @repository Decorators
|
|
236
|
+
|
|
237
|
+
Ensure all `@repository` decorators have both `model` and `dataSource`.
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
@repository({ model: YourModel, dataSource: YourDataSource })
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Step 3: REGEXP Migration (PostgreSQL Users)
|
|
244
|
+
|
|
245
|
+
Replace MySQL-style REGEXP with PostgreSQL syntax:
|
|
246
|
+
- `REGEXP` → uses `~` (case-sensitive)
|
|
247
|
+
- `IREGEXP` → uses `~*` (case-insensitive)
|
|
248
|
+
|
|
249
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Changelogs
|
|
3
|
+
description: History of significant changes, refactors, and updates to the Ignis framework
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Changelogs
|
|
7
|
+
|
|
8
|
+
This section tracks the history of significant changes, refactors, and updates to the Ignis framework.
|
|
9
|
+
|
|
10
|
+
## Planned Features
|
|
11
|
+
|
|
12
|
+
| Feature | Description | Priority |
|
|
13
|
+
|---------|-------------|----------|
|
|
14
|
+
| [Transaction Support](./planned-transaction-support) | Loopback 4-style explicit transaction objects with isolation levels | Future |
|
|
15
|
+
|
|
16
|
+
## Recent Changes
|
|
17
|
+
|
|
18
|
+
| Date | Title | Type |
|
|
19
|
+
|------|-------|------|
|
|
20
|
+
| 2025-12-18 | [Performance Optimizations](./2025-12-18-performance-optimizations) | Enhancement |
|
|
21
|
+
| 2025-12-18 | [Repository Validation & Security](./2025-12-18-repository-validation-security) | Breaking Change, Security |
|
|
22
|
+
| 2025-12-17 | [Inversion of Control Refactor](./2025-12-17-refactor) | Refactor |
|
|
23
|
+
| 2025-12-16 | [Model-Repository-DataSource Refactor](./2025-12-16-model-repo-datasource-refactor) | Breaking Change |
|
|
24
|
+
| 2025-12-16 | [Initial Architecture](./2025-12-16-initial-architecture) | Documentation |
|
|
25
|
+
|
|
26
|
+
## How to Read Changelogs
|
|
27
|
+
|
|
28
|
+
Each changelog entry includes:
|
|
29
|
+
- **Overview**: Summary of changes
|
|
30
|
+
- **Breaking Changes**: Any changes that require migration
|
|
31
|
+
- **New Features**: New capabilities added
|
|
32
|
+
- **Files Changed**: List of modified files
|
|
33
|
+
- **Migration Guide**: Steps to update existing code (if applicable)
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Planned - Transaction Support
|
|
3
|
+
description: Implementation plan for Loopback 4-style explicit transaction objects
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Planned: Transaction Support
|
|
7
|
+
|
|
8
|
+
**Status:** Planned (Not Yet Implemented)
|
|
9
|
+
**Priority:** Future Enhancement
|
|
10
|
+
|
|
11
|
+
## Goal
|
|
12
|
+
|
|
13
|
+
Implement Loopback 4-style explicit transaction objects, allowing transactions to be passed through multiple services/repositories instead of using Drizzle's callback-based approach.
|
|
14
|
+
|
|
15
|
+
## Target API
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
// Default isolation level (READ COMMITTED)
|
|
19
|
+
const tx = await userRepo.beginTransaction();
|
|
20
|
+
|
|
21
|
+
// Or with specific isolation level
|
|
22
|
+
const tx = await userRepo.beginTransaction({
|
|
23
|
+
isolationLevel: 'SERIALIZABLE'
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
await userRepo.create({ data, options: { transaction: tx } });
|
|
28
|
+
await profileRepo.create({ data, options: { transaction: tx } });
|
|
29
|
+
await tx.commit();
|
|
30
|
+
} catch (err) {
|
|
31
|
+
await tx.rollback();
|
|
32
|
+
throw err;
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Isolation Levels
|
|
37
|
+
|
|
38
|
+
| Level | Description | Use Case |
|
|
39
|
+
|-------|-------------|----------|
|
|
40
|
+
| `READ COMMITTED` | Default. Sees only committed data at query start | General use, most common |
|
|
41
|
+
| `REPEATABLE READ` | Sees snapshot from transaction start | Reports, consistent reads |
|
|
42
|
+
| `SERIALIZABLE` | Strictest. Full isolation, may throw serialization errors | Financial transactions, critical data |
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Implementation Steps
|
|
47
|
+
|
|
48
|
+
### Step 1: Define Transaction Types
|
|
49
|
+
|
|
50
|
+
**File:** `packages/core/src/base/datasources/types.ts`
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
/** PostgreSQL transaction isolation levels */
|
|
54
|
+
export type TIsolationLevel = 'READ COMMITTED' | 'REPEATABLE READ' | 'SERIALIZABLE';
|
|
55
|
+
|
|
56
|
+
/** Options for starting a transaction */
|
|
57
|
+
export interface ITransactionOptions {
|
|
58
|
+
isolationLevel?: TIsolationLevel;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Transaction object returned by beginTransaction() */
|
|
62
|
+
export interface ITransaction<Connector = TNodePostgresConnector> {
|
|
63
|
+
/** Isolated Drizzle instance bound to this transaction */
|
|
64
|
+
connector: Connector;
|
|
65
|
+
|
|
66
|
+
/** Commit the transaction */
|
|
67
|
+
commit(): Promise<void>;
|
|
68
|
+
|
|
69
|
+
/** Rollback the transaction */
|
|
70
|
+
rollback(): Promise<void>;
|
|
71
|
+
|
|
72
|
+
/** Check if transaction is still active */
|
|
73
|
+
isActive: boolean;
|
|
74
|
+
|
|
75
|
+
/** The isolation level used for this transaction */
|
|
76
|
+
isolationLevel: TIsolationLevel;
|
|
77
|
+
}
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Step 2: Add `beginTransaction()` to DataSource
|
|
81
|
+
|
|
82
|
+
**File:** `packages/core/src/base/datasources/base.ts`
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
async beginTransaction(
|
|
86
|
+
opts?: ITransactionOptions
|
|
87
|
+
): Promise<ITransaction<Connector>> {
|
|
88
|
+
// 1. Get raw client from pool
|
|
89
|
+
const pool = this.connector.client as Pool;
|
|
90
|
+
const client = await pool.connect();
|
|
91
|
+
|
|
92
|
+
// 2. Determine isolation level (default: READ COMMITTED)
|
|
93
|
+
const isolationLevel: TIsolationLevel = opts?.isolationLevel ?? 'READ COMMITTED';
|
|
94
|
+
|
|
95
|
+
// 3. Execute BEGIN with isolation level
|
|
96
|
+
await client.query(`BEGIN TRANSACTION ISOLATION LEVEL ${isolationLevel}`);
|
|
97
|
+
|
|
98
|
+
// 4. Create isolated Drizzle instance with this client
|
|
99
|
+
const txConnector = drizzle({ client, schema: this.schema });
|
|
100
|
+
|
|
101
|
+
// 5. Return transaction object
|
|
102
|
+
let isActive = true;
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
connector: txConnector as Connector,
|
|
106
|
+
isActive,
|
|
107
|
+
isolationLevel,
|
|
108
|
+
|
|
109
|
+
async commit() {
|
|
110
|
+
if (!isActive) throw new Error('Transaction already ended');
|
|
111
|
+
try {
|
|
112
|
+
await client.query('COMMIT');
|
|
113
|
+
} finally {
|
|
114
|
+
isActive = false;
|
|
115
|
+
client.release();
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
async rollback() {
|
|
120
|
+
if (!isActive) throw new Error('Transaction already ended');
|
|
121
|
+
try {
|
|
122
|
+
await client.query('ROLLBACK');
|
|
123
|
+
} finally {
|
|
124
|
+
isActive = false;
|
|
125
|
+
client.release();
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Step 3: Update Repository Base
|
|
133
|
+
|
|
134
|
+
**File:** `packages/core/src/base/repositories/core/base.ts`
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
// Add method to start transaction (delegates to DataSource)
|
|
138
|
+
async beginTransaction(opts?: ITransactionOptions): Promise<ITransaction> {
|
|
139
|
+
return this.dataSource.beginTransaction(opts);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Replace this.connector with getConnector(opts)
|
|
143
|
+
protected getConnector(opts?: { transaction?: ITransaction }) {
|
|
144
|
+
if (opts?.transaction) {
|
|
145
|
+
if (!opts.transaction.isActive) {
|
|
146
|
+
throw getError({ message: 'Transaction is no longer active' });
|
|
147
|
+
}
|
|
148
|
+
return opts.transaction.connector;
|
|
149
|
+
}
|
|
150
|
+
return this.dataSource.connector;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Step 4: Update CRUD Options Types
|
|
155
|
+
|
|
156
|
+
**File:** `packages/core/src/base/repositories/common/types.ts`
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
export type TTransactionOption = {
|
|
160
|
+
transaction?: ITransaction;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Add to existing option types
|
|
164
|
+
export type TCreateOptions = TTransactionOption & {
|
|
165
|
+
shouldReturn?: boolean;
|
|
166
|
+
log?: TRepositoryLogOptions;
|
|
167
|
+
};
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
### Step 5: Update CRUD Methods
|
|
171
|
+
|
|
172
|
+
**Files:** `readable.ts`, `persistable.ts`
|
|
173
|
+
|
|
174
|
+
Change all methods from:
|
|
175
|
+
```typescript
|
|
176
|
+
this.connector.insert(...)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
To:
|
|
180
|
+
```typescript
|
|
181
|
+
this.getConnector(opts.options).insert(...)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## Files to Modify
|
|
187
|
+
|
|
188
|
+
| File | Changes |
|
|
189
|
+
|------|---------|
|
|
190
|
+
| `packages/core/src/base/datasources/types.ts` | Add `TIsolationLevel`, `ITransactionOptions`, `ITransaction` |
|
|
191
|
+
| `packages/core/src/base/datasources/base.ts` | Add `beginTransaction(opts?)` method |
|
|
192
|
+
| `packages/core/src/base/repositories/common/types.ts` | Add `TTransactionOption` |
|
|
193
|
+
| `packages/core/src/base/repositories/core/base.ts` | Add `beginTransaction(opts?)`, `getConnector(opts)` |
|
|
194
|
+
| `packages/core/src/base/repositories/core/readable.ts` | Use `getConnector(opts)` in all methods |
|
|
195
|
+
| `packages/core/src/base/repositories/core/persistable.ts` | Use `getConnector(opts)` in all methods |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Breaking Changes
|
|
200
|
+
|
|
201
|
+
1. **`this.connector`** → `this.getConnector(opts)`
|
|
202
|
+
- Backward compatible when called without args
|
|
203
|
+
|
|
204
|
+
2. **Options parameter** - Now includes optional `transaction` field
|
|
205
|
+
- Non-breaking: transaction is optional
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Benefits
|
|
210
|
+
|
|
211
|
+
| Aspect | Current (Drizzle Callback) | After (Pass-through) |
|
|
212
|
+
|--------|---------------------------|----------------------|
|
|
213
|
+
| Service composition | Hard - all in one callback | Easy - pass tx anywhere |
|
|
214
|
+
| Separation of concerns | Services must know each other | Services stay independent |
|
|
215
|
+
| Testing | Complex mocking | Easy to mock tx object |
|
|
216
|
+
| Code organization | Nested callbacks | Flat, sequential flow |
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: [Short Title]
|
|
3
|
+
description: [Brief description of the changes]
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Changelog - YYYY-MM-DD
|
|
7
|
+
|
|
8
|
+
## [Main Title/Focus Area]
|
|
9
|
+
|
|
10
|
+
[A brief, high-level summary of the changes in this release. What is the main focus?]
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
- **[Change 1]**: Brief description
|
|
15
|
+
- **[Change 2]**: Brief description
|
|
16
|
+
- **[Change 3]**: Brief description
|
|
17
|
+
|
|
18
|
+
## Breaking Changes
|
|
19
|
+
|
|
20
|
+
> [!WARNING]
|
|
21
|
+
> This section contains changes that require migration or manual updates to existing code.
|
|
22
|
+
|
|
23
|
+
### 1. [Breaking Change Title]
|
|
24
|
+
|
|
25
|
+
**Before:**
|
|
26
|
+
```typescript
|
|
27
|
+
// Code that no longer works or is deprecated
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**After:**
|
|
31
|
+
```typescript
|
|
32
|
+
// New pattern
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## New Features
|
|
36
|
+
|
|
37
|
+
### [Feature Name]
|
|
38
|
+
|
|
39
|
+
**File:** `packages/core/src/path/to/file.ts`
|
|
40
|
+
|
|
41
|
+
**Problem:** [What problem does this solve?]
|
|
42
|
+
|
|
43
|
+
**Solution:** [How does it solve it?]
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// Example usage
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Benefits:**
|
|
50
|
+
- Benefit 1
|
|
51
|
+
- Benefit 2
|
|
52
|
+
|
|
53
|
+
## Security Fixes
|
|
54
|
+
|
|
55
|
+
### [Security Issue Title]
|
|
56
|
+
|
|
57
|
+
**Vulnerability:** [Describe the vulnerability]
|
|
58
|
+
|
|
59
|
+
**Fix:** [Describe the fix]
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Before: vulnerable code behavior
|
|
63
|
+
// After: secure code behavior
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Performance Improvements
|
|
67
|
+
|
|
68
|
+
### [Performance Improvement Title]
|
|
69
|
+
|
|
70
|
+
**File:** `packages/core/src/path/to/file.ts`
|
|
71
|
+
|
|
72
|
+
**Problem:** [What was slow/inefficient?]
|
|
73
|
+
|
|
74
|
+
**Solution:** [How was it optimized?]
|
|
75
|
+
|
|
76
|
+
| Scenario | Improvement |
|
|
77
|
+
|----------|-------------|
|
|
78
|
+
| [Use case 1] | [Improvement metric] |
|
|
79
|
+
| [Use case 2] | [Improvement metric] |
|
|
80
|
+
|
|
81
|
+
## Files Changed
|
|
82
|
+
|
|
83
|
+
### Core Package (`packages/core`)
|
|
84
|
+
|
|
85
|
+
| File | Changes |
|
|
86
|
+
|------|---------|
|
|
87
|
+
| `src/base/models/base.ts` | [Description of changes] |
|
|
88
|
+
| `src/base/repositories/core/readable.ts` | [Description of changes] |
|
|
89
|
+
|
|
90
|
+
### Helpers Package (`packages/helpers`)
|
|
91
|
+
|
|
92
|
+
| File | Changes |
|
|
93
|
+
|------|---------|
|
|
94
|
+
| `src/utils/index.ts` | [Description of changes] |
|
|
95
|
+
|
|
96
|
+
### Examples (`examples/vert`)
|
|
97
|
+
|
|
98
|
+
| File | Changes |
|
|
99
|
+
|------|---------|
|
|
100
|
+
| `src/models/entities/user.model.ts` | [Description of changes] |
|
|
101
|
+
|
|
102
|
+
## Migration Guide
|
|
103
|
+
|
|
104
|
+
> [!NOTE]
|
|
105
|
+
> Follow these steps if you're upgrading from a previous version.
|
|
106
|
+
|
|
107
|
+
### Step 1: [Action Name]
|
|
108
|
+
|
|
109
|
+
[Instructions]
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// Example of the change to apply
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Step 2: [Action Name]
|
|
116
|
+
|
|
117
|
+
[Instructions]
|
|
118
|
+
|
|
119
|
+
## No Breaking Changes
|
|
120
|
+
|
|
121
|
+
[Use this section instead of "Breaking Changes" and "Migration Guide" if there are no breaking changes]
|
|
122
|
+
|
|
123
|
+
All changes are internal optimizations. No API changes or migration required.
|
|
@@ -253,7 +253,7 @@ Open `http://localhost:3000/doc/explorer` to see interactive Swagger UI document
|
|
|
253
253
|
})
|
|
254
254
|
async greet(c: Context) {
|
|
255
255
|
const { name } = await c.req.json();
|
|
256
|
-
return c.json({ greeting: `Hello, ${name}!` });
|
|
256
|
+
return c.json({ greeting: `Hello, ${name}!` }, HTTP.ResultCodes.RS_2.Ok);
|
|
257
257
|
}
|
|
258
258
|
```
|
|
259
259
|
|
|
@@ -52,6 +52,7 @@ import {
|
|
|
52
52
|
get,
|
|
53
53
|
post,
|
|
54
54
|
TRouteContext,
|
|
55
|
+
HTTP,
|
|
55
56
|
} from '@venizia/ignis';
|
|
56
57
|
import { ROUTE_CONFIGS } from './definitions';
|
|
57
58
|
|
|
@@ -62,7 +63,7 @@ export class TestController extends BaseController {
|
|
|
62
63
|
@get({ configs: ROUTE_CONFIGS['/4'] })
|
|
63
64
|
getWithDecorator(context: TRouteContext<(typeof ROUTE_CONFIGS)['/4']>) {
|
|
64
65
|
// context is fully typed!
|
|
65
|
-
return context.json({ message: 'Hello from decorator', method: 'GET' });
|
|
66
|
+
return context.json({ message: 'Hello from decorator', method: 'GET' }, HTTP.ResultCodes.RS_2.Ok);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
@post({ configs: ROUTE_CONFIGS['/5'] })
|
|
@@ -71,11 +72,14 @@ export class TestController extends BaseController {
|
|
|
71
72
|
const body = context.req.valid('json');
|
|
72
73
|
|
|
73
74
|
// The response is validated against the schema
|
|
74
|
-
return context.json(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
return context.json(
|
|
76
|
+
{
|
|
77
|
+
id: crypto.randomUUID(),
|
|
78
|
+
name: body.name,
|
|
79
|
+
age: body.age,
|
|
80
|
+
},
|
|
81
|
+
HTTP.ResultCodes.RS_2.Ok,
|
|
82
|
+
);
|
|
79
83
|
}
|
|
80
84
|
}
|
|
81
85
|
```
|
|
@@ -97,7 +101,7 @@ export class TestController extends BaseController {
|
|
|
97
101
|
this.defineRoute({
|
|
98
102
|
configs: ROUTE_CONFIGS['/1'],
|
|
99
103
|
handler: context => {
|
|
100
|
-
return context.json({ message: 'Hello' });
|
|
104
|
+
return context.json({ message: 'Hello' }, HTTP.ResultCodes.RS_2.Ok);
|
|
101
105
|
},
|
|
102
106
|
});
|
|
103
107
|
|
|
@@ -106,7 +110,7 @@ export class TestController extends BaseController {
|
|
|
106
110
|
configs: ROUTE_CONFIGS['/3'],
|
|
107
111
|
}).to({
|
|
108
112
|
handler: context => {
|
|
109
|
-
return context.json({ message: 'Hello 3' });
|
|
113
|
+
return context.json({ message: 'Hello 3' }, HTTP.ResultCodes.RS_2.Ok);
|
|
110
114
|
},
|
|
111
115
|
});
|
|
112
116
|
}
|
|
@@ -260,5 +264,3 @@ export class PageController extends BaseController {
|
|
|
260
264
|
}
|
|
261
265
|
}
|
|
262
266
|
```
|
|
263
|
-
|
|
264
|
-
```
|
|
@@ -90,7 +90,7 @@ export class ConfigurationController extends _Controller {
|
|
|
90
90
|
// an instance of ConfigurationRepository here.
|
|
91
91
|
@inject({
|
|
92
92
|
key: BindingKeys.build({
|
|
93
|
-
namespace:
|
|
93
|
+
namespace: BindingNamespaces.REPOSITORY,
|
|
94
94
|
key: ConfigurationRepository.name,
|
|
95
95
|
}),
|
|
96
96
|
})
|
|
@@ -103,7 +103,7 @@ export class ConfigurationController extends _Controller {
|
|
|
103
103
|
|
|
104
104
|
## 3. Component-Based Modularity
|
|
105
105
|
|
|
106
|
-
Components bundle related features into self-contained,
|
|
106
|
+
Components bundle a group of related, reusable, and pluggable features into self-contained modules. A single component can encapsulate multiple providers, services, controllers, and repositories, essentially functioning as a mini-application that can be easily "plugged in" to any Ignis project.
|
|
107
107
|
|
|
108
108
|
**Built-in Components:**
|
|
109
109
|
- `AuthenticateComponent` - JWT authentication
|