@venizia/ignis-docs 0.0.3 → 0.0.4-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/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 +406 -17
- 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 +2 -2
- 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 +6 -0
- package/wiki/changelogs/planned-schema-migrator.md +0 -8
- 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/{get-started → guides}/core-concepts/components.md +24 -17
- 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 +31 -26
- package/wiki/references/base/components.md +24 -7
- package/wiki/references/base/controllers.md +50 -20
- package/wiki/references/base/datasources.md +30 -0
- 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 +604 -0
- package/wiki/references/base/models.md +215 -23
- package/wiki/references/base/providers.md +731 -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 -635
- 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 +14 -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 +16 -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/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 -97
- package/wiki/get-started/best-practices/security-guidelines.md +0 -99
- package/wiki/get-started/core-concepts/persistent.md +0 -539
- 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,228 @@
|
|
|
1
|
+
# Repositories Overview
|
|
2
|
+
|
|
3
|
+
Repositories are the data access layer in Ignis - they provide type-safe CRUD operations for your database entities.
|
|
4
|
+
|
|
5
|
+
**Files:** `packages/core/src/base/repositories/core/*.ts`
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
## Quick Start
|
|
9
|
+
|
|
10
|
+
If you're new to repositories, start here:
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
import { DefaultCRUDRepository, repository } from '@venizia/ignis';
|
|
14
|
+
import { Todo } from '@/models/todo.model';
|
|
15
|
+
import { PostgresDataSource } from '@/datasources/postgres.datasource';
|
|
16
|
+
|
|
17
|
+
@repository({ model: Todo, dataSource: PostgresDataSource })
|
|
18
|
+
export class TodoRepository extends DefaultCRUDRepository<typeof Todo.schema> {
|
|
19
|
+
// That's it! You get: find, findOne, create, updateById, deleteById, etc.
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
## Repository Classes
|
|
25
|
+
|
|
26
|
+
| Class | Capabilities | Use Case |
|
|
27
|
+
|-------|--------------|----------|
|
|
28
|
+
| **AbstractRepository** | Base class with properties | Extend for custom repositories |
|
|
29
|
+
| **ReadableRepository** | Read-only operations | Views, external tables |
|
|
30
|
+
| **PersistableRepository** | Read + Write operations | Rarely used directly |
|
|
31
|
+
| **DefaultCRUDRepository** | Full CRUD operations | Standard data tables |
|
|
32
|
+
|
|
33
|
+
**Most common:** Extend `DefaultCRUDRepository` for standard tables.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Available Methods
|
|
37
|
+
|
|
38
|
+
### Read Operations
|
|
39
|
+
| Method | Description | Example |
|
|
40
|
+
|--------|-------------|---------|
|
|
41
|
+
| `find(opts)` | Find multiple records | `repo.find({ filter: { where: { status: 'active' } } })` |
|
|
42
|
+
| `findOne(opts)` | Find single record | `repo.findOne({ filter: { where: { email } } })` |
|
|
43
|
+
| `findById(opts)` | Find by primary key | `repo.findById({ id: '123' })` |
|
|
44
|
+
| `count(opts)` | Count matching records | `repo.count({ where: { status: 'active' } })` |
|
|
45
|
+
| `existsWith(opts)` | Check if exists | `repo.existsWith({ where: { email } })` |
|
|
46
|
+
|
|
47
|
+
### Write Operations
|
|
48
|
+
| Method | Description | Example |
|
|
49
|
+
|--------|-------------|---------|
|
|
50
|
+
| `create(opts)` | Create single record | `repo.create({ data: { title: 'New' } })` |
|
|
51
|
+
| `createAll(opts)` | Create multiple records | `repo.createAll({ data: [{ title: 'A' }, { title: 'B' }] })` |
|
|
52
|
+
| `updateById(opts)` | Update by primary key | `repo.updateById({ id: '123', data: { title: 'Updated' } })` |
|
|
53
|
+
| `updateAll(opts)` | Update matching records | `repo.updateAll({ where: { status: 'draft' }, data: { status: 'published' } })` |
|
|
54
|
+
| `deleteById(opts)` | Delete by primary key | `repo.deleteById({ id: '123' })` |
|
|
55
|
+
| `deleteAll(opts)` | Delete matching records | `repo.deleteAll({ where: { status: 'archived' } })` |
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## Documentation Sections
|
|
59
|
+
|
|
60
|
+
This documentation is split into focused guides:
|
|
61
|
+
|
|
62
|
+
### [Filter System](/references/base/filter-system/)
|
|
63
|
+
Complete reference for querying data - operators, JSON filtering, array operators, default filters, and query patterns.
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Preview
|
|
67
|
+
await repo.find({
|
|
68
|
+
filter: {
|
|
69
|
+
where: {
|
|
70
|
+
status: 'active',
|
|
71
|
+
age: { gte: 18 },
|
|
72
|
+
'metadata.priority': { gte: 3 },
|
|
73
|
+
tags: { contains: ['featured'] }
|
|
74
|
+
},
|
|
75
|
+
order: ['createdAt DESC'],
|
|
76
|
+
limit: 20
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### [Relations & Includes](./relations.md)
|
|
82
|
+
Fetch related data using `include` for eager loading and nested queries.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Preview
|
|
86
|
+
await repo.find({
|
|
87
|
+
filter: {
|
|
88
|
+
include: [{
|
|
89
|
+
relation: 'posts',
|
|
90
|
+
scope: { where: { published: true } }
|
|
91
|
+
}]
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### [Advanced Features](./advanced.md)
|
|
97
|
+
Transactions, hidden properties, default filter bypass, performance optimization, and type inference.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
// Preview
|
|
101
|
+
const tx = await repo.beginTransaction();
|
|
102
|
+
try {
|
|
103
|
+
await repo.create({ data, options: { transaction: tx } });
|
|
104
|
+
await tx.commit();
|
|
105
|
+
} catch (e) {
|
|
106
|
+
await tx.rollback();
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### [Repository Mixins](./mixins.md)
|
|
111
|
+
Composable mixins for repository features - `DefaultFilterMixin` and `FieldsVisibilityMixin`.
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
## @repository Decorator
|
|
115
|
+
|
|
116
|
+
**Both `model` AND `dataSource` are required** for schema auto-discovery:
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
// ❌ WRONG - Missing dataSource
|
|
120
|
+
@repository({ model: User })
|
|
121
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
122
|
+
|
|
123
|
+
// ❌ WRONG - Missing model
|
|
124
|
+
@repository({ dataSource: PostgresDataSource })
|
|
125
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
126
|
+
|
|
127
|
+
// ✅ CORRECT
|
|
128
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
129
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Zero Boilerplate Pattern (Recommended)
|
|
133
|
+
|
|
134
|
+
DataSource is auto-injected - no constructor needed:
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
138
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
139
|
+
// Custom methods only - no boilerplate!
|
|
140
|
+
|
|
141
|
+
async findByEmail(opts: { email: string }) {
|
|
142
|
+
return this.findOne({ filter: { where: { email: opts.email } } });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Explicit @inject Pattern
|
|
148
|
+
|
|
149
|
+
When you need constructor control:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
@repository({ model: User, dataSource: PostgresDataSource })
|
|
153
|
+
export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
|
|
154
|
+
constructor(
|
|
155
|
+
@inject({ key: 'datasources.PostgresDataSource' })
|
|
156
|
+
dataSource: PostgresDataSource,
|
|
157
|
+
) {
|
|
158
|
+
super(dataSource);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
## Safety Features
|
|
165
|
+
|
|
166
|
+
### Empty Where Protection
|
|
167
|
+
|
|
168
|
+
Prevents accidental mass updates/deletes:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// ❌ Throws error - empty where without force flag
|
|
172
|
+
await repo.deleteAll({ where: {} });
|
|
173
|
+
|
|
174
|
+
// ✅ Explicitly allow with force flag (logs warning)
|
|
175
|
+
await repo.deleteAll({ where: {}, options: { force: true } });
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Scenario | `force: false` (default) | `force: true` |
|
|
179
|
+
|----------|-------------------------|---------------|
|
|
180
|
+
| Empty `where` | Throws error | Logs warning, proceeds |
|
|
181
|
+
| Valid `where` | Executes normally | Executes normally |
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
## Quick Reference
|
|
185
|
+
|
|
186
|
+
| Want to... | Code |
|
|
187
|
+
|------------|------|
|
|
188
|
+
| Find all active | `repo.find({ filter: { where: { status: 'active' } } })` |
|
|
189
|
+
| Find by ID | `repo.findById({ id: '123' })` |
|
|
190
|
+
| Find with relations | `repo.find({ filter: { include: [{ relation: 'posts' }] } })` |
|
|
191
|
+
| Create one | `repo.create({ data: { name: 'John' } })` |
|
|
192
|
+
| Update by ID | `repo.updateById({ id: '123', data: { name: 'Jane' } })` |
|
|
193
|
+
| Delete by ID | `repo.deleteById({ id: '123' })` |
|
|
194
|
+
| Count matching | `repo.count({ where: { status: 'active' } })` |
|
|
195
|
+
| Check exists | `repo.existsWith({ where: { email: 'test@example.com' } })` |
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
## Next Steps
|
|
199
|
+
|
|
200
|
+
- **New to filtering?** Start with [Filter System](/references/base/filter-system/)
|
|
201
|
+
- **Need related data?** See [Relations & Includes](./relations.md)
|
|
202
|
+
- **Need transactions?** Go to [Advanced Features](./advanced.md)
|
|
203
|
+
|
|
204
|
+
## See Also
|
|
205
|
+
|
|
206
|
+
- **Related Concepts:**
|
|
207
|
+
- [Repositories Guide](/guides/core-concepts/persistent/repositories) - Creating repositories tutorial
|
|
208
|
+
- [Models](/guides/core-concepts/persistent/models) - Entity definitions used by repositories
|
|
209
|
+
- [DataSources](/guides/core-concepts/persistent/datasources) - Database connections
|
|
210
|
+
- [Services](/guides/core-concepts/services) - Use repositories for data access
|
|
211
|
+
- [Transactions](/guides/core-concepts/persistent/transactions) - Multi-operation consistency
|
|
212
|
+
|
|
213
|
+
- **Repository Topics:**
|
|
214
|
+
- [Relations & Includes](./relations) - Loading related data
|
|
215
|
+
- [Advanced Features](./advanced) - JSON queries, performance tuning, transactions
|
|
216
|
+
- [Repository Mixins](./mixins) - Soft delete and auditing
|
|
217
|
+
|
|
218
|
+
- **Filtering:**
|
|
219
|
+
- [Filter System Overview](/references/base/filter-system/) - Complete filtering guide
|
|
220
|
+
- [Filter Quick Reference](/references/base/filter-system/quick-reference) - All operators cheat sheet
|
|
221
|
+
|
|
222
|
+
- **Best Practices:**
|
|
223
|
+
- [Data Modeling](/best-practices/data-modeling) - Repository design patterns
|
|
224
|
+
- [Performance Optimization](/best-practices/performance-optimization) - Query optimization
|
|
225
|
+
|
|
226
|
+
- **Tutorials:**
|
|
227
|
+
- [Building a CRUD API](/guides/tutorials/building-a-crud-api) - Repository examples
|
|
228
|
+
- [E-commerce API](/guides/tutorials/ecommerce-api) - Advanced queries and relations
|
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Repository Mixins
|
|
3
|
+
description: Composable mixins for repository functionality
|
|
4
|
+
difficulty: intermediate
|
|
5
|
+
lastUpdated: 2026-01-02
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Repository Mixins <Badge type="tip" text="v0.0.5+" />
|
|
9
|
+
|
|
10
|
+
Composable mixins that provide reusable functionality for repository classes.
|
|
11
|
+
|
|
12
|
+
::: info Refactored in v0.0.5
|
|
13
|
+
Repository mixins were extracted and refactored in v0.0.5 to provide better composition and reusability.
|
|
14
|
+
:::
|
|
15
|
+
|
|
16
|
+
**Files:** `packages/core/src/base/repositories/mixins/`
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## Overview
|
|
20
|
+
|
|
21
|
+
Ignis uses the mixin pattern to compose repository features. This enables:
|
|
22
|
+
|
|
23
|
+
- **Separation of concerns** - Each mixin handles one responsibility
|
|
24
|
+
- **Reusability** - Mixins can be applied to different base classes
|
|
25
|
+
- **Testability** - Individual features can be tested in isolation
|
|
26
|
+
- **Flexibility** - Create custom repositories with only needed features
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Available Mixins
|
|
30
|
+
|
|
31
|
+
| Mixin | Responsibility |
|
|
32
|
+
|-------|----------------|
|
|
33
|
+
| `DefaultFilterMixin` | Automatic filter application from model settings |
|
|
34
|
+
| `FieldsVisibilityMixin` | Hidden properties exclusion |
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## DefaultFilterMixin
|
|
38
|
+
|
|
39
|
+
Provides automatic default filter application for all repository queries.
|
|
40
|
+
|
|
41
|
+
**File:** `packages/core/src/base/repositories/mixins/default-filter.ts`
|
|
42
|
+
|
|
43
|
+
### Properties
|
|
44
|
+
|
|
45
|
+
| Property | Type | Description |
|
|
46
|
+
|----------|------|-------------|
|
|
47
|
+
| `_defaultFilter` | `TFilter \| null \| undefined` | Cached default filter |
|
|
48
|
+
|
|
49
|
+
### Methods
|
|
50
|
+
|
|
51
|
+
| Method | Returns | Description |
|
|
52
|
+
|--------|---------|-------------|
|
|
53
|
+
| `getDefaultFilter()` | `TFilter \| undefined` | Get default filter from model metadata |
|
|
54
|
+
| `hasDefaultFilter()` | `boolean` | Check if model has a default filter configured |
|
|
55
|
+
| `applyDefaultFilter(opts)` | `TFilter` | Merge default filter with user filter |
|
|
56
|
+
|
|
57
|
+
### Usage
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { DefaultFilterMixin } from '@venizia/ignis';
|
|
61
|
+
import { BaseHelper } from '@venizia/ignis-helpers';
|
|
62
|
+
|
|
63
|
+
class MyRepository extends DefaultFilterMixin(BaseHelper) {
|
|
64
|
+
// Required abstract implementations
|
|
65
|
+
abstract getEntity(): BaseEntity;
|
|
66
|
+
abstract get filterBuilder(): FilterBuilder;
|
|
67
|
+
|
|
68
|
+
// Now has access to:
|
|
69
|
+
// - getDefaultFilter()
|
|
70
|
+
// - hasDefaultFilter()
|
|
71
|
+
// - applyDefaultFilter()
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### applyDefaultFilter Options
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
interface ApplyDefaultFilterOptions {
|
|
79
|
+
userFilter?: TFilter; // User-provided filter
|
|
80
|
+
shouldSkipDefaultFilter?: boolean; // If true, bypass default filter
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Implementation
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
applyDefaultFilter<DataObject = any>(opts: {
|
|
88
|
+
userFilter?: TFilter<DataObject>;
|
|
89
|
+
shouldSkipDefaultFilter?: boolean;
|
|
90
|
+
}): TFilter<DataObject> {
|
|
91
|
+
const { userFilter, shouldSkipDefaultFilter } = opts;
|
|
92
|
+
|
|
93
|
+
// Skip default filter if explicitly requested
|
|
94
|
+
if (shouldSkipDefaultFilter) {
|
|
95
|
+
return userFilter ?? {};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Get default filter from model metadata
|
|
99
|
+
const defaultFilter = this.getDefaultFilter();
|
|
100
|
+
|
|
101
|
+
// No default filter configured - return user filter
|
|
102
|
+
if (!defaultFilter) {
|
|
103
|
+
return userFilter ?? {};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Merge default filter with user filter
|
|
107
|
+
return this.filterBuilder.mergeFilter({ defaultFilter, userFilter });
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## FieldsVisibilityMixin
|
|
113
|
+
|
|
114
|
+
Provides hidden properties management for SQL-level field exclusion.
|
|
115
|
+
|
|
116
|
+
**File:** `packages/core/src/base/repositories/mixins/fields-visibility.ts`
|
|
117
|
+
|
|
118
|
+
### Properties
|
|
119
|
+
|
|
120
|
+
| Property | Type | Description |
|
|
121
|
+
|----------|------|-------------|
|
|
122
|
+
| `_hiddenProperties` | `Set<string> \| null` | Cached hidden property names |
|
|
123
|
+
| `_visibleProperties` | `Record<string, any> \| null \| undefined` | Cached visible columns |
|
|
124
|
+
|
|
125
|
+
### Methods
|
|
126
|
+
|
|
127
|
+
| Method | Returns | Description |
|
|
128
|
+
|--------|---------|-------------|
|
|
129
|
+
| `get hiddenProperties` | `Set<string>` | Get hidden property names |
|
|
130
|
+
| `set hiddenProperties` | `void` | Override hidden properties |
|
|
131
|
+
| `getHiddenProperties()` | `Set<string>` | Get hidden properties from model metadata |
|
|
132
|
+
| `hasHiddenProperties()` | `boolean` | Check if model has hidden properties |
|
|
133
|
+
| `get visibleProperties` | `Record<string, any> \| undefined` | Get visible columns for Drizzle |
|
|
134
|
+
| `set visibleProperties` | `void` | Override visible properties |
|
|
135
|
+
| `getVisibleProperties()` | `Record<string, any> \| undefined` | Build visible columns object |
|
|
136
|
+
|
|
137
|
+
### Usage
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { FieldsVisibilityMixin } from '@venizia/ignis';
|
|
141
|
+
import { BaseHelper } from '@venizia/ignis-helpers';
|
|
142
|
+
|
|
143
|
+
class MyRepository extends FieldsVisibilityMixin(BaseHelper) {
|
|
144
|
+
// Required abstract implementation
|
|
145
|
+
abstract getEntity(): BaseEntity;
|
|
146
|
+
|
|
147
|
+
// Now has access to:
|
|
148
|
+
// - hiddenProperties (getter/setter)
|
|
149
|
+
// - visibleProperties (getter/setter)
|
|
150
|
+
// - getHiddenProperties()
|
|
151
|
+
// - hasHiddenProperties()
|
|
152
|
+
// - getVisibleProperties()
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Visible Properties for Drizzle
|
|
157
|
+
|
|
158
|
+
The `getVisibleProperties()` method returns a columns object for Drizzle's `select()` or `returning()`:
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Model with hiddenProperties: ['password', 'apiKey']
|
|
162
|
+
// Schema columns: { id, email, password, apiKey, createdAt }
|
|
163
|
+
|
|
164
|
+
const visibleProps = this.getVisibleProperties();
|
|
165
|
+
// Result: { id: column, email: column, createdAt: column }
|
|
166
|
+
// (password and apiKey excluded)
|
|
167
|
+
|
|
168
|
+
// Used in Drizzle queries
|
|
169
|
+
await connector.select(visibleProps).from(schema);
|
|
170
|
+
// SELECT id, email, created_at FROM users
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
## Mixin Composition
|
|
175
|
+
|
|
176
|
+
The `AbstractRepository` composes multiple mixins:
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
export abstract class AbstractRepository<...>
|
|
180
|
+
extends DefaultFilterMixin(FieldsVisibilityMixin(BaseHelper))
|
|
181
|
+
implements IPersistableRepository<...>
|
|
182
|
+
{
|
|
183
|
+
// Inherits from both mixins:
|
|
184
|
+
// From DefaultFilterMixin:
|
|
185
|
+
// - getDefaultFilter()
|
|
186
|
+
// - hasDefaultFilter()
|
|
187
|
+
// - applyDefaultFilter()
|
|
188
|
+
//
|
|
189
|
+
// From FieldsVisibilityMixin:
|
|
190
|
+
// - hiddenProperties
|
|
191
|
+
// - visibleProperties
|
|
192
|
+
// - getHiddenProperties()
|
|
193
|
+
// - hasHiddenProperties()
|
|
194
|
+
// - getVisibleProperties()
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Composition Order
|
|
199
|
+
|
|
200
|
+
Mixins are applied right-to-left:
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
// FieldsVisibilityMixin applied first (to BaseHelper)
|
|
204
|
+
// DefaultFilterMixin applied second (to the result)
|
|
205
|
+
DefaultFilterMixin(FieldsVisibilityMixin(BaseHelper))
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
## Creating Custom Mixins
|
|
210
|
+
|
|
211
|
+
Follow the TypeScript mixin pattern:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { TMixinTarget } from '@venizia/ignis-helpers';
|
|
215
|
+
|
|
216
|
+
export const AuditLogMixin = <T extends TMixinTarget<object>>(baseClass: T) => {
|
|
217
|
+
abstract class Mixed extends baseClass {
|
|
218
|
+
// Properties
|
|
219
|
+
private _auditEnabled: boolean = true;
|
|
220
|
+
|
|
221
|
+
// Abstract dependencies (if needed)
|
|
222
|
+
abstract getEntity(): BaseEntity;
|
|
223
|
+
|
|
224
|
+
// Public methods
|
|
225
|
+
enableAudit(): void {
|
|
226
|
+
this._auditEnabled = true;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
disableAudit(): void {
|
|
230
|
+
this._auditEnabled = false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
isAuditEnabled(): boolean {
|
|
234
|
+
return this._auditEnabled;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
logOperation(operation: string, data: any): void {
|
|
238
|
+
if (this._auditEnabled) {
|
|
239
|
+
console.log(`[${this.getEntity().name}] ${operation}:`, data);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return Mixed;
|
|
245
|
+
};
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Using Custom Mixins
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// Compose with existing mixins
|
|
252
|
+
class MyRepository extends AuditLogMixin(DefaultFilterMixin(BaseHelper)) {
|
|
253
|
+
getEntity() {
|
|
254
|
+
return this._entity;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
get filterBuilder() {
|
|
258
|
+
return this._filterBuilder;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Or create a composed base
|
|
263
|
+
const AuditableRepository = AuditLogMixin(DefaultFilterMixin(FieldsVisibilityMixin(BaseHelper)));
|
|
264
|
+
|
|
265
|
+
class ProductRepository extends AuditableRepository {
|
|
266
|
+
// Has all mixin functionality
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
## Caching Behavior
|
|
272
|
+
|
|
273
|
+
Both mixins use caching for performance:
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
// DefaultFilterMixin caching
|
|
277
|
+
// null = not computed yet
|
|
278
|
+
// undefined = computed, no default filter
|
|
279
|
+
// TFilter = computed, has default filter
|
|
280
|
+
private _defaultFilter: TFilter | null | undefined = null;
|
|
281
|
+
|
|
282
|
+
getDefaultFilter() {
|
|
283
|
+
if (this._defaultFilter !== null) {
|
|
284
|
+
return this._defaultFilter; // Return cached value
|
|
285
|
+
}
|
|
286
|
+
// Compute and cache...
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// FieldsVisibilityMixin caching
|
|
290
|
+
// null = not computed yet
|
|
291
|
+
// Set<string> = computed
|
|
292
|
+
private _hiddenProperties: Set<string> | null = null;
|
|
293
|
+
|
|
294
|
+
// null = not computed yet
|
|
295
|
+
// undefined = computed, no hidden properties
|
|
296
|
+
// Record<string, any> = computed, has hidden properties
|
|
297
|
+
private _visibleProperties: Record<string, any> | null | undefined = null;
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
## Quick Reference
|
|
302
|
+
|
|
303
|
+
| Mixin | Method | Purpose |
|
|
304
|
+
|-------|--------|---------|
|
|
305
|
+
| `DefaultFilterMixin` | `hasDefaultFilter()` | Check if default filter exists |
|
|
306
|
+
| `DefaultFilterMixin` | `getDefaultFilter()` | Get raw default filter |
|
|
307
|
+
| `DefaultFilterMixin` | `applyDefaultFilter()` | Merge filters |
|
|
308
|
+
| `FieldsVisibilityMixin` | `hasHiddenProperties()` | Check if hidden props exist |
|
|
309
|
+
| `FieldsVisibilityMixin` | `getHiddenProperties()` | Get hidden property names |
|
|
310
|
+
| `FieldsVisibilityMixin` | `getVisibleProperties()` | Get Drizzle columns object |
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
## Next Steps
|
|
314
|
+
|
|
315
|
+
- [Default Filter](../filter-system/default-filter.md) - Full default filter documentation
|
|
316
|
+
- [Advanced Features](./advanced.md) - Hidden properties usage
|
|
317
|
+
- [Repository Overview](./index.md) - Repository basics
|
|
318
|
+
|
|
319
|
+
## See Also
|
|
320
|
+
|
|
321
|
+
- **Related Concepts:**
|
|
322
|
+
- [Repositories Overview](./index) - Core repository operations
|
|
323
|
+
- [Models](/guides/core-concepts/persistent/models) - Entity definitions
|
|
324
|
+
|
|
325
|
+
- **Related Topics:**
|
|
326
|
+
- [Default Filter](../filter-system/default-filter) - Automatic filtering
|
|
327
|
+
- [Advanced Features](./advanced) - Hidden properties and transactions
|
|
328
|
+
- [Relations & Includes](./relations) - Loading related data
|
|
329
|
+
|
|
330
|
+
- **Best Practices:**
|
|
331
|
+
- [Data Modeling](/best-practices/data-modeling) - Soft delete patterns
|