@venizia/ignis-docs 0.0.1-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/mcp-server/dist/common/config.d.ts +27 -0
  2. package/mcp-server/dist/common/config.d.ts.map +1 -0
  3. package/mcp-server/dist/common/config.js +27 -0
  4. package/mcp-server/dist/common/config.js.map +1 -0
  5. package/mcp-server/dist/common/index.d.ts +3 -0
  6. package/mcp-server/dist/common/index.d.ts.map +1 -0
  7. package/mcp-server/dist/common/index.js +19 -0
  8. package/mcp-server/dist/common/index.js.map +1 -0
  9. package/mcp-server/dist/common/paths.d.ts +13 -0
  10. package/mcp-server/dist/common/paths.d.ts.map +1 -0
  11. package/mcp-server/dist/common/paths.js +23 -0
  12. package/mcp-server/dist/common/paths.js.map +1 -0
  13. package/mcp-server/dist/helpers/docs.helper.d.ts +81 -0
  14. package/mcp-server/dist/helpers/docs.helper.d.ts.map +1 -0
  15. package/mcp-server/dist/helpers/docs.helper.js +171 -0
  16. package/mcp-server/dist/helpers/docs.helper.js.map +1 -0
  17. package/mcp-server/dist/helpers/index.d.ts +3 -0
  18. package/mcp-server/dist/helpers/index.d.ts.map +1 -0
  19. package/mcp-server/dist/helpers/index.js +19 -0
  20. package/mcp-server/dist/helpers/index.js.map +1 -0
  21. package/mcp-server/dist/helpers/logger.helper.d.ts +7 -0
  22. package/mcp-server/dist/helpers/logger.helper.d.ts.map +1 -0
  23. package/mcp-server/dist/helpers/logger.helper.js +22 -0
  24. package/mcp-server/dist/helpers/logger.helper.js.map +1 -0
  25. package/mcp-server/dist/index.d.ts +3 -0
  26. package/mcp-server/dist/index.d.ts.map +1 -0
  27. package/mcp-server/dist/index.js +62 -0
  28. package/mcp-server/dist/index.js.map +1 -0
  29. package/mcp-server/dist/tools/base.tool.d.ts +98 -0
  30. package/mcp-server/dist/tools/base.tool.d.ts.map +1 -0
  31. package/mcp-server/dist/tools/base.tool.js +47 -0
  32. package/mcp-server/dist/tools/base.tool.js.map +1 -0
  33. package/mcp-server/dist/tools/get-doc-content.tool.d.ts +30 -0
  34. package/mcp-server/dist/tools/get-doc-content.tool.d.ts.map +1 -0
  35. package/mcp-server/dist/tools/get-doc-content.tool.js +127 -0
  36. package/mcp-server/dist/tools/get-doc-content.tool.js.map +1 -0
  37. package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts +40 -0
  38. package/mcp-server/dist/tools/get-doc-metadata.tool.d.ts.map +1 -0
  39. package/mcp-server/dist/tools/get-doc-metadata.tool.js +121 -0
  40. package/mcp-server/dist/tools/get-doc-metadata.tool.js.map +1 -0
  41. package/mcp-server/dist/tools/index.d.ts +8 -0
  42. package/mcp-server/dist/tools/index.d.ts.map +1 -0
  43. package/mcp-server/dist/tools/index.js +18 -0
  44. package/mcp-server/dist/tools/index.js.map +1 -0
  45. package/mcp-server/dist/tools/list-categories.tool.d.ts +20 -0
  46. package/mcp-server/dist/tools/list-categories.tool.d.ts.map +1 -0
  47. package/mcp-server/dist/tools/list-categories.tool.js +105 -0
  48. package/mcp-server/dist/tools/list-categories.tool.js.map +1 -0
  49. package/mcp-server/dist/tools/list-docs.tool.d.ts +32 -0
  50. package/mcp-server/dist/tools/list-docs.tool.d.ts.map +1 -0
  51. package/mcp-server/dist/tools/list-docs.tool.js +121 -0
  52. package/mcp-server/dist/tools/list-docs.tool.js.map +1 -0
  53. package/mcp-server/dist/tools/search-docs.tool.d.ts +32 -0
  54. package/mcp-server/dist/tools/search-docs.tool.d.ts.map +1 -0
  55. package/mcp-server/dist/tools/search-docs.tool.js +120 -0
  56. package/mcp-server/dist/tools/search-docs.tool.js.map +1 -0
  57. package/package.json +102 -0
  58. package/wiki/get-started/5-minute-quickstart.md +266 -0
  59. package/wiki/get-started/best-practices/api-usage-examples.md +222 -0
  60. package/wiki/get-started/best-practices/architectural-patterns.md +129 -0
  61. package/wiki/get-started/best-practices/code-style-standards.md +122 -0
  62. package/wiki/get-started/best-practices/common-pitfalls.md +136 -0
  63. package/wiki/get-started/best-practices/contribution-workflow.md +145 -0
  64. package/wiki/get-started/best-practices/deployment-strategies.md +121 -0
  65. package/wiki/get-started/best-practices/performance-optimization.md +88 -0
  66. package/wiki/get-started/best-practices/security-guidelines.md +97 -0
  67. package/wiki/get-started/best-practices/troubleshooting-tips.md +100 -0
  68. package/wiki/get-started/building-a-crud-api.md +717 -0
  69. package/wiki/get-started/core-concepts/application.md +168 -0
  70. package/wiki/get-started/core-concepts/components.md +96 -0
  71. package/wiki/get-started/core-concepts/controllers.md +441 -0
  72. package/wiki/get-started/core-concepts/dependency-injection.md +160 -0
  73. package/wiki/get-started/core-concepts/persistent.md +591 -0
  74. package/wiki/get-started/core-concepts/services.md +88 -0
  75. package/wiki/get-started/index.md +65 -0
  76. package/wiki/get-started/mcp-docs-server.md +840 -0
  77. package/wiki/get-started/philosophy.md +123 -0
  78. package/wiki/get-started/prerequisites.md +113 -0
  79. package/wiki/get-started/quickstart.md +382 -0
  80. package/wiki/index.md +48 -0
  81. package/wiki/references/base/application.md +67 -0
  82. package/wiki/references/base/components.md +80 -0
  83. package/wiki/references/base/controllers.md +361 -0
  84. package/wiki/references/base/datasources.md +105 -0
  85. package/wiki/references/base/dependency-injection.md +83 -0
  86. package/wiki/references/base/models.md +104 -0
  87. package/wiki/references/base/repositories.md +118 -0
  88. package/wiki/references/base/services.md +97 -0
  89. package/wiki/references/components/authentication.md +224 -0
  90. package/wiki/references/components/health-check.md +190 -0
  91. package/wiki/references/components/index.md +61 -0
  92. package/wiki/references/components/request-tracker.md +35 -0
  93. package/wiki/references/components/socket-io.md +127 -0
  94. package/wiki/references/components/swagger.md +175 -0
  95. package/wiki/references/helpers/cron.md +94 -0
  96. package/wiki/references/helpers/crypto.md +117 -0
  97. package/wiki/references/helpers/env.md +67 -0
  98. package/wiki/references/helpers/error.md +80 -0
  99. package/wiki/references/helpers/index.md +21 -0
  100. package/wiki/references/helpers/inversion.md +141 -0
  101. package/wiki/references/helpers/logger.md +98 -0
  102. package/wiki/references/helpers/network.md +143 -0
  103. package/wiki/references/helpers/queue.md +131 -0
  104. package/wiki/references/helpers/redis.md +121 -0
  105. package/wiki/references/helpers/socket-io.md +103 -0
  106. package/wiki/references/helpers/storage.md +130 -0
  107. package/wiki/references/helpers/testing.md +115 -0
  108. package/wiki/references/helpers/worker-thread.md +162 -0
  109. package/wiki/references/src-details/core.md +249 -0
  110. package/wiki/references/src-details/dev-configs.md +302 -0
  111. package/wiki/references/src-details/docs.md +61 -0
  112. package/wiki/references/src-details/helpers.md +211 -0
  113. package/wiki/references/src-details/inversion.md +345 -0
  114. package/wiki/references/src-details/mcp-server.md +726 -0
  115. package/wiki/references/utilities/crypto.md +39 -0
  116. package/wiki/references/utilities/date.md +72 -0
  117. package/wiki/references/utilities/index.md +12 -0
  118. package/wiki/references/utilities/module.md +40 -0
  119. package/wiki/references/utilities/parse.md +68 -0
  120. package/wiki/references/utilities/performance.md +64 -0
  121. package/wiki/references/utilities/promise.md +83 -0
  122. package/wiki/references/utilities/request.md +66 -0
  123. package/wiki/references/utilities/schema.md +88 -0
