@venizia/ignis-docs 0.0.4-0 → 0.0.4-2

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.
Files changed (44) hide show
  1. package/package.json +1 -1
  2. package/wiki/best-practices/api-usage-examples.md +1 -0
  3. package/wiki/best-practices/code-style-standards/advanced-patterns.md +259 -0
  4. package/wiki/best-practices/code-style-standards/constants-configuration.md +225 -0
  5. package/wiki/best-practices/code-style-standards/control-flow.md +245 -0
  6. package/wiki/best-practices/code-style-standards/documentation.md +221 -0
  7. package/wiki/best-practices/code-style-standards/function-patterns.md +142 -0
  8. package/wiki/best-practices/code-style-standards/index.md +110 -0
  9. package/wiki/best-practices/code-style-standards/naming-conventions.md +174 -0
  10. package/wiki/best-practices/code-style-standards/route-definitions.md +150 -0
  11. package/wiki/best-practices/code-style-standards/tooling.md +155 -0
  12. package/wiki/best-practices/code-style-standards/type-safety.md +165 -0
  13. package/wiki/best-practices/common-pitfalls.md +164 -3
  14. package/wiki/best-practices/contribution-workflow.md +1 -1
  15. package/wiki/best-practices/data-modeling.md +102 -2
  16. package/wiki/best-practices/error-handling.md +468 -0
  17. package/wiki/best-practices/index.md +204 -21
  18. package/wiki/best-practices/performance-optimization.md +180 -0
  19. package/wiki/best-practices/security-guidelines.md +249 -0
  20. package/wiki/best-practices/testing-strategies.md +620 -0
  21. package/wiki/changelogs/2026-01-05-range-queries-content-range.md +184 -0
  22. package/wiki/changelogs/2026-01-06-basic-authentication.md +103 -0
  23. package/wiki/changelogs/2026-01-07-controller-route-customization.md +209 -0
  24. package/wiki/changelogs/index.md +3 -0
  25. package/wiki/guides/core-concepts/components-guide.md +1 -1
  26. package/wiki/guides/core-concepts/persistent/models.md +10 -0
  27. package/wiki/guides/tutorials/complete-installation.md +1 -1
  28. package/wiki/guides/tutorials/testing.md +1 -1
  29. package/wiki/references/base/bootstrapping.md +4 -3
  30. package/wiki/references/base/components.md +47 -29
  31. package/wiki/references/base/controllers.md +220 -24
  32. package/wiki/references/base/filter-system/fields-order-pagination.md +84 -0
  33. package/wiki/references/base/middlewares.md +37 -3
  34. package/wiki/references/base/models.md +40 -2
  35. package/wiki/references/base/providers.md +1 -2
  36. package/wiki/references/base/repositories/index.md +3 -1
  37. package/wiki/references/base/services.md +2 -2
  38. package/wiki/references/components/authentication.md +261 -247
  39. package/wiki/references/helpers/index.md +1 -1
  40. package/wiki/references/helpers/socket-io.md +1 -1
  41. package/wiki/references/quick-reference.md +2 -2
  42. package/wiki/references/src-details/core.md +1 -1
  43. package/wiki/references/utilities/statuses.md +4 -4
  44. package/wiki/best-practices/code-style-standards.md +0 -1193
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@venizia/ignis-docs",
3
- "version": "0.0.4-0",
3
+ "version": "0.0.4-2",
4
4
  "description": "Documentation and MCP Server for Ignis Framework",
