@venizia/ignis-docs 0.0.1-9 → 0.0.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 (34) hide show
  1. package/LICENSE.md +1 -0
  2. package/package.json +2 -2
  3. package/wiki/changelogs/{v0.0.1-7-initial-architecture.md → 2025-12-16-initial-architecture.md} +20 -12
  4. package/wiki/changelogs/2025-12-16-model-repo-datasource-refactor.md +300 -0
  5. package/wiki/changelogs/2025-12-17-refactor.md +80 -12
  6. package/wiki/changelogs/2025-12-18-performance-optimizations.md +28 -90
  7. package/wiki/changelogs/2025-12-18-repository-validation-security.md +101 -297
  8. package/wiki/changelogs/index.md +20 -8
  9. package/wiki/changelogs/planned-schema-migrator.md +561 -0
  10. package/wiki/changelogs/planned-transaction-support.md +216 -0
  11. package/wiki/changelogs/template.md +123 -0
  12. package/wiki/get-started/best-practices/api-usage-examples.md +0 -2
  13. package/wiki/get-started/best-practices/architectural-patterns.md +2 -2
  14. package/wiki/get-started/best-practices/code-style-standards.md +575 -10
  15. package/wiki/get-started/best-practices/common-pitfalls.md +5 -3
  16. package/wiki/get-started/best-practices/contribution-workflow.md +2 -0
  17. package/wiki/get-started/best-practices/data-modeling.md +91 -34
  18. package/wiki/get-started/best-practices/security-guidelines.md +3 -1
  19. package/wiki/get-started/building-a-crud-api.md +3 -3
  20. package/wiki/get-started/core-concepts/application.md +72 -3
  21. package/wiki/get-started/core-concepts/bootstrapping.md +566 -0
  22. package/wiki/get-started/core-concepts/components.md +4 -2
  23. package/wiki/get-started/core-concepts/persistent.md +350 -378
  24. package/wiki/get-started/core-concepts/services.md +21 -27
  25. package/wiki/references/base/bootstrapping.md +789 -0
  26. package/wiki/references/base/components.md +1 -1
  27. package/wiki/references/base/dependency-injection.md +95 -2
  28. package/wiki/references/base/services.md +2 -2
  29. package/wiki/references/components/authentication.md +4 -3
  30. package/wiki/references/components/index.md +1 -1
  31. package/wiki/references/helpers/error.md +2 -2
  32. package/wiki/references/src-details/boot.md +379 -0
  33. package/wiki/references/src-details/core.md +2 -2
  34. package/wiki/changelogs/v0.0.1-8-model-repo-datasource-refactor.md +0 -278
@@ -6,26 +6,24 @@ Ignis streamlines data modeling with Drizzle ORM by providing powerful helpers a
6
6
 
7
7
  All entity models should extend `BaseEntity`. This provides integration with the framework's repository layer and automatic schema generation support.
8
8
 
9
- **Example (`src/models/entities/configuration.model.ts`):**
9
+ The recommended pattern is to define the schema and relations as **static properties** on the class. This keeps the definition self-contained and enables powerful type inference.
10
+
11
+ **Example (`src/models/entities/user.model.ts`):**
10
12
 
11
13
  ```typescript
12
- import {
13
- BaseEntity,
14
- model,
15
- TTableObject,
16
- } from '@venizia/ignis';
17
- import { configurationTable, configurationRelations } from './schema'; // Your Drizzle schema
14
+ import { BaseEntity, extraUserColumns, generateIdColumnDefs, model } from '@venizia/ignis';
15
+ import { pgTable } from 'drizzle-orm/pg-core';
18
16
 
19
- // Define types for TypeScript inference
20
- export type TConfigurationSchema = typeof configurationTable;
21
- export type TConfiguration = TTableObject<TConfigurationSchema>;
17
+ @model({ type: 'entity' })
18
+ export class User extends BaseEntity<typeof User.schema> {
19
+ // 1. Define schema as a static property
20
+ static override schema = pgTable('User', {
21
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
22
+ ...extraUserColumns({ idType: 'string' }),
23
+ });
22
24
 
23
- @model({ type: 'entity', skipMigrate: false })
24
- export class Configuration extends BaseEntity<typeof Configuration.schema> {
25
- // Use static properties (recommended pattern)
26
- static override schema = configurationTable;
27
- static override relations = () => configurationRelations.definitions;
28
- static override TABLE_NAME = 'Configuration';
25
+ // 2. Define relations as a static method (return empty array if none)
26
+ static override relations = () => [];
29
27
  }
30
28
  ```