@@ -0,0 +1,83 @@
1
+ # Deep Dive: Dependency Injection
2
+
3
+ Technical reference for the DI system in Ignis - managing resource lifecycles and dependency resolution.
4
+
5
+ **Files:**
6
+ - `packages/core/src/helpers/inversion/container.ts`
7
+ - `packages/core/src/base/metadata/injectors.ts`
8
+ - `packages/helpers/src/helpers/inversion/registry.ts`
9
+
10
+ ## Quick Reference
11
+
12
+ | Component | Purpose | Key Methods |
13
+ |-----------|---------|-------------|
14
+ | **Container** | DI registry managing resource lifecycles | `bind()`, `get()`, `instantiate()`, `findByTag()` |
15
+ | **Binding** | Single registered dependency configuration | `toClass()`, `toValue()`, `toProvider()`, `setScope()` |
16
+ | **@inject** | Decorator marking injection points | Applied to constructor parameters/properties |
17
+ | **MetadataRegistry** | Stores decorator metadata | Singleton accessed via `getInstance()` |
18
+
19
+ ## `Container` Class
20
+
21
+ Heart of the DI system - registry managing all application resources.
22
+
23
+ **File:** `packages/core/src/helpers/inversion/container.ts`
24
+
25
+ ### Key Methods
26
+
27
+ | Method | Description |
28
+ | :--- | :--- |
29
+ | **`bind<T>({ key })`** | Starts a new binding for a given key. It returns a `Binding` instance that you can use to configure the dependency. |
30
+ | **`get<T>({ key, isOptional })`** | Retrieves a dependency from the container. The `key` can be a string, a symbol, or an object like `{ namespace: 'services', key: 'MyService' }`. If the dependency is not found and `isOptional` is `false` (the default), it will throw an error. |
31
+ | **`instantiate<T>(cls)`** | Creates a new instance of a class, automatically injecting any dependencies specified in its constructor or on its properties. This is the method the container uses internally to create your controllers, services, etc. |
32
+ | **`findByTag({ tag })`** | Finds all bindings that have been tagged with a specific tag (e.g., `'controllers'`, `'components'`). This is used by the application to discover and initialize all registered resources of a certain type. |
33
+
34
+ ## `Binding` Class
35
+
36
+ A `Binding` represents a single registered dependency in the container. It's a fluent API that allows you to specify *how* a dependency should be created and managed.
37
+
38
+ - **File:** `packages/core/src/helpers/inversion/container.ts`
39
+
40
+ ### Configuration Methods
41
+
42
+ | Method | Description |
43
+ | :--- | :--- |
44
+ | **`toClass(MyClass)`** | Binds the key to a class. The container will instantiate this class (and resolve its dependencies) when the key is requested. |
45
+ | **`toValue(someValue)`** | Binds the key to a constant value (e.g., a configuration object, a string, a number). |
46
+ | **`toProvider(MyProvider)`**| Binds the key to a provider class or function. This is for dependencies that require complex creation logic. |
47
+ | **`setScope(scope)`** | Sets the lifecycle scope of the binding. See "Binding Scopes" below. |
48
+
49
+ ### Binding Scopes
50
+
51
+ | Scope | Description |
52
+ | :--- | :--- |
53
+ | **`BindingScopes.TRANSIENT`** | (Default) A new instance of the dependency is created every time it is injected or requested from the container. |
54
+ | **`BindingScopes.SINGLETON`** | A single instance is created the first time it is requested, and that same instance is reused for all subsequent requests. DataSources and Components are typically singletons. |
55
+
56
+ ## `@inject` Decorator
57
+
58
+ The `@inject` decorator is used to mark where dependencies should be injected.
59
+
60
+ - **File:** `packages/core/src/base/metadata/injectors.ts`
61
+
62
+ ### How It Works
63
+
64
+ 1. When you apply the `@inject` decorator to a constructor parameter or a class property, it uses `Reflect.metadata` to attach metadata to the class.
65
+ 2. The metadata includes the **binding key** of the dependency to be injected.
66
+ 3. When the `container.instantiate(MyClass)` method is called, it reads this metadata.
67
+ 4. It then calls `container.get({ key })` for each decorated parameter/property to resolve the dependency.
68
+ 5. Finally, it creates the instance of `MyClass`, passing the resolved dependencies to the constructor or setting them on the instance properties.
69
+
70
+ This entire process is managed by the framework when your application starts up, ensuring that all your registered classes are created with their required dependencies.
71
+
72
+ ## `MetadataRegistry`
73
+
74
+ The `MetadataRegistry` is a crucial part of the DI and routing systems. It's a singleton class responsible for storing and retrieving all the metadata attached by decorators like `@inject`, `@controller`, `@get`, etc.
75
+
76
+ - **File:** `packages/helpers/src/helpers/inversion/registry.ts`
77
+
78
+ ### Role in DI
79
+
80
+ - When you use a decorator (e.g., `@inject`), it calls a method on the `MetadataRegistry.getInstance()` to store information about the injection (like the binding key and target property/parameter).
81
+ - When the `Container` instantiates a class, it queries the `MetadataRegistry` to find out which dependencies need to be injected and where.
82
+
83
+ You typically won't interact with the `MetadataRegistry` directly, but it's the underlying mechanism that makes the decorator-based DI and routing systems work seamlessly.
@@ -0,0 +1,104 @@
1
+ # Deep Dive: Models and Enrichers
2
+
3
+ Technical reference for model architecture and schema enrichers in Ignis.
4
+
5
+ **Files:**
6
+ - `packages/core/src/base/models/base.ts`
7
+ - `packages/core/src/base/models/enrichers/*.ts`
8
+
9
+ ## Quick Reference
10
+
11
+ | Component | Purpose | Key Features |
12
+ |-----------|---------|--------------|
13
+ | **BaseEntity** | Wraps Drizzle schema | Schema encapsulation, Zod generation, `toObject()`/`toJSON()` |
14
+ | **Schema Enrichers** | Add common columns to tables | `generateIdColumnDefs()`, `generateTzColumnDefs()`, etc. |
15
+
16
+ ## `BaseEntity` Class
17
+
18
+ Fundamental building block wrapping a Drizzle ORM schema.
19
+
20
+ **File:** `packages/core/src/base/models/base.ts`
21
+
22
+ ### Purpose
23
+
24
+ | Feature | Description |
25
+ |---------|-------------|
26
+ | **Schema Encapsulation** | Holds Drizzle `pgTable` schema for consistent repository access |
27
+ | **Metadata** | Works with `@model` decorator to mark database entities |
28
+ | **Schema Generation** | Uses `drizzle-zod` to generate Zod schemas (`SELECT`, `CREATE`, `UPDATE`) |
29
+ | **Convenience** | Includes `toObject()` and `toJSON()` methods |
30
+
31
+ ### Class Definition
32
+
33
+ ```typescript
34
+ import { createSchemaFactory } from 'drizzle-zod';
35
+ import { BaseHelper } from '../helpers';
36
+ import { SchemaTypes, TSchemaType, TTableSchemaWithId } from './common';
37
+
38
+ export class BaseEntity<Schema extends TTableSchemaWithId = TTableSchemaWithId> extends BaseHelper {
39
+ name: string;
40
+ schema: Schema;
41
+ schemaFactory: ReturnType<typeof createSchemaFactory>;
42
+
43
+ constructor(opts: { name: string; schema: Schema }) {
44
+ super({ scope: opts.name });
45
+ this.name = opts.name;
46
+ this.schema = opts.schema;
47
+ this.schemaFactory = createSchemaFactory();
48
+ }
49
+
50
+ getSchema(opts: { type: TSchemaType }) {
51
+ switch (opts.type) {
52
+ case SchemaTypes.CREATE: {
53
+ return this.schemaFactory.createInsertSchema(this.schema);
54
+ }
55
+ case SchemaTypes.UPDATE: {
56
+ return this.schemaFactory.createUpdateSchema(this.schema);
57
+ }
58
+ case SchemaTypes.SELECT: {
59
+ return this.schemaFactory.createSelectSchema(this.schema);
60
+ }
61
+ default: {
62
+ throw getError({
63
+ message: `[getSchema] Invalid schema type | type: ${opts.type} | valid: ${[SchemaTypes.SELECT, SchemaTypes.UPDATE, SchemaTypes.CREATE]}`,
64
+ });
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ When you define a model in your application, you extend `BaseEntity`, passing your Drizzle table schema to the `super` constructor.
72
+
73
+ ## Schema Enrichers
74
+
75
+ Enrichers are helper functions located in `packages/core/src/base/models/enrichers/` that return an object of Drizzle ORM column definitions. They are designed to be spread into a `pgTable` definition to quickly add common, standardized fields to your models.
76
+
77
+ ### Available Enrichers
78
+
79
+ | Enricher Function | Purpose |
80
+ | :--- | :--- |
81
+ | **`generateIdColumnDefs`** | Adds a primary key `id` column (string UUID or numeric serial). |
82
+ | **`generateTzColumnDefs`** | Adds `createdAt` and `modifiedAt` timestamp columns with timezone support. |
83
+ | **`generateUserAuditColumnDefs`** | Adds `createdBy` and `modifiedBy` columns to track user audit information. |
84
+ | **`generateDataTypeColumnDefs`** | Adds generic data type columns (`dataType`, `nValue`, `tValue`, `bValue`, `jValue`, `boValue`) for flexible data storage. |
85
+ | **`generatePrincipalColumnDefs`** | Adds polymorphic fields for associating with different principal types. |
86
+ | **`extraUserColumns`** (from `components/auth/models/entities/user.model.ts`) | Adds common fields for a user model, such as `realm`, `status`, `type`, `activatedAt`, `lastLoginAt`, and `parentId`. |
87
+
88
+ ### Example Usage
89
+
90
+ ```typescript
91
+ import { pgTable, text } from 'drizzle-orm/pg-core';
92
+ import {
93
+ generateIdColumnDefs,
94
+ generateTzColumnDefs,
95
+ generateUserAuditColumnDefs,
96
+ } from '@venizia/ignis';
97
+
98
+ export const myTable = pgTable('MyTable', {
99
+ ...generateIdColumnDefs({ id: { dataType: 'string' } }),
100
+ ...generateTzColumnDefs(),
101
+ ...generateUserAuditColumnDefs({ created: { dataType: 'string' }, modified: { dataType: 'string' } }),
102
+ name: text('name').notNull(),
103
+ });
104
+ ```
@@ -0,0 +1,118 @@
1
+ # Deep Dive: Repositories
2
+
3
+ Technical reference for repository classes - the data access layer in Ignis.
4
+
5
+ **Files:** `packages/core/src/base/repositories/core/*.ts`
6
+
7
+ ## Quick Reference
8
+
9
+ | Class | Capabilities | Use Case |
10
+ |-------|--------------|----------|
11
+ | **AbstractRepository** | Base class with properties | Extend for custom repositories |
12
+ | **ReadableRepository** | Read-only operations | Views, external tables |
13
+ | **PersistableRepository** | Read + Write operations | Rarely used directly |
14
+ | **DefaultCRUDRepository** | Full CRUD operations | Standard data tables ✅ |
15
+
16
+ ## `AbstractRepository`
17
+
18
+ Base class for all repositories - sets up fundamental properties and dependencies for data access.
19
+
20
+ **File:** `packages/core/src/base/repositories/core/base.ts`
21
+
22
+ ### Key Properties
23
+
24
+ - `entity` (`BaseEntity`): An instance of the model class associated with this repository. It provides access to the Drizzle schema.
25
+ - `dataSource` (`IDataSource`): The datasource instance injected into the repository, which holds the database connection.
26
+ - `connector`: A getter that provides direct access to the Drizzle ORM instance from the datasource.
27
+ - `filterBuilder` (`DrizzleFilterBuilder`): An instance of the filter builder responsible for converting `Ignis`'s filter objects into Drizzle-compatible query options.
28
+ - `relations` (`{ [relationName: string]: TRelationConfig }`): A map of relation configurations defined for the entity.
29
+
30
+ ### Abstract Methods
31
+
32
+ `AbstractRepository` defines the method signatures for standard CRUD operations that concrete repository classes must implement:
33
+ - `count()`
34
+ - `existsWith()`
35
+ - `find()`
36
+ - `findOne()`
37
+ - `findById()`
38
+ - `create()`
39
+ - `updateById()`
40
+ - `deleteById()`
41
+ - (and `...All` variants)
42
+
43
+ ## `ReadableRepository`
44
+
45
+ The `ReadableRepository` provides a **read-only** implementation of the repository pattern. It is ideal for data sources that should not be modified, such as views or tables from an external system.
46
+
47
+ - **File:** `packages/core/src/base/repositories/core/readable.ts`
48
+
49
+ ### Implemented Methods
50
+
51
+ `ReadableRepository` provides concrete implementations for all read operations:
52
+
53
+ - **`find(opts)`**: Returns an array of entities matching the filter.
54
+ - **`findOne(opts)`**: Returns the first entity matching the filter.
55
+ - **`findById(opts)`**: A convenience method that calls `findOne` with an ID-based `where` clause.
56
+ - **`count(opts)`**: Returns the number of entities matching the `where` clause.
57
+ - **`existsWith(opts)`**: Returns `true` if at least one entity matches the `where` clause.
58
+
59
+ ### Write Operations
60
+
61
+ `ReadableRepository` throws a "NOT ALLOWED" error for all write operations (`create`, `update`, `delete`).
62
+
63
+ ## `PersistableRepository`
64
+
65
+ The `PersistableRepository` extends `ReadableRepository` and adds **write operations**. It provides the core logic for creating, updating, and deleting records.
66
+
67
+ - **File:** `packages/core/src/base/repositories/core/persistable.ts`
68
+
69
+ ### Implemented Methods
70
+
71
+ - `create(opts)`
72
+ - `createAll(opts)`
73
+ - `updateById(opts)`
74
+ - `updateAll(opts)`
75
+ - `deleteById(opts)`
76
+ - `deleteAll(opts)`
77
+
78
+ You will typically not use this class directly, but rather the `DefaultCRUDRepository`.
79
+
80
+ ## `DefaultCRUDRepository`
81
+
82
+ This is the primary class you should extend for repositories that require full **Create, Read, Update, and Delete (CRUD)** capabilities. It extends `PersistableRepository` and serves as the standard, full-featured repository implementation.
83
+
84
+ - **File:** `packages/core/src/base/repositories/core/default-crud.ts`
85
+
86
+ ### Example Implementation
87
+
88
+ ```typescript
89
+ // src/repositories/configuration.repository.ts
90
+ import {
91
+ Configuration,
92
+ configurationRelations,
93
+ TConfigurationSchema,
94
+ } from '@/models/entities';
95
+ import { IDataSource, inject, repository, DefaultCRUDRepository } from '@venizia/ignis';
96
+
97
+ // Decorator to mark this class as a repository for DI
98
+ @repository({})
99
+ export class ConfigurationRepository extends DefaultCRUDRepository<TConfigurationSchema> {
100
+ constructor(
101
+ // Inject the configured datasource
102
+ @inject({ key: 'datasources.PostgresDataSource' }) dataSource: IDataSource,
103
+ ) {
104
+ // Pass the datasource, the model's Entity class, and the relations definitions to the super constructor
105
+ super({ dataSource, entityClass: Configuration, relations: configurationRelations.definitions });
106
+ }
107
+
108
+ // You can add custom data access methods here
109
+ async findByCode(code: string): Promise<Configuration | undefined> {
110
+ // 'this.connector' gives you direct access to the Drizzle instance
111
+ const result = await this.connector.query.Configuration.findFirst({
112
+ where: (table, { eq }) => eq(table.code, code)
113
+ });
114
+ return result;
115
+ }
116
+ }
117
+ ```
118
+ This architecture provides a clean and powerful abstraction for data access, separating the "how" of data fetching (Drizzle logic) from the "what" of business logic (services).
@@ -0,0 +1,97 @@
1
+ # Deep Dive: Services
2
+
3
+ Technical reference for `BaseService` - the foundation for business logic layers in Ignis.
4
+
5
+ **File:** `packages/core/src/base/services/base.ts`
6
+
7
+ ## Quick Reference
8
+
9
+ | Feature | Benefit |
10
+ |---------|---------|
11
+ | **Extends `BaseHelper`** | Auto-configured scoped logger (`this.logger`) |
12
+ | **DI Integration** | Fits into framework's dependency injection system |
13
+ | **Business Logic Layer** | Bridge between Controllers and Repositories |
14
+
15
+ ## `BaseService` Class
16
+
17
+ Abstract class that all application services should extend.
18
+
19
+ ### Key Features
20
+
21
+ | Feature | Description |
22
+ | :--- | :--- |
23
+ | **Standardization** | Common base for all services, fits framework architecture |
24
+ | **Logging** | Extends `BaseHelper` - auto-configured logger at `this.logger` (scope = class name) |
25
+ | **Clarity** | Signals the class contains business logic |
26
+
27
+ ### Class Definition
28
+
29
+ The implementation is straightforward:
30
+
31
+ ```typescript
32
+ import { BaseHelper } from '../helpers';
33
+ import { IService } from './types';
34
+
35
+ export abstract class BaseService extends BaseHelper implements IService {
36
+ constructor(opts: { scope: string }) {
37
+ super({ scope: opts.scope });
38
+ }
39
+ }
40
+ ```
41
+
42
+ ## How Services Fit into the Architecture
43
+
44
+ Services are the core of your application's logic. They act as a bridge between the presentation layer (Controllers) and the data access layer (Repositories).
45
+
46
+ ### Typical Service Flow
47
+
48
+ 1. **Instantiated by DI Container**: When the application starts, the DI container creates instances of your services.
49
+ 2. **Dependencies Injected**: The service's constructor receives instances of any repositories or other services it depends on.
50
+ 3. **Called by a Controller**: An HTTP request comes into a controller, which then calls a method on a service to handle the business logic for that request.
51
+ 4. **Orchestrates Logic**: The service method executes the business logic. This may involve:
52
+ - Validating input data.
53
+ - Calling one or more repository methods to fetch or save data.
54
+ - Calling other services to perform related tasks.
55
+ - Performing calculations or data transformations.
56
+ 5. **Returns Data**: The service returns the result of the operation back to the controller, which then formats it into an HTTP response.
57
+
58
+ ### Example
59
+
60
+ ```typescript
61
+ import { BaseService, inject } from '@venizia/ignis';
62
+ import { UserRepository } from '../repositories/user.repository';
63
+ import { TUser } from '../models/entities';
64
+
65
+ // 1. Service is decorated with `@injectable` (or registered via `app.service()`)
66
+ @injectable()
67
+ export class UserService extends BaseService {
68
+ // 2. Dependencies (like UserRepository) are injected
69
+ constructor(
70
+ @inject({ key: 'repositories.UserRepository' })
71
+ private userRepository: UserRepository,
72
+ ) {
73
+ super({ scope: UserService.name });
74
+ }
75
+
76
+ // 3. Method is called by a controller
77
+ async getUserProfile(userId: string): Promise<Partial<TUser>> {
78
+ this.logger.info(`Fetching profile for user ${userId}`);
79
+
80
+ // 4. Orchestrates logic: calls the repository
81
+ const user = await this.userRepository.findById({ id: userId });
82
+
83
+ if (!user) {
84
+ throw new Error('User not found');
85
+ }
86
+
87
+ // 5. Returns transformed data
88
+ return {
89
+ id: user.id,
90
+ name: user.name, // Assuming a 'name' field exists
91
+ email: user.email,
92
+ };
93
+ }
94
+ }
95
+ ```
96
+
97
+ By adhering to this pattern, you keep your code organized, testable, and maintainable. You can easily test `UserService` by providing a mock `UserRepository` without needing a real database connection.
@@ -0,0 +1,224 @@
1
+ # Authentication Component
2
+
3
+ JWT-based authentication and authorization system for Ignis applications.
4
+
5
+ ## Quick Reference
6
+
7
+ | Component | Purpose |
8
+ |-----------|---------|
9
+ | **AuthenticateComponent** | Main component registering auth services and controllers |
10
+ | **AuthenticationStrategyRegistry** | Singleton managing available auth strategies |
11
+ | **JWTAuthenticationStrategy** | JWT verification using `JWTTokenService` |
12
+ | **JWTTokenService** | Generate, verify, encrypt/decrypt JWT tokens |
13
+ | **IAuthService** | Interface for custom auth implementation (sign-in, sign-up) |
14
+
15
+ ### Key Environment Variables
16
+
17
+ | Variable | Purpose | Required |
18
+ |----------|---------|----------|
19
+ | `APP_ENV_APPLICATION_SECRET` | Encrypt JWT payload | ✅ Yes |
20
+ | `APP_ENV_JWT_SECRET` | Sign and verify JWT signature | ✅ Yes |
21
+ | `APP_ENV_JWT_EXPIRES_IN` | Token expiration (seconds) | Optional |
22
+
23
+ ## Architecture Components
24
+
25
+ - **`AuthenticateComponent`**: Registers all necessary services and controllers
26
+ - **`AuthenticationStrategyRegistry`**: Singleton managing authentication strategies
27
+ - **`JWTAuthenticationStrategy`**: JWT strategy implementation using `JWTTokenService`
28
+ - **`JWTTokenService`**: Generates, verifies, encrypts/decrypts JWT payloads
29
+ - **Protected Routes**: Use `authStrategies` in route configs to secure endpoints
30
+
31
+ ## Implementation Details
32
+
33
+ ### Tech Stack
34
+
35
+ - **Hono**
36
+ - **`jose`:** For JWT signing, verification, and encryption.
37
+ - **`@venizia/ignis`**: The core framework.
38
+
39
+ ### Configuration
40
+
41
+ Configure the authentication feature using environment variables:
42
+
43
+ - `APP_ENV_APPLICATION_SECRET`: A secret for encrypting the JWT payload.
44
+ - `APP_ENV_JWT_SECRET`: The secret for signing and verifying the JWT signature.
45
+ - `APP_ENV_JWT_EXPIRES_IN`: The JWT expiration time in seconds.
46
+
47
+ ::: danger SECURITY NOTE
48
+ Both `APP_ENV_APPLICATION_SECRET` and `APP_ENV_JWT_SECRET` are **mandatory**. For security purposes, you must set these to strong, unique secret values. The application will fail to start if these environment variables are missing or left empty.
49
+ :::
50
+
51
+ **Example `.env` file:**
52
+
53
+ ```
54
+ APP_ENV_APPLICATION_SECRET=your-strong-application-secret
55
+ APP_ENV_JWT_SECRET=your-strong-jwt-secret
56
+ APP_ENV_JWT_EXPIRES_IN=86400
57
+ ```
58
+
59
+ ### Code Samples
60
+
61
+ #### 1. Registering the Authentication Component
62
+
63
+ In `src/application.ts`, register the `AuthenticateComponent` and the `JWTAuthenticationStrategy`. You also need to provide an `AuthenticationService`.
64
+
65
+ ```typescript
66
+ // src/application.ts
67
+ import {
68
+ AuthenticateComponent,
69
+ Authentication,
70
+ AuthenticationStrategyRegistry,
71
+ JWTAuthenticationStrategy,
72
+ BaseApplication,
73
+ ValueOrPromise,
74
+ } from '@venizia/ignis';
75
+ import { AuthenticationService } from './services'; // Your custom auth service
76
+
77
+ export class Application extends BaseApplication {
78
+ // ...
79
+
80
+ registerAuth() {
81
+ this.service(AuthenticationService);
82
+ this.component(AuthenticateComponent);
83
+ AuthenticationStrategyRegistry.getInstance().register({
84
+ container: this,
85
+ name: Authentication.STRATEGY_JWT,
86
+ strategy: JWTAuthenticationStrategy,
87
+ });
88
+ }
89
+
90
+ preConfigure(): ValueOrPromise<void> {
91
+ // ...
92
+ this.registerAuth();
93
+ // ...
94
+ }
95
+ }
96
+ ```
97
+
98
+ #### 2. Implementing an AuthenticationService
99
+
100
+ The `AuthenticateComponent` depends on a service that implements the `IAuthService` interface. You need to provide your own implementation for this service, which will contain your application's specific logic for user authentication.
101
+
102
+ Here is a minimal example of what this service might look like:
103
+
104
+ ```typescript
105
+ // src/services/authentication.service.ts
106
+ import {
107
+ BaseService,
108
+ inject,
109
+ IAuthService,
110
+ IJWTTokenPayload,
111
+ JWTTokenService,
112
+ TSignInRequest,
113
+ } from '@venizia/ignis';
114
+ import { Context } from 'hono';
115
+
116
+ export class AuthenticationService extends BaseService implements IAuthService {
117
+ constructor(
118
+ @inject({ key: 'services.JWTTokenService' }) private jwtService: JWTTokenService,
119
+ ) {
120
+ super({ scope: AuthenticationService.name });
121
+ }
122
+
123
+ async signIn(context: Context, opts: TSignInRequest): Promise<{ token: string }> {
124
+ const { identifier, credential } = opts;
125
+
126
+ // --- Your custom logic here ---
127
+ // 1. Find the user by identifier (e.g., username or email).
128
+ // 2. Verify the credential (e.g., check the password).
129
+ // 3. If valid, create a JWT payload.
130
+ const user = { id: 'user-id-from-db', roles: [] }; // Dummy user
131
+
132
+ if (identifier.value !== 'test_username' || credential.value !== 'test_password') {
133
+ throw new Error('Invalid credentials');
134
+ }
135
+ // --- End of custom logic ---
136
+
137
+ const payload: IJWTTokenPayload = {
138
+ userId: user.id,
139
+ roles: user.roles,
140
+ // Add any other data you want in the token
141
+ };
142
+
143
+ const token = await this.jwtService.generate({ payload });
144
+ return { token };
145
+ }
146
+
147
+ async signUp(context: Context, opts: any): Promise<any> {
148
+ // Implement your sign-up logic
149
+ throw new Error('Method not implemented.');
150
+ }
151
+
152
+ async changePassword(context: Context, opts: any): Promise<any> {
153
+ // Implement your change password logic
154
+ throw new Error('Method not implemented.');
155
+ }
156
+ }
157
+ ```
158
+
159
+ This service is then registered in `application.ts` as shown in the previous step. It injects the `JWTTokenService` (provided by the `AuthenticateComponent`) to generate a token upon successful sign-in.
160
+
161
+ #### 3. Securing Routes
162
+
163
+ In your controllers, use decorator-based routing (`@get`, `@post`, etc.) with the `authStrategies` property in the `configs` object to protect endpoints. This will automatically run the necessary authentication middlewares and attach the authenticated user to the Hono `Context`, which can then be accessed type-safely using `TRouteContext`.
164
+
165
+ ```typescript
166
+ // src/controllers/test.controller.ts
167
+ import {
168
+ Authentication,
169
+ BaseController,
170
+ controller,
171
+ get, // Or @api, @post, etc.
172
+ HTTP,
173
+ jsonResponse,
174
+ IJWTTokenPayload,
175
+ TRouteContext, // Import TRouteContext for type safety
176
+ } from '@venizia/ignis';
177
+ import { z } from '@hono/zod-openapi';
178
+
179
+ const SECURE_ROUTE_CONFIG = {
180
+ path: '/secure-data',
181
+ method: HTTP.Methods.GET,
182
+ authStrategies: [Authentication.STRATEGY_JWT],
183
+ responses: jsonResponse({
184
+ description: 'Test message content',
185
+ schema: z.object({ message: z.string() }),
186
+ }),
187
+ } as const;
188
+
189
+ @controller({ path: '/test' })
190
+ export class TestController extends BaseController {
191
+ constructor() {
192
+ super({
193
+ scope: TestController.name,
194
+ path: '/test',
195
+ });
196
+ }
197
+
198
+ @get({ configs: SECURE_ROUTE_CONFIG })
199
+ secureData(c: TRouteContext<typeof SECURE_ROUTE_CONFIG>) {
200
+ // 'c' is fully typed here, including c.get and c.json return type
201
+ const user = c.get(Authentication.CURRENT_USER) as IJWTTokenPayload | undefined;
202
+ return c.json({ message: `Hello, ${user?.userId || 'guest'} from protected data` });
203
+ }
204
+ }
205
+ ```
206
+
207
+ #### 4. Accessing the Current User in Context
208
+
209
+ After a route has been processed, the authenticated user's payload is available directly on the Hono `Context` object, using the `Authentication.CURRENT_USER` key.
210
+
211
+ ```typescript
212
+ import { Context } from 'hono';
213
+ import { Authentication, IJWTTokenPayload } from '@venizia/ignis';
214
+
215
+ // Inside a route handler or a custom middleware
216
+ const user = c.get(Authentication.CURRENT_USER) as IJWTTokenPayload | undefined;
217
+
218
+ if (user) {
219
+ console.log('Authenticated user ID:', user.userId);
220
+ // You can also access roles, email, etc. from the user object
221
+ } else {
222
+ console.log('User is not authenticated or not found in context.');
223
+ }
224
+ ```