@venizia/ignis-docs 0.0.8 → 0.1.0
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 +7 -7
- package/content/best-practices/api-usage-examples.md +15 -12
- package/content/best-practices/architectural-patterns.md +70 -78
- package/content/best-practices/architecture-decisions.md +91 -60
- package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
- package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
- package/content/best-practices/code-style-standards/control-flow.md +5 -2
- package/content/best-practices/code-style-standards/documentation.md +13 -13
- package/content/best-practices/code-style-standards/function-patterns.md +9 -10
- package/content/best-practices/code-style-standards/index.md +1 -1
- package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
- package/content/best-practices/code-style-standards/route-definitions.md +30 -12
- package/content/best-practices/code-style-standards/tooling.md +8 -5
- package/content/best-practices/code-style-standards/type-safety.md +13 -12
- package/content/best-practices/common-pitfalls.md +56 -37
- package/content/best-practices/contribution-workflow.md +13 -14
- package/content/best-practices/data-modeling.md +44 -20
- package/content/best-practices/deployment-strategies.md +28 -27
- package/content/best-practices/error-handling.md +48 -24
- package/content/best-practices/index.md +5 -5
- package/content/best-practices/performance-optimization.md +36 -28
- package/content/best-practices/security-guidelines.md +52 -23
- package/content/best-practices/testing-strategies.md +65 -51
- package/content/best-practices/troubleshooting-tips.md +24 -24
- package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
- package/content/extensions/components/authentication/api.md +19 -19
- package/content/extensions/components/authentication/errors.md +7 -7
- package/content/extensions/components/authentication/index.md +10 -8
- package/content/extensions/components/authentication/usage.md +101 -6
- package/content/extensions/components/authorization/api.md +45 -25
- package/content/extensions/components/authorization/errors.md +6 -6
- package/content/extensions/components/authorization/index.md +11 -10
- package/content/extensions/components/authorization/usage.md +21 -21
- package/content/extensions/components/health-check.md +1 -1
- package/content/extensions/components/index.md +5 -5
- package/content/extensions/components/mail/errors.md +15 -15
- package/content/extensions/components/mail/index.md +1 -2
- package/content/extensions/components/mail/usage.md +1 -1
- package/content/extensions/components/request-tracker.md +1 -1
- package/content/extensions/components/socket-io/api.md +9 -9
- package/content/extensions/components/socket-io/errors.md +5 -5
- package/content/extensions/components/socket-io/index.md +8 -8
- package/content/extensions/components/socket-io/usage.md +1 -1
- package/content/extensions/components/static-asset/api.md +17 -4
- package/content/extensions/components/static-asset/errors.md +4 -4
- package/content/extensions/components/static-asset/index.md +26 -28
- package/content/extensions/components/static-asset/usage.md +13 -12
- package/content/extensions/components/template/index.md +2 -2
- package/content/extensions/components/template/setup-page.md +1 -1
- package/content/extensions/components/websocket/api.md +3 -3
- package/content/extensions/components/websocket/errors.md +5 -5
- package/content/extensions/components/websocket/index.md +5 -5
- package/content/extensions/components/websocket/usage.md +3 -3
- package/content/extensions/helpers/cron/index.md +2 -2
- package/content/extensions/helpers/crypto/index.md +1 -1
- package/content/extensions/helpers/env/index.md +27 -12
- package/content/extensions/helpers/error/index.md +81 -25
- package/content/extensions/helpers/index.md +2 -3
- package/content/extensions/helpers/inversion/index.md +15 -7
- package/content/extensions/helpers/kafka/examples.md +1 -1
- package/content/extensions/helpers/logger/index.md +32 -2
- package/content/extensions/helpers/network/index.md +6 -0
- package/content/extensions/helpers/queue/index.md +14 -17
- package/content/extensions/helpers/redis/index.md +548 -323
- package/content/extensions/helpers/socket-io/index.md +14 -10
- package/content/extensions/helpers/storage/api.md +44 -8
- package/content/extensions/helpers/storage/index.md +43 -7
- package/content/extensions/helpers/template/index.md +6 -3
- package/content/extensions/helpers/types/index.md +11 -8
- package/content/extensions/helpers/websocket/api.md +9 -9
- package/content/extensions/helpers/websocket/index.md +7 -7
- package/content/extensions/helpers/worker-thread/index.md +2 -2
- package/content/extensions/index.md +3 -4
- package/content/extensions/src-details/mcp-server.md +18 -24
- package/content/guides/core-concepts/application/bootstrapping.md +11 -14
- package/content/guides/core-concepts/application/index.md +3 -3
- package/content/guides/core-concepts/components.md +19 -10
- package/content/guides/core-concepts/dependency-injection.md +6 -3
- package/content/guides/core-concepts/grpc-controllers.md +6 -5
- package/content/guides/core-concepts/persistent/datasources.md +33 -27
- package/content/guides/core-concepts/persistent/index.md +16 -5
- package/content/guides/core-concepts/persistent/models.md +24 -20
- package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
- package/content/guides/core-concepts/persistent/repositories.md +40 -23
- package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
- package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
- package/content/guides/core-concepts/persistent/transactions.md +61 -25
- package/content/guides/core-concepts/rest-controllers.md +12 -9
- package/content/guides/core-concepts/services.md +330 -60
- package/content/guides/get-started/5-minute-quickstart.md +15 -15
- package/content/guides/get-started/philosophy.md +36 -36
- package/content/guides/get-started/setup.md +3 -3
- package/content/guides/index.md +3 -3
- package/content/guides/migrations/redis-helpers-migration.md +177 -0
- package/content/guides/migrations/scoped-rbac-migration.md +17 -17
- package/content/guides/migrations/unified-connectors-migration.md +113 -0
- package/content/guides/reference/glossary.md +19 -12
- package/content/guides/reference/mcp-docs-server.md +22 -18
- package/content/guides/tutorials/building-a-crud-api.md +30 -33
- package/content/guides/tutorials/complete-installation.md +17 -17
- package/content/guides/tutorials/ecommerce-api.md +158 -119
- package/content/guides/tutorials/realtime-chat.md +176 -130
- package/content/guides/tutorials/testing.md +65 -523
- package/content/index.md +2 -180
- package/content/public/apple-touch-icon.png +0 -0
- package/content/public/og-image.png +0 -0
- package/content/public/site.webmanifest +11 -0
- package/content/references/base/application.md +4 -5
- package/content/references/base/bootstrapping.md +18 -5
- package/content/references/base/components.md +149 -120
- package/content/references/base/connectors.md +178 -0
- package/content/references/base/controllers.md +41 -30
- package/content/references/base/datasources.md +154 -81
- package/content/references/base/dependency-injection.md +34 -22
- package/content/references/base/filter-system/application-usage.md +17 -14
- package/content/references/base/filter-system/array-operators.md +7 -2
- package/content/references/base/filter-system/comparison-operators.md +3 -0
- package/content/references/base/filter-system/default-filter.md +89 -71
- package/content/references/base/filter-system/fields-order-pagination.md +22 -22
- package/content/references/base/filter-system/index.md +6 -3
- package/content/references/base/filter-system/json-filtering.md +20 -1
- package/content/references/base/filter-system/list-operators.md +1 -1
- package/content/references/base/filter-system/logical-operators.md +33 -1
- package/content/references/base/filter-system/null-operators.md +30 -1
- package/content/references/base/filter-system/quick-reference.md +23 -4
- package/content/references/base/filter-system/tips.md +5 -5
- package/content/references/base/filter-system/use-cases.md +12 -12
- package/content/references/base/grpc-controllers.md +13 -13
- package/content/references/base/index.md +24 -12
- package/content/references/base/middlewares.md +265 -327
- package/content/references/base/models.md +63 -49
- package/content/references/base/providers.md +136 -130
- package/content/references/base/repositories/advanced.md +59 -58
- package/content/references/base/repositories/index.md +115 -91
- package/content/references/base/repositories/mixins.md +55 -291
- package/content/references/base/repositories/relations.md +54 -64
- package/content/references/base/repositories/soft-deletable.md +31 -30
- package/content/references/base/services.md +296 -93
- package/content/references/configuration/environment-variables.md +46 -30
- package/content/references/configuration/index.md +6 -6
- package/content/references/index.md +17 -12
- package/content/references/quick-reference.md +65 -106
- package/content/references/utilities/crypto.md +65 -23
- package/content/references/utilities/index.md +3 -3
- package/content/references/utilities/jsx.md +6 -4
- package/content/references/utilities/module.md +68 -20
- package/content/references/utilities/parse.md +4 -14
- package/content/references/utilities/promise.md +9 -7
- package/content/references/utilities/schema.md +5 -3
- package/dist/mcp-server/common/guards.d.ts +8 -0
- package/dist/mcp-server/common/guards.d.ts.map +1 -0
- package/dist/mcp-server/common/guards.js +14 -0
- package/dist/mcp-server/common/guards.js.map +1 -0
- package/dist/mcp-server/common/index.d.ts +1 -0
- package/dist/mcp-server/common/index.d.ts.map +1 -1
- package/dist/mcp-server/common/index.js +1 -0
- package/dist/mcp-server/common/index.js.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
- package/dist/mcp-server/helpers/docs.helper.js +4 -2
- package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
- package/dist/mcp-server/helpers/github.helper.js +1 -1
- package/dist/mcp-server/index.js +7 -2
- package/dist/mcp-server/index.js.map +1 -1
- package/dist/mcp-server/tools/base.tool.d.ts +6 -2
- package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/base.tool.js.map +1 -1
- package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
- package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
- package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
- package/package.json +9 -9
- package/content/extensions/helpers/testing/index.md +0 -510
- package/content/references/base/middleware.md +0 -347
|
@@ -55,13 +55,13 @@ Use consistent prefixes based on function purpose:
|
|
|
55
55
|
| Prefix | Purpose | Examples |
|
|
56
56
|
|--------|---------|----------|
|
|
57
57
|
| `generate*` | Create column definitions / schemas | `generateIdColumnDefs()`, `generateTzColumnDefs()` |
|
|
58
|
-
| `build*` | Construct complex objects | `
|
|
59
|
-
| `to*` | Convert/transform data | `toCamel()`, `toBoolean()
|
|
58
|
+
| `build*` | Construct complex objects | `buildValueCondition()`, `buildJsonOrderBy()` |
|
|
59
|
+
| `to*` | Convert/transform data | `toCamel()`, `toBoolean()` |
|
|
60
60
|
| `is*` | Boolean validation/check | `isWeekday()`, `isInt()`, `isFloat()`, `isPromiseLike()` |
|
|
61
61
|
| `extract*` | Pull out specific parts | `extractTimestamp()`, `extractWorkerId()`, `extractSequence()` |
|
|
62
62
|
| `enrich*` | Enhance with additional data | `enrichUserAudit()`, `enrichWithMetadata()` |
|
|
63
63
|
| `get*` | Retrieve/fetch data | `getSchema()`, `getConnector()`, `getError()` |
|
|
64
|
-
| `resolve*` | Determine/compute value | `resolveValue()`, `
|
|
64
|
+
| `resolve*` | Determine/compute value | `resolveValue()`, `resolveClass()` |
|
|
65
65
|
|
|
66
66
|
**Examples:**
|
|
67
67
|
|
|
@@ -71,13 +71,12 @@ const idCols = generateIdColumnDefs({ id: { dataType: 'string' } });
|
|
|
71
71
|
const tzCols = generateTzColumnDefs();
|
|
72
72
|
|
|
73
73
|
// Builders - construct complex query objects
|
|
74
|
-
const condition =
|
|
74
|
+
const condition = buildValueCondition(column, value);
|
|
75
75
|
const orderBy = buildJsonOrderBy(schema, path, direction);
|
|
76
76
|
|
|
77
77
|
// Converters - transform data types
|
|
78
78
|
const camelCase = toCamel('snake_case');
|
|
79
79
|
const bool = toBoolean('true');
|
|
80
|
-
const decimal = toStringDecimal(123.456, 2);
|
|
81
80
|
|
|
82
81
|
// Validators - boolean checks
|
|
83
82
|
if (isWeekday(date)) { /* ... */ }
|
|
@@ -94,9 +93,9 @@ const workerId = extractWorkerId(snowflakeId);
|
|
|
94
93
|
Every class extending a base class should set its scope using `ClassName.name`:
|
|
95
94
|
|
|
96
95
|
```typescript
|
|
97
|
-
export class
|
|
96
|
+
export class PaymentService extends BaseService {
|
|
98
97
|
constructor() {
|
|
99
|
-
super({ scope:
|
|
98
|
+
super({ scope: PaymentService.name });
|
|
100
99
|
}
|
|
101
100
|
}
|
|
102
101
|
|
|
@@ -135,7 +134,7 @@ await executeWithPerformanceMeasure({
|
|
|
135
134
|
await syncAllUsers();
|
|
136
135
|
},
|
|
137
136
|
});
|
|
138
|
-
// Logs: [DataSync] Sync user records | Took: 1234.56 (ms)
|
|
137
|
+
// Logs: [DataSync] DONE | Sync user records | Took: 1234.56 (ms)
|
|
139
138
|
```
|
|
140
139
|
|
|
141
140
|
**Method-scoped logging pattern:**
|
|
@@ -144,13 +143,13 @@ await executeWithPerformanceMeasure({
|
|
|
144
143
|
class UserService {
|
|
145
144
|
private logger = Logger.get('UserService');
|
|
146
145
|
|
|
147
|
-
async createUser(data:
|
|
146
|
+
async createUser(data: TCreateUserRequest) {
|
|
148
147
|
// Use .for() to add method context to all logs
|
|
149
148
|
this.logger.for('createUser').info('Creating user: %j', data);
|
|
150
149
|
// Output: [UserService-createUser] Creating user: {...}
|
|
151
150
|
|
|
152
151
|
try {
|
|
153
|
-
const user = await this.
|
|
152
|
+
const { data: user } = await this.userRepository.create({ data });
|
|
154
153
|
this.logger.for('createUser').info('User created: %s', user.id);
|
|
155
154
|
return user;
|
|
156
155
|
} catch (error) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Code Style Standards
|
|
2
2
|
|
|
3
|
-
Maintain consistent code style using **Prettier** (formatting) and **ESLint** (code quality).
|
|
3
|
+
Maintain consistent code style using **Prettier** (formatting) and **ESLint** (code quality). IGNIS provides centralized configurations via the `@venizia/dev-configs` package.
|
|
4
4
|
|
|
5
5
|
## Quick Reference
|
|
6
6
|
|
|
@@ -57,9 +57,9 @@ export * from './types';
|
|
|
57
57
|
|------|---------|---------|
|
|
58
58
|
| Components | `[Feature]Component` | `HealthCheckComponent`, `AuthComponent` |
|
|
59
59
|
| Controllers | `[Feature]Controller` | `UserController`, `AuthController` |
|
|
60
|
-
| Services | `[Feature]Service` | `
|
|
60
|
+
| Services | `[Feature]Service` | `JWSTokenService`, `PaymentService` |
|
|
61
61
|
| Repositories | `[Feature]Repository` | `UserRepository`, `OrderRepository` |
|
|
62
|
-
| Strategies | `[Feature]Strategy` | `
|
|
62
|
+
| Strategies | `[Feature]Strategy` | `JWSAuthenticationStrategy`, `BasicAuthenticationStrategy` |
|
|
63
63
|
| Factories | `[Feature]Factory` | `UIProviderFactory` |
|
|
64
64
|
|
|
65
65
|
## File Names
|
|
@@ -90,15 +90,17 @@ interface IHealthCheckOptions {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
interface IAuthService {
|
|
93
|
-
signIn(context: Context): Promise<
|
|
93
|
+
signIn(context: Context, opts: TSignInRequest): Promise<AnyObject>;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// Type aliases use 'T' prefix
|
|
97
97
|
type TSignInRequest = z.infer<typeof SignInRequestSchema>;
|
|
98
|
-
type
|
|
98
|
+
type TAuthStrategy = TConstValue<typeof AuthenticateStrategy>;
|
|
99
99
|
|
|
100
100
|
// Generic constraints
|
|
101
|
-
type TTableSchemaWithId =
|
|
101
|
+
type TTableSchemaWithId<TC extends TableConfig = TableConfig> = PgTable<TC> & {
|
|
102
|
+
id: TIdColumn;
|
|
103
|
+
};
|
|
102
104
|
```
|
|
103
105
|
|
|
104
106
|
## Binding Keys
|
|
@@ -123,14 +125,14 @@ Use underscore prefix (`_`) for private and protected class fields to distinguis
|
|
|
123
125
|
```typescript
|
|
124
126
|
class MyRepository extends DefaultCRUDRepository {
|
|
125
127
|
// Private fields with underscore prefix
|
|
126
|
-
private _dataSource:
|
|
127
|
-
private _entity:
|
|
128
|
+
private _dataSource: AbstractDataSource;
|
|
129
|
+
private _entity: AbstractEntity;
|
|
128
130
|
private _hiddenProperties: Set<string> | null = null;
|
|
129
131
|
|
|
130
132
|
// Protected fields also use underscore prefix
|
|
131
133
|
protected _schemaFactory?: ReturnType<typeof createSchemaFactory>;
|
|
132
134
|
|
|
133
|
-
constructor(dataSource:
|
|
135
|
+
constructor(dataSource: AbstractDataSource) {
|
|
134
136
|
// 'dataSource' (param) vs '_dataSource' (field)
|
|
135
137
|
this._dataSource = dataSource;
|
|
136
138
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Route Definitions
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
IGNIS supports multiple methods for defining routes. Choose based on your needs.
|
|
4
4
|
|
|
5
5
|
## Method 1: Config-Driven Routes
|
|
6
6
|
|
|
@@ -15,12 +15,14 @@ export class UserRestPaths {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
// common/route-configs.ts
|
|
18
|
+
// jsonResponse({ schema }) expands to a 200 response plus a '4xx | 5xx' error fallback
|
|
18
19
|
export const RouteConfigs = {
|
|
19
20
|
GET_USERS: {
|
|
20
21
|
method: HTTP.Methods.GET,
|
|
21
22
|
path: UserRestPaths.ROOT,
|
|
22
23
|
responses: jsonResponse({
|
|
23
|
-
|
|
24
|
+
description: 'List of users',
|
|
25
|
+
schema: UserListSchema,
|
|
24
26
|
}),
|
|
25
27
|
},
|
|
26
28
|
GET_USER_BY_ID: {
|
|
@@ -30,8 +32,8 @@ export const RouteConfigs = {
|
|
|
30
32
|
params: z.object({ id: z.string() }),
|
|
31
33
|
},
|
|
32
34
|
responses: jsonResponse({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
+
description: 'User detail',
|
|
36
|
+
schema: UserSchema,
|
|
35
37
|
}),
|
|
36
38
|
},
|
|
37
39
|
} as const;
|
|
@@ -42,6 +44,9 @@ export const RouteConfigs = {
|
|
|
42
44
|
```typescript
|
|
43
45
|
@controller({ path: '/users' })
|
|
44
46
|
export class UserController extends BaseRestController {
|
|
47
|
+
constructor() {
|
|
48
|
+
super({ scope: UserController.name });
|
|
49
|
+
}
|
|
45
50
|
|
|
46
51
|
@api({ configs: RouteConfigs.GET_USERS })
|
|
47
52
|
list(context: TRouteContext) {
|
|
@@ -58,14 +63,18 @@ export class UserController extends BaseRestController {
|
|
|
58
63
|
|
|
59
64
|
## Method 3: Using `bindRoute` (Programmatic)
|
|
60
65
|
|
|
66
|
+
Register routes in the `binding()` lifecycle method:
|
|
67
|
+
|
|
61
68
|
```typescript
|
|
62
69
|
@controller({ path: '/health' })
|
|
63
70
|
export class HealthCheckController extends BaseRestController {
|
|
64
71
|
constructor() {
|
|
65
72
|
super({ scope: HealthCheckController.name });
|
|
73
|
+
}
|
|
66
74
|
|
|
75
|
+
override binding(): ValueOrPromise<void> {
|
|
67
76
|
this.bindRoute({ configs: RouteConfigs.GET_HEALTH }).to({
|
|
68
|
-
handler: context => context.json({ status: 'ok' }),
|
|
77
|
+
handler: context => context.json({ status: 'ok' }, HTTP.ResultCodes.RS_2.Ok),
|
|
69
78
|
});
|
|
70
79
|
}
|
|
71
80
|
}
|
|
@@ -78,11 +87,13 @@ export class HealthCheckController extends BaseRestController {
|
|
|
78
87
|
export class HealthCheckController extends BaseRestController {
|
|
79
88
|
constructor() {
|
|
80
89
|
super({ scope: HealthCheckController.name });
|
|
90
|
+
}
|
|
81
91
|
|
|
92
|
+
override binding(): ValueOrPromise<void> {
|
|
82
93
|
this.defineRoute({
|
|
83
94
|
configs: RouteConfigs.POST_PING,
|
|
84
95
|
handler: context => {
|
|
85
|
-
const { message } = context.req.valid('json');
|
|
96
|
+
const { message } = context.req.valid<{ message: string }>('json');
|
|
86
97
|
return context.json({ echo: message }, HTTP.ResultCodes.RS_2.Ok);
|
|
87
98
|
},
|
|
88
99
|
});
|
|
@@ -124,6 +135,8 @@ const UserSchema = z.object({
|
|
|
124
135
|
## Request Validation
|
|
125
136
|
|
|
126
137
|
```typescript
|
|
138
|
+
// Use an explicit status-code map (with jsonContent) when the success
|
|
139
|
+
// code is not 200 - jsonResponse() always keys the success response at 200
|
|
127
140
|
export const RouteConfigs = {
|
|
128
141
|
CREATE_USER: {
|
|
129
142
|
method: HTTP.Methods.POST,
|
|
@@ -134,11 +147,16 @@ export const RouteConfigs = {
|
|
|
134
147
|
description: 'User data',
|
|
135
148
|
}),
|
|
136
149
|
},
|
|
137
|
-
responses:
|
|
138
|
-
[HTTP.ResultCodes.RS_2.Created]:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
responses: {
|
|
151
|
+
[HTTP.ResultCodes.RS_2.Created]: jsonContent({
|
|
152
|
+
description: 'Created user',
|
|
153
|
+
schema: UserSchema,
|
|
154
|
+
}),
|
|
155
|
+
['4xx | 5xx']: jsonContent({
|
|
156
|
+
description: 'Error Response',
|
|
157
|
+
schema: ErrorSchema,
|
|
158
|
+
}),
|
|
159
|
+
},
|
|
142
160
|
},
|
|
143
161
|
} as const;
|
|
144
162
|
```
|
|
@@ -147,4 +165,4 @@ export const RouteConfigs = {
|
|
|
147
165
|
|
|
148
166
|
- [API Usage Examples](../api-usage-examples) - Full API patterns
|
|
149
167
|
- [Controllers Reference](../../references/base/controllers) - Controller API
|
|
150
|
-
- [Swagger Component](../../extensions/components/
|
|
168
|
+
- [Swagger Component](../../extensions/components/api-reference) - OpenAPI setup
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Tooling Configuration
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
IGNIS provides centralized development configurations via the `@venizia/dev-configs` package.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ bun add -d @venizia/dev-configs
|
|
|
10
10
|
|
|
11
11
|
This package provides:
|
|
12
12
|
- **ESLint rules** - Pre-configured for Node.js/TypeScript projects
|
|
13
|
-
- **Prettier settings** - Consistent formatting across all
|
|
13
|
+
- **Prettier settings** - Consistent formatting across all IGNIS projects
|
|
14
14
|
- **TypeScript configs** - Shared base and common configurations
|
|
15
15
|
|
|
16
16
|
## Prettier Configuration
|
|
@@ -29,8 +29,9 @@ export default prettierConfigs;
|
|
|
29
29
|
| Setting | Value | Description |
|
|
30
30
|
|---------|-------|-------------|
|
|
31
31
|
| `bracketSpacing` | `true` | `{ foo: bar }` |
|
|
32
|
-
| `singleQuote` | `
|
|
32
|
+
| `singleQuote` | `true` | `'string'` (single quotes) |
|
|
33
33
|
| `printWidth` | `100` | Maximum line length |
|
|
34
|
+
| `tabWidth` | `2` | 2-space indentation |
|
|
34
35
|
| `trailingComma` | `'all'` | `[1, 2, 3,]` |
|
|
35
36
|
| `arrowParens` | `'avoid'` | `x => x` not `(x) => x` |
|
|
36
37
|
| `semi` | `true` | Semicolons required |
|
|
@@ -65,6 +66,7 @@ export default eslintConfigs;
|
|
|
65
66
|
**Includes:**
|
|
66
67
|
- Pre-configured rules for Node.js/TypeScript (via `@minimaltech/eslint-node`)
|
|
67
68
|
- Disables `@typescript-eslint/no-explicit-any` by default
|
|
69
|
+
- Enforces mandatory braces: `curly: ['error', 'all']` and `unicorn/switch-case-braces: ['error', 'always']`
|
|
68
70
|
|
|
69
71
|
**Customization:**
|
|
70
72
|
```javascript
|
|
@@ -113,9 +115,10 @@ Use the centralized TypeScript configs:
|
|
|
113
115
|
|
|
114
116
|
| Option | Value | Purpose |
|
|
115
117
|
|--------|-------|---------|
|
|
116
|
-
| `target` | `
|
|
117
|
-
| `experimentalDecorators` | `true` | Required for
|
|
118
|
+
| `target` | `ES2024` | Modern JavaScript features |
|
|
119
|
+
| `experimentalDecorators` | `true` | Required for IGNIS decorators |
|
|
118
120
|
| `emitDecoratorMetadata` | `true` | Metadata reflection for DI |
|
|
121
|
+
| `useDefineForClassFields` | `false` | Decorator-compatible class fields |
|
|
119
122
|
| `strict` | `true` | Strict type checking |
|
|
120
123
|
| `skipLibCheck` | `true` | Faster compilation |
|
|
121
124
|
|
|
@@ -77,16 +77,17 @@ type RouteKey = keyof typeof RouteConfigs; // 'GET_USERS' | 'GET_USER_BY_ID'
|
|
|
77
77
|
|
|
78
78
|
```typescript
|
|
79
79
|
export class DefaultCRUDRepository<
|
|
80
|
-
|
|
80
|
+
EntitySchema extends TTableSchemaWithId = TTableSchemaWithId
|
|
81
81
|
> {
|
|
82
|
-
//
|
|
82
|
+
// EntitySchema is constrained to have an 'id' column
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export interface IAuthService<
|
|
86
|
+
E extends Env = Env,
|
|
86
87
|
SIRQ extends TSignInRequest = TSignInRequest,
|
|
87
88
|
SIRS = AnyObject,
|
|
88
89
|
> {
|
|
89
|
-
signIn(context:
|
|
90
|
+
signIn(context: TContext<E>, opts: SIRQ): Promise<SIRS>;
|
|
90
91
|
}
|
|
91
92
|
```
|
|
92
93
|
|
|
@@ -96,21 +97,21 @@ Use TypeScript method overloads when return types depend on input options:
|
|
|
96
97
|
|
|
97
98
|
```typescript
|
|
98
99
|
class Repository<T, R> {
|
|
99
|
-
// Overload 1: shouldReturn: false → data
|
|
100
|
-
create(opts: { data: T; options: { shouldReturn: false } }): Promise<{ count: number; data: null }>;
|
|
100
|
+
// Overload 1: shouldReturn: false → no data returned
|
|
101
|
+
create(opts: { data: T; options: { shouldReturn: false } }): Promise<{ count: number; data: undefined | null }>;
|
|
101
102
|
// Overload 2: shouldReturn: true (default) → data is R
|
|
102
103
|
create(opts: { data: T; options?: { shouldReturn?: true } }): Promise<{ count: number; data: R }>;
|
|
103
104
|
// Implementation signature
|
|
104
|
-
create(opts: { data: T; options?: { shouldReturn?: boolean } }): Promise<{ count: number; data: R | null }> {
|
|
105
|
+
create(opts: { data: T; options?: { shouldReturn?: boolean } }): Promise<{ count: number; data: R | undefined | null }> {
|
|
105
106
|
// implementation
|
|
106
107
|
}
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
// Usage
|
|
110
|
-
const result1 = await
|
|
111
|
-
// result1.data is typed as null
|
|
111
|
+
const result1 = await userRepository.create({ data: user, options: { shouldReturn: false } });
|
|
112
|
+
// result1.data is typed as undefined | null
|
|
112
113
|
|
|
113
|
-
const result2 = await
|
|
114
|
+
const result2 = await userRepository.create({ data: user });
|
|
114
115
|
// result2.data is typed as R (the entity type)
|
|
115
116
|
```
|
|
116
117
|
|
|
@@ -151,10 +152,10 @@ function processResult<T>(result: TResult<T>) {
|
|
|
151
152
|
if (result.success) {
|
|
152
153
|
// TypeScript knows result.data exists
|
|
153
154
|
return result.data;
|
|
154
|
-
} else {
|
|
155
|
-
// TypeScript knows result.error exists
|
|
156
|
-
throw new Error(result.error);
|
|
157
155
|
}
|
|
156
|
+
|
|
157
|
+
// TypeScript knows result.error exists
|
|
158
|
+
throw getError({ message: result.error });
|
|
158
159
|
}
|
|
159
160
|
```
|
|
160
161
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Common Pitfalls
|
|
2
2
|
|
|
3
|
-
Avoid these common mistakes when building
|
|
3
|
+
Avoid these common mistakes when building IGNIS applications.
|
|
4
4
|
|
|
5
5
|
## 1. Forgetting to Register Resources
|
|
6
6
|
|
|
@@ -55,7 +55,7 @@ export class Application extends BaseApplication {
|
|
|
55
55
|
})
|
|
56
56
|
|
|
57
57
|
// ❌ BAD - typo in string (note: "Repository" is misspelled)
|
|
58
|
-
@inject({ key: 'repositories.
|
|
58
|
+
@inject({ key: 'repositories.ConfigurationRepositry' })
|
|
59
59
|
```
|
|
60
60
|
|
|
61
61
|
## 3. Business Logic in Controllers
|
|
@@ -66,7 +66,7 @@ export class Application extends BaseApplication {
|
|
|
66
66
|
|
|
67
67
|
- **Bad:**
|
|
68
68
|
```typescript
|
|
69
|
-
import {
|
|
69
|
+
import { getError, HTTP } from '@venizia/ignis-helpers';
|
|
70
70
|
|
|
71
71
|
// In a Controller
|
|
72
72
|
async createUser(c: Context) {
|
|
@@ -79,7 +79,9 @@ export class Application extends BaseApplication {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const company = await this.companyRepository.findOrCreate(companyName);
|
|
82
|
-
const user = await this.userRepository.create({
|
|
82
|
+
const user = await this.userRepository.create({
|
|
83
|
+
data: { name, email, companyId: company.id },
|
|
84
|
+
});
|
|
83
85
|
|
|
84
86
|
return c.json(user, HTTP.ResultCodes.RS_2.Ok);
|
|
85
87
|
}
|
|
@@ -99,7 +101,7 @@ export class Application extends BaseApplication {
|
|
|
99
101
|
// All the complex logic now resides in the service
|
|
100
102
|
const existingUser = await this.userRepository.findByEmail(data.email);
|
|
101
103
|
// ...
|
|
102
|
-
return await this.userRepository.create(
|
|
104
|
+
return await this.userRepository.create({ data });
|
|
103
105
|
}
|
|
104
106
|
```
|
|
105
107
|
|
|
@@ -142,7 +144,7 @@ This ensures that the route configuration object is treated as a readonly litera
|
|
|
142
144
|
|
|
143
145
|
**Problem:** Attempting to update or delete all records without an explicit `where` condition.
|
|
144
146
|
|
|
145
|
-
**Solution:**
|
|
147
|
+
**Solution:** IGNIS prevents accidental bulk data destruction. You must either provide a `where` condition or explicitly set `force: true`.
|
|
146
148
|
|
|
147
149
|
```typescript
|
|
148
150
|
// ❌ BAD - Will throw error
|
|
@@ -150,7 +152,8 @@ await userRepository.updateBy({
|
|
|
150
152
|
data: { status: 'INACTIVE' },
|
|
151
153
|
where: {}, // Empty where = targets ALL records
|
|
152
154
|
});
|
|
153
|
-
// Error: [
|
|
155
|
+
// Error: [_update] Entity: User | DENY to perform update | Empty where condition
|
|
156
|
+
// (updateBy is an alias that delegates to updateAll)
|
|
154
157
|
|
|
155
158
|
// ✅ GOOD - Explicit where condition
|
|
156
159
|
await userRepository.updateBy({
|
|
@@ -175,26 +178,27 @@ await userRepository.updateBy({
|
|
|
175
178
|
|
|
176
179
|
**Error Message:**
|
|
177
180
|
```
|
|
178
|
-
[UserRepository] Schema key mismatch | Entity name 'User' not found in connector.query | Available keys: [Configuration, Post]
|
|
181
|
+
[UserRepository] Schema key mismatch | Entity name 'User' not found in connector.query | Available keys: [Configuration, Post] | Ensure the model's TABLE_NAME matches the schema registration key
|
|
179
182
|
```
|
|
180
183
|
|
|
181
|
-
**Solution:**
|
|
184
|
+
**Solution:** The schema registration key follows the precedence `@model tableName metadata > static TABLE_NAME > class name`, while the repository looks the query interface up by the entity **class name**. Do not set `TABLE_NAME` (or `tableName` metadata) to a value that differs from the class name:
|
|
182
185
|
|
|
183
186
|
```typescript
|
|
184
|
-
// ❌ BAD -
|
|
187
|
+
// ❌ BAD - Schema is registered under 'users', but looked up as 'User'
|
|
185
188
|
@model({ type: 'entity' })
|
|
186
|
-
export class User extends
|
|
187
|
-
static override
|
|
189
|
+
export class User extends BasePostgresEntity<typeof User.schema> {
|
|
190
|
+
static override TABLE_NAME = 'users'; // Differs from class name!
|
|
191
|
+
static override schema = pgTable('User', { /* ... */ });
|
|
188
192
|
}
|
|
189
193
|
|
|
190
|
-
// ✅ GOOD -
|
|
194
|
+
// ✅ GOOD - No TABLE_NAME override; registration key defaults to the class name
|
|
191
195
|
@model({ type: 'entity' })
|
|
192
|
-
export class User extends
|
|
193
|
-
static override schema = pgTable('User', { /* ... */ });
|
|
196
|
+
export class User extends BasePostgresEntity<typeof User.schema> {
|
|
197
|
+
static override schema = pgTable('User', { /* ... */ });
|
|
194
198
|
}
|
|
195
199
|
```
|
|
196
200
|
|
|
197
|
-
**Why this matters:** The framework uses `entity.name` (class name) to look up the query interface in `connector.query
|
|
201
|
+
**Why this matters:** The framework uses `entity.name` (class name) to look up the query interface in `connector.query`, but registers the schema under the resolved table name. If they don't match, the repository can't find its table. The first argument of `pgTable()` is the physical SQL table name and is independent of this lookup - by convention it matches the class name.
|
|
198
202
|
|
|
199
203
|
## 8. Validation Error Response Structure
|
|
200
204
|
|
|
@@ -205,9 +209,12 @@ export class User extends BaseEntity<typeof User.schema> {
|
|
|
205
209
|
```json
|
|
206
210
|
{
|
|
207
211
|
"statusCode": 422,
|
|
208
|
-
"message": "
|
|
212
|
+
"message": "Invalid email",
|
|
213
|
+
"messageCode": "invalid_string",
|
|
209
214
|
"requestId": "abc123",
|
|
210
215
|
"details": {
|
|
216
|
+
"url": "http://localhost:3000/users",
|
|
217
|
+
"path": "/users",
|
|
211
218
|
"cause": [
|
|
212
219
|
{
|
|
213
220
|
"path": "email",
|
|
@@ -252,10 +259,10 @@ try {
|
|
|
252
259
|
|
|
253
260
|
```typescript
|
|
254
261
|
// ❌ BAD - Direct import causes circular dependency
|
|
255
|
-
import {
|
|
262
|
+
import { User } from './user.model';
|
|
256
263
|
|
|
257
264
|
@model({ type: 'entity' })
|
|
258
|
-
export class Order extends
|
|
265
|
+
export class Order extends BasePostgresEntity<typeof Order.schema> {
|
|
259
266
|
static override relations = (): TRelationConfig[] => [
|
|
260
267
|
{ schema: User.schema, ... }, // User imports Order, Order imports User
|
|
261
268
|
];
|
|
@@ -263,7 +270,7 @@ export class Order extends BaseEntity<typeof Order.schema> {
|
|
|
263
270
|
|
|
264
271
|
// ✅ GOOD - Lazy import breaks the cycle
|
|
265
272
|
@model({ type: 'entity' })
|
|
266
|
-
export class Order extends
|
|
273
|
+
export class Order extends BasePostgresEntity<typeof Order.schema> {
|
|
267
274
|
static override relations = (): TRelationConfig[] => {
|
|
268
275
|
const { User } = require('./user.model'); // Lazy require
|
|
269
276
|
return [{ schema: User.schema, ... }];
|
|
@@ -283,15 +290,15 @@ export class Order extends BaseEntity<typeof Order.schema> {
|
|
|
283
290
|
|
|
284
291
|
```typescript
|
|
285
292
|
// ❌ BAD - No error handling
|
|
286
|
-
const tx = await
|
|
287
|
-
await
|
|
293
|
+
const tx = await userRepository.beginTransaction();
|
|
294
|
+
await userRepository.create({ data, options: { transaction: tx } });
|
|
288
295
|
await tx.commit(); // If create fails, commit is never called but neither is rollback
|
|
289
296
|
|
|
290
297
|
// ✅ GOOD - Proper transaction handling
|
|
291
|
-
const tx = await
|
|
298
|
+
const tx = await userRepository.beginTransaction();
|
|
292
299
|
try {
|
|
293
|
-
await
|
|
294
|
-
await
|
|
300
|
+
await userRepository.create({ data, options: { transaction: tx } });
|
|
301
|
+
await orderRepository.updateById({ id, data: other, options: { transaction: tx } });
|
|
295
302
|
await tx.commit();
|
|
296
303
|
} catch (error) {
|
|
297
304
|
await tx.rollback();
|
|
@@ -342,40 +349,52 @@ async createOrder(c: Context) {
|
|
|
342
349
|
|
|
343
350
|
**Problem:** Relations return empty arrays or `null` unexpectedly.
|
|
344
351
|
|
|
345
|
-
**Cause:** Mismatch between `fields` and `references` in relation metadata.
|
|
352
|
+
**Cause:** Mismatch between `fields` and `references` in relation metadata (or defining them on the wrong side).
|
|
346
353
|
|
|
347
|
-
**Solution:**
|
|
354
|
+
**Solution:** `fields`/`references` belong on the `RelationTypes.ONE` side (the entity holding the foreign key). The `RelationTypes.MANY` side only takes a `relationName` pointing at the matching `one` relation:
|
|
348
355
|
|
|
349
356
|
```typescript
|
|
350
|
-
// ❌ BAD - fields and references swapped
|
|
357
|
+
// ❌ BAD - fields and references swapped on the ONE side (on Post)
|
|
351
358
|
static override relations = (): TRelationConfig[] => [
|
|
352
359
|
{
|
|
353
|
-
name: '
|
|
354
|
-
type: RelationTypes.
|
|
355
|
-
schema:
|
|
360
|
+
name: 'author',
|
|
361
|
+
type: RelationTypes.ONE,
|
|
362
|
+
schema: User.schema,
|
|
356
363
|
metadata: {
|
|
357
|
-
fields: [
|
|
358
|
-
references: [
|
|
364
|
+
fields: [User.schema.id], // Wrong! This should be Post.schema.authorId
|
|
365
|
+
references: [Post.schema.authorId], // Wrong! This should be User.schema.id
|
|
359
366
|
},
|
|
360
367
|
},
|
|
361
368
|
];
|
|
362
369
|
|
|
363
370
|
// ✅ GOOD - Correct configuration
|
|
364
|
-
// "
|
|
371
|
+
// On Post: "Post belongs to one User where Post.authorId = User.id"
|
|
372
|
+
static override relations = (): TRelationConfig[] => [
|
|
373
|
+
{
|
|
374
|
+
name: 'author',
|
|
375
|
+
type: RelationTypes.ONE,
|
|
376
|
+
schema: User.schema,
|
|
377
|
+
metadata: {
|
|
378
|
+
fields: [Post.schema.authorId], // Current entity's foreign key
|
|
379
|
+
references: [User.schema.id], // Related entity's key
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
];
|
|
383
|
+
|
|
384
|
+
// On User: "User has many Posts" - MANY side references the one relation by name
|
|
365
385
|
static override relations = (): TRelationConfig[] => [
|
|
366
386
|
{
|
|
367
387
|
name: 'posts',
|
|
368
388
|
type: RelationTypes.MANY,
|
|
369
389
|
schema: Post.schema,
|
|
370
390
|
metadata: {
|
|
371
|
-
|
|
372
|
-
references: [Post.schema.authorId], // Child's foreign key
|
|
391
|
+
relationName: 'author', // Points to the 'one' relation name on Post
|
|
373
392
|
},
|
|
374
393
|
},
|
|
375
394
|
];
|
|
376
395
|
```
|
|
377
396
|
|
|
378
|
-
**Rule of thumb:** `fields` is the key on the current entity, `references` is the key on the related entity.
|
|
397
|
+
**Rule of thumb:** on the `ONE` side, `fields` is the foreign key on the current entity, `references` is the key on the related entity. The `MANY` side only names its inverse relation.
|
|
379
398
|
|
|
380
399
|
## 13. Overwriting Data with Partial Updates
|
|
381
400
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Contribution Workflow
|
|
2
2
|
|
|
3
|
-
Guidelines for contributing to
|
|
3
|
+
Guidelines for contributing to IGNIS - help us maintain quality and streamline the process.
|
|
4
4
|
|
|
5
5
|
## Git Branching Strategy
|
|
6
6
|
|
|
@@ -42,7 +42,7 @@ git remote add upstream https://github.com/VENIZIA-AI/ignis.git
|
|
|
42
42
|
|
|
43
43
|
## Package Build Order
|
|
44
44
|
|
|
45
|
-
|
|
45
|
+
IGNIS is a monorepo with interdependent packages. Understanding the dependency chain is critical for development:
|
|
46
46
|
|
|
47
47
|
```
|
|
48
48
|
dev-configs → inversion → helpers → boot → core
|
|
@@ -97,7 +97,7 @@ make update-boot
|
|
|
97
97
|
make update-helpers
|
|
98
98
|
make update-inversion
|
|
99
99
|
make update-dev-configs
|
|
100
|
-
make update-docs
|
|
100
|
+
make update-docs-mcp
|
|
101
101
|
```
|
|
102
102
|
|
|
103
103
|
## 2. Development Workflow
|
|
@@ -107,8 +107,8 @@ make update-docs
|
|
|
107
107
|
```bash
|
|
108
108
|
# Sync with upstream
|
|
109
109
|
git fetch upstream
|
|
110
|
-
git checkout
|
|
111
|
-
git merge upstream/
|
|
110
|
+
git checkout develop
|
|
111
|
+
git merge upstream/develop
|
|
112
112
|
|
|
113
113
|
# Create feature branch
|
|
114
114
|
git checkout -b feature/your-feature-name
|
|
@@ -128,7 +128,7 @@ git checkout -b feature/your-feature-name
|
|
|
128
128
|
- Follow [Code Style Standards](./code-style-standards/)
|
|
129
129
|
- Follow [Architectural Patterns](./architectural-patterns.md)
|
|
130
130
|
- Add tests for new features/fixes
|
|
131
|
-
- Update docs in `
|
|
131
|
+
- Update docs in `docs/wiki/content/` if needed
|
|
132
132
|
|
|
133
133
|
### Step 3: Commit
|
|
134
134
|
|
|
@@ -155,14 +155,13 @@ git commit -m "chore: upgrade Hono to v4.0"
|
|
|
155
155
|
```bash
|
|
156
156
|
# Lint and format (from root)
|
|
157
157
|
make lint
|
|
158
|
-
# Or
|
|
158
|
+
# Or run `bun run lint:fix` inside a package directory
|
|
159
159
|
|
|
160
|
-
# Build all packages
|
|
160
|
+
# Build all packages (from root)
|
|
161
161
|
make build
|
|
162
|
-
# Or: bun run build
|
|
163
162
|
|
|
164
|
-
# Run tests
|
|
165
|
-
bun
|
|
163
|
+
# Run tests (from a package directory, e.g. packages/core or packages/boot)
|
|
164
|
+
cd packages/core && bun test
|
|
166
165
|
```
|
|
167
166
|
|
|
168
167
|
## 3. Submit Pull Request
|
|
@@ -187,7 +186,7 @@ git push origin feature/your-feature-name
|
|
|
187
186
|
- Code is linted and formatted
|
|
188
187
|
- Documentation updated
|
|
189
188
|
- Commit messages follow conventions
|
|
190
|
-
- Branch is up-to-date with `
|
|
189
|
+
- Branch is up-to-date with `develop`
|
|
191
190
|
|
|
192
191
|
## 4. Review Process
|
|
193
192
|
|
|
@@ -195,7 +194,7 @@ git push origin feature/your-feature-name
|
|
|
195
194
|
1. Maintainer reviews your PR (usually within 2-3 days)
|
|
196
195
|
2. Feedback or change requests may be provided
|
|
197
196
|
3. Address feedback and push updates
|
|
198
|
-
4. Once approved, maintainer merges to `
|
|
197
|
+
4. Once approved, maintainer merges to `develop`
|
|
199
198
|
|
|
200
199
|
**Responding to feedback:**
|
|
201
200
|
```bash
|
|
@@ -205,4 +204,4 @@ git commit -m "fix: address review feedback"
|
|
|
205
204
|
git push origin feature/your-feature-name
|
|
206
205
|
```
|
|
207
206
|
|
|
208
|
-
**Thank you for contributing to
|
|
207
|
+
**Thank you for contributing to IGNIS!**
|