@venizia/ignis-docs 0.0.4-1 → 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 (38) 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/components.md +47 -29
  30. package/wiki/references/base/controllers.md +215 -18
  31. package/wiki/references/base/filter-system/fields-order-pagination.md +84 -0
  32. package/wiki/references/base/middlewares.md +33 -1
  33. package/wiki/references/base/models.md +40 -2
  34. package/wiki/references/base/repositories/index.md +2 -0
  35. package/wiki/references/components/authentication.md +261 -247
  36. package/wiki/references/helpers/index.md +1 -1
  37. package/wiki/references/src-details/core.md +1 -1
  38. package/wiki/best-practices/code-style-standards.md +0 -1193
@@ -0,0 +1,245 @@
1
+ # Control Flow & Organization
2
+
3
+ Guidelines for control flow, logging, error handling, and code organization.
4
+
5
+ ## Control Flow Patterns
6
+
7
+ ### Mandatory Braces
8
+
9
+ **Always use braces for `if`, `for`, `while`, and `do-while` statements**, even for single-line bodies. Never use inline statements.
10
+
11
+ ```typescript
12
+ // ✅ GOOD - Always use braces
13
+ if (condition) {
14
+ doSomething();
15
+ }
16
+
17
+ for (const item of items) {
18
+ process(item);
19
+ }
20
+
21
+ while (running) {
22
+ tick();
23
+ }
24
+
25
+ do {
26
+ attempt();
27
+ } while (retrying);
28
+
29
+ // ❌ BAD - Never inline without braces
30
+ if (condition) doSomething();
31
+ for (const item of items) process(item);
32
+ while (running) tick();
33
+ ```
34
+
35
+ **Why braces are mandatory:**
36
+ - Prevents bugs when adding statements later
37
+ - Clearer code structure at a glance
38
+ - Consistent formatting across codebase
39
+
40
+ ### Switch Statement Requirements
41
+
42
+ **All switch statements must:**
43
+ 1. Use braces `{}` for each case block
44
+ 2. Include a `default` case (even if it throws)
45
+
46
+ ```typescript
47
+ // ✅ GOOD - Braces and default case
48
+ switch (status) {
49
+ case 'active': {
50
+ activateUser();
51
+ break;
52
+ }
53
+ case 'inactive': {
54
+ deactivateUser();
55
+ break;
56
+ }
57
+ case 'pending': {
58
+ notifyAdmin();
59
+ break;
60
+ }
61
+ default: {
62
+ throw getError({
63
+ statusCode: HTTP.ResultCodes.RS_4.BadRequest,
64
+ message: `Unknown status: ${status}`,
65
+ });
66
+ }
67
+ }
68
+
69
+ // ❌ BAD - Missing braces and default case
70
+ switch (status) {
71
+ case 'active':
72
+ activateUser();
73
+ break;
74
+ case 'inactive':
75
+ deactivateUser();
76
+ break;
77
+ // Missing default case!
78
+ }
79
+ ```
80
+
81
+ **Why these rules:**
82
+ - Braces prevent variable scoping issues between cases
83
+ - Default case ensures all values are handled
84
+ - Throwing in default catches unexpected values early
85
+
86
+ ## Logging Patterns
87
+
88
+ ### Method Context Prefix
89
+
90
+ Always include class and method context in log messages:
91
+
92
+ ```typescript
93
+ // Format: [ClassName][methodName] Message with %s placeholders
94
+ this.logger.info('[binding] Asset storage bound | Key: %s | Type: %s', key, storageType);
95
+ this.logger.debug('[authenticate] Token validated | User: %s', userId);
96
+ this.logger.warn('[register] Skipping duplicate registration | Type: %s', opts.type);
97
+ this.logger.error('[generate] Token generation failed | Error: %s', error.message);
98
+ ```
99
+
100
+ ### Structured Data
101
+
102
+ Use format specifiers for structured logging:
103
+
104
+ ```typescript
105
+ // %s - string, %d - number, %j - JSON object
106
+ this.logger.info('[create] User created | ID: %s | Email: %s', user.id, user.email);
107
+ this.logger.debug('[config] Server options: %j', this.serverOptions);
108
+ ```
109
+
110
+ ### Log Levels
111
+
112
+ | Level | Use For |
113
+ |-------|---------|
114
+ | `error` | Exceptions that need attention |
115
+ | `warn` | Recoverable issues, deprecations |
116
+ | `info` | Important business events |
117
+ | `debug` | Detailed debugging information |
118
+
119
+ ## Standardized Error Handling
120
+
121
+ Use the `getError` helper and `HTTP` constants to throw consistent, formatted exceptions.
122
+
123
+ ### Basic Error
124
+
125
+ ```typescript
126
+ import { getError, HTTP } from '@venizia/ignis';
127
+
128
+ if (!record) {
129
+ throw getError({
130
+ statusCode: HTTP.ResultCodes.RS_4.NotFound,
131
+ message: 'Record not found',
132
+ details: { id: requestedId },
133
+ });
134
+ }
135
+ ```
136
+
137
+ ### Error with Context
138
+
139
+ Include class/method context in error messages:
140
+
141
+ ```typescript
142
+ // Format: [ClassName][methodName] Descriptive message
143
+ throw getError({
144
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
145
+ message: '[JWTTokenService][generate] Failed to generate token',
146
+ });
147
+
148
+ throw getError({
149
+ statusCode: HTTP.ResultCodes.RS_4.Unauthorized,
150
+ message: '[AuthMiddleware][authenticate] Missing authorization header',
151
+ });
152
+ ```
153
+
154
+ ### Validation Errors
155
+
156
+ ```typescript
157
+ constructor(options: IServiceOptions) {
158
+ if (!options.apiKey) {
159
+ throw getError({
160
+ statusCode: HTTP.ResultCodes.RS_5.InternalServerError,
161
+ message: '[PaymentService] Missing required apiKey configuration',
162
+ });
163
+ }
164
+ }
165
+ ```
166
+
167
+ ### HTTP Status Code Quick Reference
168
+
169
+ | Category | Constant | Use Case |
170
+ |----------|----------|----------|
171
+ | Success | `HTTP.ResultCodes.RS_2.Ok` | Successful response |
172
+ | Created | `HTTP.ResultCodes.RS_2.Created` | Resource created |
173
+ | Bad Request | `HTTP.ResultCodes.RS_4.BadRequest` | Invalid input |
174
+ | Unauthorized | `HTTP.ResultCodes.RS_4.Unauthorized` | Missing/invalid auth |
175
+ | Forbidden | `HTTP.ResultCodes.RS_4.Forbidden` | Insufficient permissions |
176
+ | Not Found | `HTTP.ResultCodes.RS_4.NotFound` | Resource not found |
177
+ | Internal Error | `HTTP.ResultCodes.RS_5.InternalServerError` | Server errors |
178
+
179
+ ## Code Organization
180
+
181
+ ### Section Separator Comments
182
+
183
+ Use visual separators for major code sections in long files:
184
+
185
+ ```typescript
186
+ // ---------------------------------------------------------------------------
187
+ // Type Definitions
188
+ // ---------------------------------------------------------------------------
189
+
190
+ type TMyType = { /* ... */ };
191
+
192
+ // ---------------------------------------------------------------------------
193
+ // Constants
194
+ // ---------------------------------------------------------------------------
195
+
196
+ const DEFAULT_OPTIONS = { /* ... */ };
197
+
198
+ // ---------------------------------------------------------------------------
199
+ // Main Implementation
200
+ // ---------------------------------------------------------------------------
201
+
202
+ export class MyClass {
203
+ // ...
204
+ }
205
+ ```
206
+
207
+ **Guidelines:**
208
+ - Use for files > 200 lines with distinct sections
209
+ - Use 75-character wide separator lines
210
+ - Descriptive section names (2-4 words)
211
+
212
+ ### Import Organization Order
213
+
214
+ Organize imports in this order:
215
+
216
+ ```typescript
217
+ // 1. Node built-ins (with 'node:' prefix)
218
+ import fs from 'node:fs';
219
+ import path from 'node:path';
220
+
221
+ // 2. Third-party packages (alphabetical)
222
+ import { z } from '@hono/zod-openapi';
223
+ import dayjs from 'dayjs';
224
+
225
+ // 3. Internal absolute imports (by domain/package)
226
+ import { getError } from '@venizia/ignis-helpers';
227
+ import { BaseEntity } from '@/base/models';
228
+ import { UserService } from '@/services';
229
+
230
+ // 4. Relative imports (same feature) - LAST
231
+ import { AbstractRepository } from './base';
232
+ import { QueryBuilder } from '../query';
233
+ ```
234
+
235
+ **Rules:**
236
+ - Blank line between each group
237
+ - Alphabetical within each group
238
+ - `node:` prefix for Node.js built-ins
239
+ - Relative imports only for same feature/module
240
+
241
+ ## See Also
242
+
243
+ - [Error Handling](../error-handling) - Comprehensive error patterns
244
+ - [Logging Reference](../../references/helpers/logger) - Logger API
245
+ - [Function Patterns](./function-patterns) - Method organization
@@ -0,0 +1,221 @@
1
+ # Documentation (JSDoc)
2
+
3
+ Use JSDoc comments for public APIs to improve IDE support and generate documentation.
4
+
5
+ ## When to Use JSDoc
6
+
7
+ | Context | Required? | Reason |
8
+ |---------|-----------|--------|
9
+ | Public methods | Yes | Consumers need documentation |
10
+ | Exported functions | Yes | API contract documentation |
11
+ | Complex types | Yes | Clarify usage |
12
+ | Private methods | No | Internal, can change |
13
+ | Self-explanatory code | No | Avoid redundant docs |
14
+
15
+ ## JSDoc Format
16
+
17
+ ```typescript
18
+ /**
19
+ * Brief description of what the function does.
20
+ *
21
+ * @param opts - Description of the options object
22
+ * @param opts.filter - Query filter with where, limit, offset
23
+ * @param opts.options - Additional options like transaction
24
+ * @returns Promise resolving to the query result
25
+ *
26
+ * @example
27
+ * const users = await userRepo.find({
28
+ * filter: { where: { status: 'ACTIVE' }, limit: 10 },
29
+ * });
30
+ *
31
+ * @throws {ApplicationError} When validation fails
32
+ * @see {@link UserService} for business logic
33
+ */
34
+ async find(opts: TFindOptions): Promise<TFindResult<TUser>> {
35
+ // implementation
36
+ }
37
+ ```
38
+
39
+ ## Common JSDoc Tags
40
+
41
+ | Tag | Usage |
42
+ |-----|-------|
43
+ | `@param` | Document function parameters |
44
+ | `@returns` | Document return value |
45
+ | `@throws` | Document thrown exceptions |
46
+ | `@example` | Provide usage examples |
47
+ | `@see` | Reference related items |
48
+ | `@deprecated` | Mark as deprecated with migration path |
49
+ | `@internal` | Mark as internal (not public API) |
50
+ | `@since` | Version when feature was added |
51
+ | `@default` | Default value for optional parameter |
52
+
53
+ ## Examples
54
+
55
+ ### Service Method
56
+
57
+ ```typescript
58
+ /**
59
+ * Creates a new user account with the given data.
60
+ *
61
+ * Validates that the email is unique, hashes the password,
62
+ * and sends a welcome email upon successful creation.
63
+ *
64
+ * @param data - User creation data
65
+ * @returns The created user without sensitive fields
66
+ * @throws {ApplicationError} 409 if email already exists
67
+ * @throws {ApplicationError} 422 if validation fails
68
+ *
69
+ * @example
70
+ * const user = await userService.createUser({
71
+ * email: 'john@example.com',
72
+ * name: 'John Doe',
73
+ * password: 'SecurePass123!',
74
+ * });
75
+ */
76
+ async createUser(data: TCreateUserRequest): Promise<TUser> {
77
+ // ...
78
+ }
79
+ ```
80
+
81
+ ### Repository Method
82
+
83
+ ```typescript
84
+ /**
85
+ * Finds entities matching the given filter.
86
+ *
87
+ * @param opts - Find options
88
+ * @param opts.filter - Query filter
89
+ * @param opts.filter.where - Conditions to match
90
+ * @param opts.filter.limit - Maximum records to return (default: 100)
91
+ * @param opts.filter.offset - Records to skip for pagination
92
+ * @param opts.filter.order - Sort order (e.g., ['createdAt DESC'])
93
+ * @param opts.filter.include - Relations to load
94
+ * @returns Promise with data array and count
95
+ *
96
+ * @example
97
+ * // Find active users, sorted by name
98
+ * const result = await userRepo.find({
99
+ * filter: {
100
+ * where: { status: 'ACTIVE' },
101
+ * order: ['name ASC'],
102
+ * limit: 20,
103
+ * },
104
+ * });
105
+ */
106
+ async find(opts: TFindOpts<Schema>): Promise<TFindResult<TEntity>> {
107
+ // ...
108
+ }
109
+ ```
110
+
111
+ ### Deprecation
112
+
113
+ ```typescript
114
+ /**
115
+ * @deprecated Use {@link findById} instead. Will be removed in v2.0.
116
+ *
117
+ * @example
118
+ * // Before (deprecated)
119
+ * const user = await repo.getById('123');
120
+ *
121
+ * // After (recommended)
122
+ * const { data: user } = await repo.findById({ id: '123' });
123
+ */
124
+ async getById(id: string): Promise<TUser | null> {
125
+ return this.findById({ id }).then(r => r.data);
126
+ }
127
+ ```
128
+
129
+ ### Type Documentation
130
+
131
+ ```typescript
132
+ /**
133
+ * Options for configuring user audit columns.
134
+ *
135
+ * @property dataType - The database type for user IDs ('string' | 'number')
136
+ * @property columnName - The column name in the database
137
+ * @property allowAnonymous - Whether to allow null user IDs (default: true)
138
+ *
139
+ * @example
140
+ * const opts: TUserAuditColumnOpts = {
141
+ * dataType: 'string',
142
+ * columnName: 'created_by',
143
+ * allowAnonymous: false,
144
+ * };
145
+ */
146
+ type TUserAuditColumnOpts = {
147
+ dataType: 'string' | 'number';
148
+ columnName: string;
149
+ allowAnonymous?: boolean;
150
+ };
151
+ ```
152
+
153
+ ### Interface Documentation
154
+
155
+ ```typescript
156
+ /**
157
+ * Configuration for JWT authentication strategy.
158
+ *
159
+ * @interface IJWTStrategyOptions
160
+ * @property secret - Secret key for signing tokens (required)
161
+ * @property expiresIn - Token expiration time (default: '1h')
162
+ * @property algorithm - Signing algorithm (default: 'HS256')
163
+ * @property issuer - Token issuer claim
164
+ * @property audience - Token audience claim
165
+ */
166
+ interface IJWTStrategyOptions {
167
+ secret: string;
168
+ expiresIn?: string;
169
+ algorithm?: 'HS256' | 'HS384' | 'HS512';
170
+ issuer?: string;
171
+ audience?: string;
172
+ }
173
+ ```
174
+
175
+ ### Class Documentation
176
+
177
+ ```typescript
178
+ /**
179
+ * Base repository providing CRUD operations for database entities.
180
+ *
181
+ * Extends this class to create entity-specific repositories with
182
+ * type-safe operations and automatic schema binding.
183
+ *
184
+ * @template Schema - The Drizzle table schema type
185
+ *
186
+ * @example
187
+ * @repository({ model: User, dataSource: PostgresDataSource })
188
+ * export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {
189
+ * // Custom methods here
190
+ * }
191
+ *
192
+ * @see {@link BaseEntity} for model definition
193
+ * @see {@link BaseDataSource} for database connection
194
+ */
195
+ abstract class DefaultCRUDRepository<Schema extends TTableSchemaWithId> {
196
+ // ...
197
+ }
198
+ ```
199
+
200
+ ## Best Practices
201
+
202
+ ### Do
203
+
204
+ - Write descriptions in third person ("Creates...", "Returns...", "Validates...")
205
+ - Include `@example` for non-obvious usage
206
+ - Document all parameters with `@param`
207
+ - Use `@throws` for expected exceptions
208
+ - Link to related items with `@see` and `{@link}`
209
+
210
+ ### Don't
211
+
212
+ - Don't document obvious things (`@param id - The ID` - unhelpful)
213
+ - Don't copy TypeScript types into JSDoc (they're already visible)
214
+ - Don't write multi-paragraph descriptions for simple functions
215
+ - Don't use JSDoc for private implementation details
216
+
217
+ ## See Also
218
+
219
+ - [Type Safety](./type-safety) - TypeScript best practices
220
+ - [Function Patterns](./function-patterns) - Method organization
221
+ - [API Reference](../../references/) - Documentation examples
@@ -0,0 +1,142 @@
1
+ # Function Patterns
2
+
3
+ Consistent function patterns improve code readability and maintainability.
4
+
5
+ ## Module Exports
6
+
7
+ ### Prefer Named Exports
8
+
9
+ Avoid `export default` except for configuration files (e.g., `eslint.config.mjs`) or lazy-loaded components. Use named exports for all classes, functions, and constants.
10
+
11
+ **Why?**
12
+ - **Refactoring:** Renaming a symbol automatically updates imports across the monorepo
13
+ - **Consistency:** Enforces consistent naming across all files importing the module
14
+
15
+ ```typescript
16
+ // ✅ GOOD
17
+ export class UserController { }
18
+ export function createUser() { }
19
+ export const DEFAULT_OPTIONS = { };
20
+
21
+ // ❌ BAD
22
+ export default class UserController { }
23
+ ```
24
+
25
+ ## The Options Object Pattern
26
+
27
+ Prefer using a single object parameter (`opts`) over multiple positional arguments, especially for constructors and public methods with more than 2 arguments.
28
+
29
+ **Why?**
30
+ - **Extensibility:** You can add new properties without breaking existing calls
31
+ - **Readability:** Named keys act as documentation at the call site
32
+
33
+ ```typescript
34
+ // ✅ GOOD
35
+ class UserService {
36
+ createUser(opts: { name: string; email: string; role?: string }) {
37
+ // ...
38
+ }
39
+ }
40
+ // Usage: service.createUser({ name: 'John', email: 'john@example.com' });
41
+
42
+ // ❌ BAD
43
+ class UserService {
44
+ createUser(name: string, email: string, role?: string) {
45
+ // ...
46
+ }
47
+ }
48
+ // Usage: service.createUser('John', 'john@example.com');
49
+ ```
50
+
51
+ ## Function Naming Conventions
52
+
53
+ Use consistent prefixes based on function purpose:
54
+
55
+ | Prefix | Purpose | Examples |
56
+ |--------|---------|----------|
57
+ | `generate*` | Create column definitions / schemas | `generateIdColumnDefs()`, `generateTzColumnDefs()` |
58
+ | `build*` | Construct complex objects | `buildPrimitiveCondition()`, `buildJsonOrderBy()` |
59
+ | `to*` | Convert/transform data | `toCamel()`, `toBoolean()`, `toStringDecimal()` |
60
+ | `is*` | Boolean validation/check | `isWeekday()`, `isInt()`, `isFloat()`, `isPromiseLike()` |
61
+ | `extract*` | Pull out specific parts | `extractTimestamp()`, `extractWorkerId()`, `extractSequence()` |
62
+ | `enrich*` | Enhance with additional data | `enrichUserAudit()`, `enrichWithMetadata()` |
63
+ | `get*` | Retrieve/fetch data | `getSchema()`, `getConnector()`, `getError()` |
64
+ | `resolve*` | Determine/compute value | `resolveValue()`, `resolvePath()` |
65
+
66
+ **Examples:**
67
+
68
+ ```typescript
69
+ // Generators - create schema definitions
70
+ const idCols = generateIdColumnDefs({ id: { dataType: 'string' } });
71
+ const tzCols = generateTzColumnDefs();
72
+
73
+ // Builders - construct complex query objects
74
+ const condition = buildPrimitiveCondition(column, operator, value);
75
+ const orderBy = buildJsonOrderBy(schema, path, direction);
76
+
77
+ // Converters - transform data types
78
+ const camelCase = toCamel('snake_case');
79
+ const bool = toBoolean('true');
80
+ const decimal = toStringDecimal(123.456, 2);
81
+
82
+ // Validators - boolean checks
83
+ if (isWeekday(date)) { /* ... */ }
84
+ if (isInt(value)) { /* ... */ }
85
+ if (isPromiseLike(result)) { /* ... */ }
86
+
87
+ // Extractors - pull specific data
88
+ const timestamp = extractTimestamp(snowflakeId);
89
+ const workerId = extractWorkerId(snowflakeId);
90
+ ```
91
+
92
+ ## Scope Naming
93
+
94
+ Every class extending a base class should set its scope using `ClassName.name`:
95
+
96
+ ```typescript
97
+ export class JWTTokenService extends BaseService {
98
+ constructor() {
99
+ super({ scope: JWTTokenService.name });
100
+ }
101
+ }
102
+
103
+ export class UserController extends BaseController {
104
+ constructor() {
105
+ super({ scope: UserController.name });
106
+ }
107
+ }
108
+ ```
109
+
110
+ ## Performance Logging Pattern
111
+
112
+ Use `performance.now()` for timing critical operations:
113
+
114
+ ```typescript
115
+ const t = performance.now();
116
+
117
+ // ... operation to measure ...
118
+
119
+ this.logger.info('[methodName] DONE | Took: %s (ms)', performance.now() - t);
120
+ ```
121
+
122
+ **With the helper utility:**
123
+
124
+ ```typescript
125
+ import { executeWithPerformanceMeasure } from '@venizia/ignis';
126
+
127
+ await executeWithPerformanceMeasure({
128
+ logger: this.logger,
129
+ scope: 'DataSync',
130
+ description: 'Sync user records',
131
+ task: async () => {
132
+ await syncAllUsers();
133
+ },
134
+ });
135
+ // Logs: [DataSync] Sync user records | Took: 1234.56 (ms)
136
+ ```
137
+
138
+ ## See Also
139
+
140
+ - [Naming Conventions](./naming-conventions) - Class and file naming
141
+ - [Type Safety](./type-safety) - Typed function signatures
142
+ - [Route Definitions](./route-definitions) - Controller methods
@@ -0,0 +1,110 @@
1
+ # Code Style Standards
2
+
3
+ Maintain consistent code style using **Prettier** (formatting) and **ESLint** (code quality). Ignis provides centralized configurations via the `@venizia/dev-configs` package.
4
+
5
+ ## Quick Reference
6
+
7
+ | Aspect | Standard |
8
+ |--------|----------|
9
+ | Interface prefix | `I` (e.g., `IUserService`) |
10
+ | Type alias prefix | `T` (e.g., `TUserRequest`) |
11
+ | Class naming | PascalCase with suffix (e.g., `UserController`) |
12
+ | File naming | kebab-case (e.g., `user.controller.ts`) |
13
+ | Private fields | Underscore prefix (`_dataSource`) |
14
+ | Binding keys | `@app/[component]/[feature]` |
15
+ | Constants | Static readonly class (not enums) |
16
+ | Barrel exports | `index.ts` at every folder level |
17
+ | Error format | `[ClassName][method] Message` |
18
+ | Logging format | `[method] Message \| Key: %s` |
19
+ | Default options | `DEFAULT_OPTIONS` constant |
20
+ | Type safety | No `any` or `unknown` allowed |
21
+ | Scope naming | `ClassName.name` |
22
+ | Arguments | Options object (`opts`) |
23
+ | Exports | Named exports only |
24
+ | Return types | Explicitly defined |
25
+ | Control flow | Always use braces (`{}`) |
26
+ | Switch statements | Braces + default case required |
27
+ | Imports | Node → Third-party → Internal → Relative |
28
+ | Function naming | `generate*`, `build*`, `to*`, `is*`, `extract*` |
29
+
30
+ ## Sections
31
+
32
+ | Section | Description |
33
+ |---------|-------------|
34
+ | [Tooling](./tooling) | ESLint, Prettier, TypeScript configuration |
35
+ | [Naming Conventions](./naming-conventions) | Classes, files, types, binding keys |
36
+ | [Type Safety](./type-safety) | Avoiding `any`, explicit returns, generics |
37
+ | [Function Patterns](./function-patterns) | Options object, naming, exports |
38
+ | [Route Definitions](./route-definitions) | Config-driven routes, OpenAPI integration |
39
+ | [Constants & Configuration](./constants-configuration) | Static classes vs enums, defaults |
40
+ | [Control Flow & Organization](./control-flow) | Braces, switches, imports, logging |
41
+ | [Advanced Patterns](./advanced-patterns) | Mixins, factories, value resolvers |
42
+ | [Documentation (JSDoc)](./documentation) | When and how to document code |
43
+
44
+ ## Essential Examples
45
+
46
+ ### Naming
47
+
48
+ ```typescript
49
+ // Interfaces use 'I' prefix
50
+ interface IUserService { }
51
+
52
+ // Type aliases use 'T' prefix
53
+ type TUserRequest = { };
54
+
55
+ // Classes use PascalCase with suffix
56
+ class UserController extends BaseController { }
57
+ class UserService extends BaseService { }
58
+ class UserRepository extends BaseRepository { }
59
+ ```
60
+
61
+ ### File Structure
62
+
63
+ ```
64
+ src/components/auth/
65
+ ├── index.ts # Barrel exports
66
+ ├── component.ts # IoC binding
67
+ ├── controller.ts # Routes
68
+ └── common/
69
+ ├── index.ts
70
+ ├── keys.ts # Binding keys
71
+ └── types.ts # Interfaces
72
+ ```
73
+
74
+ ### Constants (Static Class vs Enum)
75
+
76
+ ```typescript
77
+ // ✅ GOOD - Static class (tree-shakable)
78
+ export class UserStatuses {
79
+ static readonly ACTIVE = 'active';
80
+ static readonly INACTIVE = 'inactive';
81
+ }
82
+
83
+ // ❌ AVOID - Enum (not tree-shakable)
84
+ enum UserStatus {
85
+ ACTIVE = 'active',
86
+ INACTIVE = 'inactive',
87
+ }
88
+ ```
89
+
90
+ ### Import Order
91
+
92
+ ```typescript
93
+ // 1. Node built-ins
94
+ import fs from 'node:fs';
95
+
96
+ // 2. Third-party
97
+ import { z } from '@hono/zod-openapi';
98
+
99
+ // 3. Internal absolute
100
+ import { getError } from '@venizia/ignis-helpers';
101
+
102
+ // 4. Relative (same feature)
103
+ import { QueryBuilder } from './query';
104
+ ```
105
+
106
+ ## See Also
107
+
108
+ - [Architectural Patterns](../architectural-patterns) - High-level design
109
+ - [Common Pitfalls](../common-pitfalls) - Mistakes to avoid
110
+ - [@venizia/dev-configs Reference](../../references/src-details/dev-configs) - Full tooling docs