5
5
  "keywords": [
6
6
  "ignis",
@@ -223,6 +223,7 @@ const deleted = await configurationRepository.deleteById({
223
223
  id: newRecord.data!.id,
224
224
  options: { shouldReturn: true }, // Option to return the deleted record
225
225
  });
226
+ ```
226
227
 
227
228
  ## Server-Side Rendering (JSX)
228
229
 
@@ -0,0 +1,259 @@
1
+ # Advanced Patterns
2
+
3
+ Advanced TypeScript patterns used throughout the Ignis framework.
4
+
5
+ ## Mixin Pattern
6
+
7
+ Create reusable class extensions without deep inheritance:
8
+
9
+ ```typescript
10
+ import { TMixinTarget } from '@venizia/ignis';
11
+
12
+ export const LoggableMixin = <BaseClass extends TMixinTarget<Base>>(
13
+ baseClass: BaseClass,
14
+ ) => {
15
+ return class extends baseClass {
16
+ protected logger = LoggerFactory.getLogger(this.constructor.name);
17
+
18
+ log(message: string): void {
19
+ this.logger.info(message);
20
+ }
21
+ };
22
+ };
23
+
24
+ // Usage
25
+ class MyService extends LoggableMixin(BaseService) {
26
+ doWork(): void {
27
+ this.log('Work started'); // Method from mixin
28
+ }
29
+ }
30
+ ```
31
+
32
+ ### Multiple Mixins
33
+
34
+ ```typescript
35
+ class MyRepository extends LoggableMixin(CacheableMixin(BaseRepository)) {
36
+ // Has both logging and caching capabilities
37
+ }
38
+ ```
39
+
40
+ ### Typed Mixin with Constraints
41
+
42
+ ```typescript
43
+ type TWithId = { id: string };
44
+
45
+ export const TimestampMixin = <
46
+ BaseClass extends TMixinTarget<TWithId>
47
+ >(baseClass: BaseClass) => {
48
+ return class extends baseClass {
49
+ createdAt: Date = new Date();
50
+ updatedAt: Date = new Date();
51
+
52
+ touch(): void {
53
+ this.updatedAt = new Date();
54
+ }
55
+ };
56
+ };
57
+ ```
58
+
59
+ ## Factory Pattern with Dynamic Class
60
+
61
+ Generate classes dynamically with configuration:
62
+
63
+ ```typescript
64
+ class ControllerFactory {
65
+ static defineCrudController<Schema extends TTableSchemaWithId>(
66
+ opts: ICrudControllerOptions<Schema>,
67
+ ) {
68
+ return class extends BaseController {
69
+ constructor(repository: AbstractRepository<Schema>) {
70
+ super({ scope: opts.controller.name });
71
+ this.repository = repository;
72
+ this.setupRoutes();
73
+ }
74
+
75
+ private setupRoutes(): void {
76
+ // Dynamically bind CRUD routes
77
+ this.defineRoute({
78
+ configs: { method: 'get', path: '/' },
79
+ handler: (c) => this.list(c),
80
+ });
81
+ this.defineRoute({
82
+ configs: { method: 'get', path: '/:id' },
83
+ handler: (c) => this.getById(c),
84
+ });
85
+ // ... more routes
86
+ }
87
+
88
+ async list(c: Context) {
89
+ const data = await this.repository.find({});
90
+ return c.json(data);
91
+ }
92
+
93
+ async getById(c: Context) {
94
+ const { id } = c.req.param();
95
+ const data = await this.repository.findById({ id });
96
+ return c.json(data);
97
+ }
98
+ };
99
+ }
100
+ }
101
+
102
+ // Usage
103
+ const UserCrudController = ControllerFactory.defineCrudController({
104
+ controller: { name: 'UserController', basePath: '/users' },
105
+ repository: { name: UserRepository.name },
106
+ entity: () => User,
107
+ });
108
+
109
+ @controller({ path: '/users' })
110
+ export class UserController extends UserCrudController {
111
+ // Additional custom routes
112
+ }
113
+ ```
114
+
115
+ ## Value Resolver Pattern
116
+
117
+ Support multiple input types that resolve to a single value:
118
+
119
+ ```typescript
120
+ // Type definitions
121
+ export type TResolver<T> = () => T;
122
+ export type TConstructor<T> = new (...args: any[]) => T;
123
+ export type TValueOrResolver<T> = T | TResolver<T> | TConstructor<T>;
124
+
125
+ // Resolver function
126
+ export const resolveValue = <T>(valueOrResolver: TValueOrResolver<T>): T => {
127
+ if (typeof valueOrResolver !== 'function') {
128
+ return valueOrResolver; // Direct value
129
+ }
130
+ if (isClassConstructor(valueOrResolver)) {
131
+ return valueOrResolver as T; // Class constructor (return as-is)
132
+ }
133
+ return (valueOrResolver as TResolver<T>)(); // Function resolver
134
+ };
135
+
136
+ // Helper to detect class constructors
137
+ function isClassConstructor(fn: Function): boolean {
138
+ return fn.toString().startsWith('class ');
139
+ }
140
+ ```
141
+
142
+ ### Usage
143
+
144
+ ```typescript
145
+ interface IOptions {
146
+ entity: TValueOrResolver<typeof User>;
147
+ }
148
+
149
+ // All valid:
150
+ const opts1: IOptions = { entity: User }; // Direct class
151
+ const opts2: IOptions = { entity: () => User }; // Resolver function (for lazy loading)
152
+
153
+ // In consumer code
154
+ const EntityClass = resolveValue(opts.entity);
155
+ const instance = new EntityClass();
156
+ ```
157
+
158
+ ### Why Use Value Resolvers?
159
+
160
+ 1. **Circular Dependency Prevention**: Lazy loading via resolver functions breaks cycles
161
+ 2. **Lazy Initialization**: Defer expensive imports until needed
162
+ 3. **Testing**: Easy to swap implementations via resolvers
163
+ 4. **Flexibility**: Single API accepts multiple input types
164
+
165
+ ## Builder Pattern
166
+
167
+ For constructing complex objects step-by-step:
168
+
169
+ ```typescript
170
+ class QueryBuilder<T> {
171
+ private _where: Record<string, any> = {};
172
+ private _orderBy: string[] = [];
173
+ private _limit?: number;
174
+ private _offset?: number;
175
+
176
+ where(conditions: Record<string, any>): this {
177
+ this._where = { ...this._where, ...conditions };
178
+ return this;
179
+ }
180
+
181
+ orderBy(field: string, direction: 'asc' | 'desc' = 'asc'): this {
182
+ this._orderBy.push(`${field} ${direction.toUpperCase()}`);
183
+ return this;
184
+ }
185
+
186
+ limit(n: number): this {
187
+ this._limit = n;
188
+ return this;
189
+ }
190
+
191
+ offset(n: number): this {
192
+ this._offset = n;
193
+ return this;
194
+ }
195
+
196
+ build(): TQueryOptions {
197
+ return {
198
+ where: this._where,
199
+ order: this._orderBy,
200
+ limit: this._limit,
201
+ offset: this._offset,
202
+ };
203
+ }
204
+ }
205
+
206
+ // Usage
207
+ const query = new QueryBuilder()
208
+ .where({ status: 'active' })
209
+ .orderBy('createdAt', 'desc')
210
+ .limit(10)
211
+ .offset(0)
212
+ .build();
213
+ ```
214
+
215
+ ## Registry Pattern
216
+
217
+ Centralized registration of components:
218
+
219
+ ```typescript
220
+ class StrategyRegistry<T> {
221
+ private strategies = new Map<string, T>();
222
+
223
+ register(name: string, strategy: T): void {
224
+ if (this.strategies.has(name)) {
225
+ throw new Error(`Strategy '${name}' already registered`);
226
+ }
227
+ this.strategies.set(name, strategy);
228
+ }
229
+
230
+ get(name: string): T {
231
+ const strategy = this.strategies.get(name);
232
+ if (!strategy) {
233
+ throw new Error(`Strategy '${name}' not found`);
234
+ }
235
+ return strategy;
236
+ }
237
+
238
+ has(name: string): boolean {
239
+ return this.strategies.has(name);
240
+ }
241
+
242
+ all(): Map<string, T> {
243
+ return new Map(this.strategies);
244
+ }
245
+ }
246
+
247
+ // Usage
248
+ const authRegistry = new StrategyRegistry<IAuthStrategy>();
249
+ authRegistry.register('jwt', new JWTStrategy());
250
+ authRegistry.register('basic', new BasicStrategy());
251
+
252
+ const strategy = authRegistry.get('jwt');
253
+ ```
254
+
255
+ ## See Also
256
+
257
+ - [Type Safety](./type-safety) - Generic type patterns
258
+ - [Repositories Reference](../../references/base/repositories/) - Mixin usage
259
+ - [Architectural Patterns](../architectural-patterns) - High-level patterns
@@ -0,0 +1,225 @@
1
+ # Constants & Configuration
2
+
3
+ Best practices for defining constants and managing configuration.
4
+
5
+ ## Constants Pattern
6
+
7
+ **Prefer static classes over enums** for better tree-shaking and extensibility.
8
+
9
+ ### Basic Constants
10
+
11
+ ```typescript
12
+ export class Authentication {
13
+ static readonly STRATEGY_BASIC = 'basic';
14
+ static readonly STRATEGY_JWT = 'jwt';
15
+ static readonly TYPE_BEARER = 'Bearer';
16
+ }
17
+
18
+ export class HealthCheckRestPaths {
19
+ static readonly ROOT = '/';
20
+ static readonly PING = '/ping';
21
+ static readonly METRICS = '/metrics';
22
+ }
23
+ ```
24
+
25
+ ### Typed Constants with Validation
26
+
27
+ For constants that need type extraction and runtime validation, use this pattern:
28
+
29
+ ```typescript
30
+ import { TConstValue } from '@venizia/ignis-helpers';
31
+
32
+ export class DocumentUITypes {
33
+ // 1. Define static readonly values
34
+ static readonly SWAGGER = 'swagger';
35
+ static readonly SCALAR = 'scalar';
36
+
37
+ // 2. Create a Set for O(1) validation lookup
38
+ static readonly SCHEME_SET = new Set([this.SWAGGER, this.SCALAR]);
39
+
40
+ // 3. Validation helper method
41
+ static isValid(value: string): boolean {
42
+ return this.SCHEME_SET.has(value);
43
+ }
44
+ }
45
+
46
+ // 4. Extract union type from class values
47
+ export type TDocumentUIType = TConstValue<typeof DocumentUITypes>;
48
+ // Result: 'swagger' | 'scalar'
49
+ ```
50
+
51
+ ### Full Example with Usage
52
+
53
+ ```typescript
54
+ import { TConstValue } from '@venizia/ignis-helpers';
55
+
56
+ export class UserStatuses {
57
+ static readonly ACTIVE = 'active';
58
+ static readonly INACTIVE = 'inactive';
59
+ static readonly PENDING = 'pending';
60
+ static readonly BANNED = 'banned';
61
+
62
+ static readonly SCHEME_SET = new Set([
63
+ this.ACTIVE,
64
+ this.INACTIVE,
65
+ this.PENDING,
66
+ this.BANNED,
67
+ ]);
68
+
69
+ static isValid(value: string): boolean {
70
+ return this.SCHEME_SET.has(value);
71
+ }
72
+
73
+ // Optional: get all values as array
74
+ static values(): string[] {
75
+ return [...this.SCHEME_SET];
76
+ }
77
+ }
78
+
79
+ // Type-safe union type
80
+ export type TUserStatus = TConstValue<typeof UserStatuses>;
81
+ // Result: 'active' | 'inactive' | 'pending' | 'banned'
82
+
83
+ // Usage in interfaces
84
+ interface IUser {
85
+ id: string;
86
+ status: TUserStatus; // Type-safe!
87
+ }
88
+
89
+ // Usage with validation
90
+ function updateUserStatus(userId: string, status: string) {
91
+ if (!UserStatuses.isValid(status)) {
92
+ throw getError({
93
+ statusCode: HTTP.ResultCodes.RS_4.BadRequest,
94
+ message: `Invalid status: ${status}. Valid: ${UserStatuses.values().join(', ')}`,
95
+ });
96
+ }
97
+ // status is validated at runtime
98
+ }
99
+ ```
100
+
101
+ ## Enum vs Static Class Comparison
102
+
103
+ | Aspect | Static Class | TypeScript Enum |
104
+ |--------|--------------|-----------------|
105
+ | Tree-shaking | Full support | Partial (IIFE blocks it) |
106
+ | Bundle size | Minimal | Larger (IIFE wrapper) |
107
+ | Runtime validation | O(1) with `Set` | O(n) with `Object.values()` |
108
+ | Type extraction | `TConstValue<typeof X>` → values | `keyof typeof X` → keys (not values!) |
109
+ | Add methods | Yes | Not possible |
110
+ | Compiled output | Clean class | IIFE wrapper |
111
+
112
+ **Compiled JavaScript:**
113
+
114
+ ```typescript
115
+ // Enum compiles to IIFE (not tree-shakable)
116
+ var UserStatus;
117
+ (function (UserStatus) {
118
+ UserStatus["ACTIVE"] = "active";
119
+ })(UserStatus || (UserStatus = {}));
120
+
121
+ // Static class compiles cleanly
122
+ class UserStatuses { }
123
+ UserStatuses.ACTIVE = 'active';
124
+ ```
125
+
126
+ **Type Extraction Difference:**
127
+
128
+ ```typescript
129
+ // Enum - extracts KEYS
130
+ type T = keyof typeof UserStatus; // 'ACTIVE' | 'INACTIVE'
131
+
132
+ // Static Class - extracts VALUES
133
+ type T = TConstValue<typeof UserStatuses>; // 'active' | 'inactive'
134
+ ```
135
+
136
+ **When to use `const enum`:** Only for numeric flags with no iteration needed (values are inlined, zero runtime). But doesn't work with `--isolatedModules`.
137
+
138
+ **Verdict:** Use Static Class for 90% of cases - better tree-shaking, easy validation, type-safe values, extensible with methods.
139
+
140
+ ## Configuration Patterns
141
+
142
+ ### Default Options
143
+
144
+ Every configurable class should define `DEFAULT_OPTIONS`:
145
+
146
+ ```typescript
147
+ const DEFAULT_OPTIONS: IHealthCheckOptions = {
148
+ restOptions: { path: '/health' },
149
+ };
150
+
151
+ const DEFAULT_SERVER_OPTIONS: Partial<IServerOptions> = {
152
+ identifier: 'SOCKET_IO_SERVER',
153
+ path: '/io',
154
+ cors: {
155
+ origin: '*',
156
+ methods: ['GET', 'POST'],
157
+ },
158
+ };
159
+ ```
160
+
161
+ ### Option Merging
162
+
163
+ ```typescript
164
+ // In component constructor or binding
165
+ const extraOptions = this.application.get<Partial<IServerOptions>>({
166
+ key: BindingKeys.SERVER_OPTIONS,
167
+ isOptional: true,
168
+ }) ?? {};
169
+
170
+ this.options = Object.assign({}, DEFAULT_OPTIONS, extraOptions);
171
+ ```
172
+
173
+ ### Constructor Validation
174
+
175
+ Validate required options in the constructor:
176
+
177
+ ```typescript
178
+ constructor(options: IJWTTokenServiceOptions) {
179
+ super({ scope: JWTTokenService.name });
180
+
181
+ if (!options.jwtSecret) {
182
+ throw getError({
183
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
184
+ message: '[JWTTokenService] Invalid jwtSecret',
185
+ });
186
+ }
187
+
188
+ if (!options.applicationSecret) {
189
+ throw getError({
190
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
191
+ message: '[JWTTokenService] Invalid applicationSecret',
192
+ });
193
+ }
194
+
195
+ this.options = options;
196
+ }
197
+ ```
198
+
199
+ ## Environment Variables Management
200
+
201
+ Avoid using `process.env` directly in your business logic. Instead, use the `applicationEnvironment` helper and define your keys as constants.
202
+
203
+ **Define Keys (`src/common/environments.ts`):**
204
+ ```typescript
205
+ export class EnvironmentKeys {
206
+ static readonly APP_ENV_STRIPE_KEY = 'APP_ENV_STRIPE_KEY';
207
+ static readonly APP_ENV_MAX_RETRIES = 'APP_ENV_MAX_RETRIES';
208
+ }
209
+ ```
210
+
211
+ **Usage:**
212
+ ```typescript
213
+ import { applicationEnvironment } from '@venizia/ignis';
214
+ import { EnvironmentKeys } from '@/common/environments';
215
+
216
+ // Correct usage
217
+ const stripeKey = applicationEnvironment.get<string>(EnvironmentKeys.APP_ENV_STRIPE_KEY);
218
+ const retries = applicationEnvironment.get<number>(EnvironmentKeys.APP_ENV_MAX_RETRIES);
219
+ ```
220
+
221
+ ## See Also
222
+
223
+ - [Naming Conventions](./naming-conventions) - Constant naming
224
+ - [Type Safety](./type-safety) - Type extraction patterns
225
+ - [Configuration Reference](../../references/configuration/) - Environment variables