31
29
 
@@ -41,7 +39,7 @@ Instead of manually defining common columns like primary keys, timestamps, or au
41
39
  | `generateTzColumnDefs` | Adds timestamps | `createdAt`, `modifiedAt` (auto-updating) |
42
40
  | `generateUserAuditColumnDefs` | Adds audit fields | `createdBy`, `modifiedBy` |
43
41
  | `generateDataTypeColumnDefs` | Adds generic value fields | `nValue` (number), `tValue` (text), `jValue` (json), etc. |
44
- | `generatePrincipalColumnDefs` | Adds polymorphic relation fields | `principalType`, `principalId` |
42
+ | `extraUserColumns` | Comprehensive user fields | Combines audit, timestamps, status, and type fields |
45
43
 
46
44
  **Usage Example:**
47
45
 
@@ -82,39 +80,98 @@ export const configurationTable = pgTable(
82
80
 
83
81
  ## 3. Defining Relations
84
82
 
85
- Use the `createRelations` helper to define relationships cleanly. This abstracts the Drizzle `relations` function and makes it easy to bind to repositories.
83
+ Relations are defined using the `TRelationConfig` structure within the static `relations` method of your model.
86
84
 
87
- **Example:**
85
+ **Example (`src/models/entities/configuration.model.ts`):**
88
86
 
89
87
  ```typescript
90
- import { createRelations, RelationTypes } from '@venizia/ignis';
91
- import { userTable } from './user.model';
88
+ import {
89
+ BaseEntity,
90
+ model,
91
+ RelationTypes,
92
+ TRelationConfig,
93
+ } from '@venizia/ignis';
94
+ import { User } from './user.model';
95
+
96
+ @model({ type: 'entity' })
97
+ export class Configuration extends BaseEntity<typeof Configuration.schema> {
98
+ // ... schema definition ...
92
99
 
93
- export const configurationRelations = createRelations({
94
- source: configurationTable,
95
- relations: [
100
+ // Define relations
101
+ static override relations = (): TRelationConfig[] => [
96
102
  {
97
103
  name: 'creator',
98
104
  type: RelationTypes.ONE,
99
- schema: userTable,
105
+ schema: User.schema,
100
106
  metadata: {
101
- fields: [configurationTable.createdBy],
102
- references: [userTable.id],
107
+ fields: [Configuration.schema.createdBy],
108
+ references: [User.schema.id],
103
109
  },
104
110
  },
105
- ],
106
- });
111
+ ];
112
+ }
107
113
  ```
108
114
 
109
- This configuration is automatically used when you define your Repository with the `@repository` decorator:
115
+ ## 4. Repositories and Auto-Discovery
116
+
117
+ Ignis simplifies the connection between models, repositories, and datasources.
118
+
119
+ ### DataSource Auto-Discovery
120
+
121
+ DataSources automatically discover their schema from the repositories that bind to them. You **do not** need to manually register schemas in the DataSource constructor.
110
122
 
111
123
  ```typescript
112
- import { PostgresDataSource } from '@/datasources';
124
+ // src/datasources/postgres.datasource.ts
125
+ @datasource({ driver: 'node-postgres' })
126
+ export class PostgresDataSource extends BaseDataSource<TNodePostgresConnector, IDSConfigs> {
127
+ constructor() {
128
+ super({
129
+ name: PostgresDataSource.name,
130
+ config: { /* connection config */ },
131
+ // NO schema property needed - auto-discovered!
132
+ });
133
+ }
134
+
135
+ override configure(): ValueOrPromise<void> {
136
+ // This method automatically collects all schemas from bound repositories
137
+ const schema = this.getSchema();
138
+ this.connector = drizzle({ client: new Pool(this.settings), schema });
139
+ }
140
+ }
141
+ ```
142
+
143
+ ### Repository Binding
144
+
145
+ Repositories use the `@repository` decorator to bind a **Model** to a **DataSource**. This binding is what powers the auto-discovery mechanism.
146
+
147
+ **Pattern 1: Zero Boilerplate (Recommended)**
113
148
 
114
- // Both 'model' and 'dataSource' are required for schema auto-discovery
149
+ For most repositories, you don't need a constructor. The DataSource is automatically injected.
150
+
151
+ ```typescript
115
152
  @repository({ model: Configuration, dataSource: PostgresDataSource })
116
153
  export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {
117
- // No constructor needed! DataSource and relations are auto-resolved
118
- // from the @repository decorator and entity's static properties
154
+ // No constructor needed!
119
155
  }
120
156
  ```
157
+
158
+ **Pattern 2: Explicit Injection (Advanced)**
159
+
160
+ If you need to perform custom initialization or inject additional dependencies, you can define a constructor. **Important:** The first parameter must be the DataSource.
161
+
162
+ ```typescript
163
+ @repository({ model: User, dataSource: PostgresDataSource })
164
+ export class UserRepository extends ReadableRepository<typeof User.schema> {
165
+ constructor(
166
+ @inject({ key: 'datasources.PostgresDataSource' })
167
+ dataSource: PostgresDataSource,
168
+ ) {
169
+ super(dataSource);
170
+ }
171
+
172
+ // Custom methods
173
+ async findByRealm(realm: string) {
174
+ return this.findOne({ filter: { where: { realm } } });
175
+ }
176
+ }
177
+ ```
@@ -72,9 +72,11 @@ const SecureRoute = {
72
72
 
73
73
  **Access user in protected routes:**
74
74
  ```typescript
75
+ import { Authentication, IJWTTokenPayload, ApplicationError, getError } from '@venizia/ignis';
76
+
75
77
  const user = c.get(Authentication.CURRENT_USER) as IJWTTokenPayload;
76
78
  if (!user.roles.includes('admin')) {
77
- throw new ApplicationError({ statusCode: 403, message: 'Forbidden' });
79
+ throw getError({ statusCode: 403, message: 'Forbidden' });
78
80
  }
79
81
  ```
80
82
 
@@ -645,7 +645,7 @@ For complex validation or business rules, create a Service layer:
645
645
 
646
646
  ```typescript
647
647
  // src/services/todo.service.ts
648
- import { BaseService, inject } from '@venizia/ignis';
648
+ import { BaseService, inject, getError } from '@venizia/ignis';
649
649
  import { TodoRepository } from '@/repositories/todo.repository';
650
650
 
651
651
  export class TodoService extends BaseService {
@@ -659,7 +659,7 @@ export class TodoService extends BaseService {
659
659
  async createTodo(data: any) {
660
660
  // Business logic validation
661
661
  if (data.title.length < 3) {
662
- throw new Error('Title too short');
662
+ throw getError({ message: 'Title too short' });
663
663
  }
664
664
 
665
665
  // Check for duplicates
@@ -667,7 +667,7 @@ export class TodoService extends BaseService {
667
667
  filter: { where: { title: data.title } },
668
668
  });
669
669
  if (existing) {
670
- throw new Error('Todo already exists');
670
+ throw getError({ message: 'Todo already exists' });
671
671
  }
672
672
 
673
673
  return this.todoRepository.create({ data });
@@ -42,10 +42,13 @@ export class Application extends BaseApplication {
42
42
  }
43
43
 
44
44
  preConfigure(): ValueOrPromise<void> {
45
- // Register all your resources here
45
+ // Manual registration (traditional approach)
46
46
  this.dataSource(MyDataSource);
47
47
  this.service(MyService);
48
48
  this.controller(MyController);
49
+
50
+ // Or use boot system for auto-discovery (recommended for larger apps)
51
+ // See Bootstrapping section below
49
52
  }
50
53
 
51
54
  postConfigure(): ValueOrPromise<void> {
@@ -75,10 +78,74 @@ The `BaseApplication` class provides several **overridable hook methods** that a
75
78
  | :--- | :--- |
76
79
  | `getAppInfo()` | **Required.** Return application metadata, usually from `package.json`. Used for OpenAPI docs. |
77
80
  | `staticConfigure()` | Configure static file serving. |
78
- | `preConfigure()` | **Most Important Hook.** Set up application resources like components, controllers, services, and datasources. |
81
+ | `preConfigure()` | **Most Important Hook.** Set up application resources like components, controllers, services, and datasources. Can be skipped if using boot system. |
79
82
  | `postConfigure()` | Perform actions *after* all resources have been configured and instantiated. |
80
83
  | `setupMiddlewares()`| Add custom application-level middlewares to the Hono instance. |
81
84
 
85
+ ## Bootstrapping (Auto-discovery)
86
+
87
+ The boot system provides automatic artifact discovery and loading, eliminating manual registration. When enabled, it scans your project directory and automatically loads controllers, services, repositories, and datasources.
88
+
89
+ > **Detailed Guide:** See [Bootstrapping Concepts](./bootstrapping.md) for complete documentation.
90
+
91
+ ### Enabling Boot System
92
+
93
+ Add `bootOptions` to your application config:
94
+
95
+ ```typescript
96
+ export const appConfigs: IApplicationConfigs = {
97
+ host: process.env.APP_ENV_SERVER_HOST,
98
+ port: +(process.env.APP_ENV_SERVER_PORT ?? 3000),
99
+ // Enable boot system
100
+ bootOptions: {
101
+ datasources: { dirs: ['datasources'] },
102
+ repositories: { dirs: ['repositories'] },
103
+ services: { dirs: ['services'] },
104
+ controllers: { dirs: ['controllers'] }
105
+ }
106
+ };
107
+ ```
108
+
109
+ With boot enabled, you can skip manual registration in `preConfigure()`:
110
+
111
+ ```typescript
112
+ export class Application extends BaseApplication {
113
+ // No need to register artifacts manually!
114
+ // Boot system handles it automatically
115
+
116
+ preConfigure(): ValueOrPromise<void> {
117
+ // Only register things that need custom configuration
118
+ // Everything else is auto-discovered
119
+ }
120
+ }
121
+ ```
122
+
123
+ ### Boot vs Manual Registration
124
+
125
+ | Approach | Use Case | Pros | Cons |
126
+ |----------|----------|------|------|
127
+ | **Boot System** | Apps with 10+ artifacts per type | Auto-discovery, scalable, clean code | Requires file naming conventions |
128
+ | **Manual Registration** | Small apps (< 5 artifacts) | Fine-grained control, explicit | Tedious, maintenance burden |
129
+
130
+ ### Project Structure for Boot
131
+
132
+ Follow naming conventions for auto-discovery:
133
+
134
+ ```
135
+ src/
136
+ ├── datasources/
137
+ │ └── postgres.datasource.js
138
+ ├── repositories/
139
+ │ ├── user.repository.js
140
+ │ └── product.repository.js
141
+ ├── services/
142
+ │ ├── auth.service.js
143
+ │ └── user.service.js
144
+ └── controllers/
145
+ ├── auth.controller.js
146
+ └── user.controller.js
147
+ ```
148
+
82
149
  ## Lifecycle Diagram
83
150
 
84
151
  This diagram shows the sequence of operations during application startup. The methods you can override are highlighted.
@@ -90,7 +157,8 @@ graph TD
90
157
 
91
158
  subgraph "initialize() Sequence"
92
159
  direction TB
93
- B --> B1["printStartUpInfo"];
160
+ B --> B0["boot() - if bootOptions configured"];
161
+ B0 --> B1["printStartUpInfo"];
94
162
  B1 --> B2["validateEnvs"];
95
163
  B2 --> B3["registerDefaultMiddlewares"];
96
164
  B3 --> B4["staticConfigure()"];
@@ -129,6 +197,7 @@ Application configuration is passed to the `BaseApplication` constructor via an
129
197
  | `path.isStrict`| `boolean`| `true` | If `true`, the router is strict about trailing slashes. |
130
198
  | `debug.showRoutes`| `boolean`| `false`| If `true`, prints all registered routes to the console on startup. |
131
199
  | `favicon` | `string` | `'🔥'` | An emoji to be used as the application's favicon. |
200
+ | `bootOptions` | `IBootOptions` | `undefined` | Enable auto-discovery of artifacts. See [Bootstrapping](./bootstrapping.md). |
132
201
 
133
202
  ### Example Configuration
134
203