@venizia/ignis-docs 0.0.8 → 0.2.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.
Files changed (180) hide show
  1. package/README.md +7 -7
  2. package/content/best-practices/api-usage-examples.md +15 -12
  3. package/content/best-practices/architectural-patterns.md +70 -78
  4. package/content/best-practices/architecture-decisions.md +91 -60
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/content/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/content/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/content/best-practices/code-style-standards/documentation.md +13 -13
  9. package/content/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/content/best-practices/code-style-standards/index.md +1 -1
  11. package/content/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/content/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/content/best-practices/code-style-standards/tooling.md +8 -5
  14. package/content/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/content/best-practices/common-pitfalls.md +56 -37
  16. package/content/best-practices/contribution-workflow.md +13 -14
  17. package/content/best-practices/data-modeling.md +46 -22
  18. package/content/best-practices/deployment-strategies.md +28 -27
  19. package/content/best-practices/error-handling.md +48 -24
  20. package/content/best-practices/index.md +5 -5
  21. package/content/best-practices/performance-optimization.md +40 -31
  22. package/content/best-practices/security-guidelines.md +52 -23
  23. package/content/best-practices/testing-strategies.md +65 -51
  24. package/content/best-practices/troubleshooting-tips.md +24 -24
  25. package/content/extensions/components/{swagger.md → api-reference.md} +40 -31
  26. package/content/extensions/components/authentication/api.md +19 -19
  27. package/content/extensions/components/authentication/errors.md +7 -7
  28. package/content/extensions/components/authentication/index.md +10 -8
  29. package/content/extensions/components/authentication/usage.md +101 -6
  30. package/content/extensions/components/authorization/api.md +45 -25
  31. package/content/extensions/components/authorization/errors.md +6 -6
  32. package/content/extensions/components/authorization/index.md +11 -10
  33. package/content/extensions/components/authorization/usage.md +21 -21
  34. package/content/extensions/components/health-check.md +1 -1
  35. package/content/extensions/components/index.md +5 -5
  36. package/content/extensions/components/mail/errors.md +15 -15
  37. package/content/extensions/components/mail/index.md +1 -2
  38. package/content/extensions/components/mail/usage.md +1 -1
  39. package/content/extensions/components/request-tracker.md +1 -1
  40. package/content/extensions/components/socket-io/api.md +9 -9
  41. package/content/extensions/components/socket-io/errors.md +5 -5
  42. package/content/extensions/components/socket-io/index.md +8 -8
  43. package/content/extensions/components/socket-io/usage.md +1 -1
  44. package/content/extensions/components/static-asset/api.md +17 -4
  45. package/content/extensions/components/static-asset/errors.md +4 -4
  46. package/content/extensions/components/static-asset/index.md +26 -28
  47. package/content/extensions/components/static-asset/usage.md +13 -12
  48. package/content/extensions/components/template/index.md +2 -2
  49. package/content/extensions/components/template/setup-page.md +1 -1
  50. package/content/extensions/components/websocket/api.md +3 -3
  51. package/content/extensions/components/websocket/errors.md +5 -5
  52. package/content/extensions/components/websocket/index.md +5 -5
  53. package/content/extensions/components/websocket/usage.md +3 -3
  54. package/content/extensions/helpers/cron/index.md +2 -2
  55. package/content/extensions/helpers/crypto/index.md +1 -1
  56. package/content/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +81 -25
  58. package/content/extensions/helpers/index.md +2 -3
  59. package/content/extensions/helpers/inversion/index.md +15 -7
  60. package/content/extensions/helpers/kafka/compile-binary.md +92 -0
  61. package/content/extensions/helpers/kafka/examples.md +1 -1
  62. package/content/extensions/helpers/kafka/index.md +3 -0
  63. package/content/extensions/helpers/logger/index.md +32 -2
  64. package/content/extensions/helpers/network/index.md +6 -0
  65. package/content/extensions/helpers/queue/index.md +14 -17
  66. package/content/extensions/helpers/redis/index.md +548 -323
  67. package/content/extensions/helpers/socket-io/index.md +14 -10
  68. package/content/extensions/helpers/storage/api.md +44 -8
  69. package/content/extensions/helpers/storage/index.md +43 -7
  70. package/content/extensions/helpers/template/index.md +6 -3
  71. package/content/extensions/helpers/types/index.md +11 -8
  72. package/content/extensions/helpers/websocket/api.md +9 -9
  73. package/content/extensions/helpers/websocket/index.md +7 -7
  74. package/content/extensions/helpers/worker-thread/index.md +2 -2
  75. package/content/extensions/index.md +3 -4
  76. package/content/extensions/src-details/mcp-server.md +18 -24
  77. package/content/guides/core-concepts/application/bootstrapping.md +11 -14
  78. package/content/guides/core-concepts/application/index.md +3 -3
  79. package/content/guides/core-concepts/components.md +19 -10
  80. package/content/guides/core-concepts/dependency-injection.md +6 -3
  81. package/content/guides/core-concepts/grpc-controllers.md +6 -5
  82. package/content/guides/core-concepts/persistent/datasources.md +42 -43
  83. package/content/guides/core-concepts/persistent/index.md +16 -7
  84. package/content/guides/core-concepts/persistent/models.md +24 -20
  85. package/content/guides/core-concepts/persistent/postgres-drivers.md +201 -0
  86. package/content/guides/core-concepts/persistent/repositories.md +40 -23
  87. package/content/guides/core-concepts/persistent/search-meilisearch.md +185 -0
  88. package/content/guides/core-concepts/persistent/search-typesense.md +431 -0
  89. package/content/guides/core-concepts/persistent/transactions.md +61 -25
  90. package/content/guides/core-concepts/rest-controllers.md +12 -9
  91. package/content/guides/core-concepts/services.md +330 -60
  92. package/content/guides/get-started/5-minute-quickstart.md +15 -15
  93. package/content/guides/get-started/philosophy.md +36 -36
  94. package/content/guides/get-started/setup.md +3 -3
  95. package/content/guides/index.md +3 -3
  96. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  97. package/content/guides/migrations/scoped-rbac-migration.md +17 -17
  98. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  99. package/content/guides/reference/glossary.md +19 -12
  100. package/content/guides/reference/mcp-docs-server.md +22 -18
  101. package/content/guides/tutorials/building-a-crud-api.md +37 -44
  102. package/content/guides/tutorials/complete-installation.md +17 -17
  103. package/content/guides/tutorials/ecommerce-api.md +163 -124
  104. package/content/guides/tutorials/realtime-chat.md +181 -135
  105. package/content/guides/tutorials/testing.md +65 -523
  106. package/content/index.md +2 -180
  107. package/content/public/apple-touch-icon.png +0 -0
  108. package/content/public/og-image.png +0 -0
  109. package/content/public/site.webmanifest +11 -0
  110. package/content/references/base/application.md +4 -5
  111. package/content/references/base/bootstrapping.md +18 -5
  112. package/content/references/base/components.md +149 -120
  113. package/content/references/base/connectors.md +178 -0
  114. package/content/references/base/controllers.md +41 -30
  115. package/content/references/base/datasources.md +163 -92
  116. package/content/references/base/dependency-injection.md +34 -22
  117. package/content/references/base/filter-system/application-usage.md +17 -14
  118. package/content/references/base/filter-system/array-operators.md +7 -2
  119. package/content/references/base/filter-system/comparison-operators.md +3 -0
  120. package/content/references/base/filter-system/default-filter.md +89 -71
  121. package/content/references/base/filter-system/fields-order-pagination.md +22 -22
  122. package/content/references/base/filter-system/index.md +6 -3
  123. package/content/references/base/filter-system/json-filtering.md +20 -1
  124. package/content/references/base/filter-system/list-operators.md +1 -1
  125. package/content/references/base/filter-system/logical-operators.md +33 -1
  126. package/content/references/base/filter-system/null-operators.md +30 -1
  127. package/content/references/base/filter-system/quick-reference.md +23 -4
  128. package/content/references/base/filter-system/tips.md +5 -5
  129. package/content/references/base/filter-system/use-cases.md +12 -12
  130. package/content/references/base/grpc-controllers.md +13 -13
  131. package/content/references/base/index.md +24 -12
  132. package/content/references/base/middlewares.md +265 -327
  133. package/content/references/base/models.md +63 -49
  134. package/content/references/base/providers.md +136 -130
  135. package/content/references/base/repositories/advanced.md +59 -58
  136. package/content/references/base/repositories/index.md +115 -91
  137. package/content/references/base/repositories/mixins.md +55 -291
  138. package/content/references/base/repositories/relations.md +54 -64
  139. package/content/references/base/repositories/soft-deletable.md +31 -30
  140. package/content/references/base/services.md +296 -93
  141. package/content/references/configuration/environment-variables.md +49 -31
  142. package/content/references/configuration/index.md +6 -6
  143. package/content/references/index.md +17 -12
  144. package/content/references/quick-reference.md +65 -106
  145. package/content/references/utilities/crypto.md +65 -23
  146. package/content/references/utilities/index.md +3 -3
  147. package/content/references/utilities/jsx.md +6 -4
  148. package/content/references/utilities/module.md +68 -20
  149. package/content/references/utilities/parse.md +4 -14
  150. package/content/references/utilities/promise.md +9 -7
  151. package/content/references/utilities/schema.md +5 -3
  152. package/dist/mcp-server/common/guards.d.ts +8 -0
  153. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  154. package/dist/mcp-server/common/guards.js +14 -0
  155. package/dist/mcp-server/common/guards.js.map +1 -0
  156. package/dist/mcp-server/common/index.d.ts +1 -0
  157. package/dist/mcp-server/common/index.d.ts.map +1 -1
  158. package/dist/mcp-server/common/index.js +1 -0
  159. package/dist/mcp-server/common/index.js.map +1 -1
  160. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  162. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  163. package/dist/mcp-server/helpers/github.helper.js +1 -1
  164. package/dist/mcp-server/index.js +7 -2
  165. package/dist/mcp-server/index.js.map +1 -1
  166. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  167. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  168. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  169. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  170. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  175. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  178. package/package.json +9 -9
  179. package/content/extensions/helpers/testing/index.md +0 -510
  180. package/content/references/base/middleware.md +0 -347
@@ -1,28 +1,33 @@
1
1
  ---
2
2
  title: DataSources Reference
3
- description: Technical reference for DataSource classes and database connections
3
+ description: Technical reference for the engine-neutral DataSource contract and the PostgreSQL connector implementation
4
4
  difficulty: intermediate
5
5
  ---
6
6
 
7
7
  # Deep Dive: DataSources
8
8
 
9
- Technical reference for DataSource classes - managing database connections in Ignis.
9
+ Technical reference for DataSource classes - managing database and search engine connections in IGNIS.
10
10
 
11
- **Files:** `packages/core/src/base/datasources/*.ts`
11
+ > [!IMPORTANT] Base vs. Connectors
12
+ > IGNIS splits datasources into an **engine-neutral root** (`src/base/datasources/`) and **per-engine connectors** (`src/connectors/{postgres,typesense}/datasources/`). `AbstractDataSource` has no SQL, no Drizzle, and no `pool` - those live only in the PostgreSQL connector. See [Connectors](./connectors) for the full base-vs-connectors architecture, dual-door exports, and how to add a new engine. This page documents the neutral contract plus the PostgreSQL connector in depth; see [Search & Typesense](/guides/core-concepts/persistent/search-typesense) for the other engine.
13
+
14
+ **Files:** `packages/core/src/base/datasources/*.ts` (neutral) and `packages/core/src/connectors/postgres/datasources/*.ts` (PostgreSQL)
12
15
 
13
16
  ## Quick Reference
14
17
 
15
18
  | Class/Interface | Purpose | Key Members |
16
19
  |-----------------|---------|-------------|
17
- | **IDataSource** | Contract for all datasources | `name`, `settings`, `connector`, `getSchema()`, `configure()`, `beginTransaction()` |
18
- | **AbstractDataSource** | Base implementation with logging | Extends `BaseHelper`, declares abstract methods |
19
- | **BaseDataSource** | Concrete class to extend | Auto-discovery, transaction support, constructor with config |
20
- | **ITransaction** | Transaction object | `connector`, `isActive`, `isolationLevel`, `commit()`, `rollback()` |
21
- | **IsolationLevels** | Isolation level constants | `READ_COMMITTED`, `REPEATABLE_READ`, `SERIALIZABLE` |
20
+ | **IDataSource** | Engine-neutral contract for all datasources | `name`, `settings`, `schema`, `getSchema()`, `getSettings()`, `configure()` |
21
+ | **AbstractDataSource** | Engine-neutral base implementation with logging | Extends `BaseHelper`, `getCapabilities()` defaults to `{ transactions: false }`, `beginTransaction()` defaults to `throwNotSupported(...)` |
22
+ | **AbstractPostgresDataSource** | PostgreSQL-aware abstraction | Adds `connector`, `client`, `driver`, abstract `getConnectionString()`/`beginTransaction()` |
23
+ | **BasePostgresDataSource** | Concrete class to extend for PostgreSQL | Auto-discovery, real transaction support, constructor with config. Canonical name - `BaseDataSource` is a compatibility alias re-exporting the same class |
24
+ | **ITransaction** | Engine-neutral transaction contract | `isActive`, `commit()`, `rollback()` (no connector field) |
25
+ | **IDatabaseTransaction** | PostgreSQL transaction object | Extends `ITransaction`, adds `connector`, `isolationLevel` |
26
+ | **IsolationLevels** | Isolation level constants (PostgreSQL) | `READ_COMMITTED`, `REPEATABLE_READ`, `SERIALIZABLE` |
22
27
 
23
28
  ## `IDataSource` Interface
24
29
 
25
- Contract for all datasource classes in the framework.
30
+ Engine-neutral contract implemented by every datasource in the framework, regardless of engine.
26
31
 
27
32
  **File:** `packages/core/src/base/datasources/common/types.ts`
28
33
 
@@ -39,7 +44,7 @@ interface IDataSource<
39
44
  | Type Parameter | Default | Description |
40
45
  |----------------|---------|-------------|
41
46
  | `Settings` | `{}` | Connection configuration type (host, port, etc.) |
42
- | `Schema` | `TAnyDataSourceSchema` | Combined Drizzle schema type (tables + relations) |
47
+ | `Schema` | `TAnyDataSourceSchema` | Combined schema type (shape depends on the connector - Drizzle tables for PostgreSQL, collection definitions for typesense) |
43
48
  | `ConfigurableOptions` | `{}` | Options passed to `configure()` |
44
49
 
45
50
  ### Properties & Methods
@@ -48,22 +53,19 @@ interface IDataSource<
48
53
  |--------|------|-------------|
49
54
  | `name` | `string` | Datasource name |
50
55
  | `settings` | `Settings` | Configuration object |
51
- | `connector` | `TNodePostgresConnector<Schema>` | Drizzle ORM connector instance |
52
- | `schema` | `Schema` | Combined Drizzle schema (auto-discovered or manual) |
53
- | `getSchema()` | `Schema` | Returns combined Drizzle schema |
56
+ | `schema` | `Schema` | Combined schema (auto-discovered or manual) |
57
+ | `getSchema()` | `Schema` | Returns combined schema |
54
58
  | `getSettings()` | `Settings` | Returns connection settings |
55
- | `getConnector()` | `TNodePostgresConnector<Schema>` | Returns the Drizzle connector |
56
- | `getConnectionString()` | `ValueOrPromise<string>` | Returns connection string |
57
- | `configure(opts?)` | `ValueOrPromise<void>` | Initializes pool and connector |
58
- | `beginTransaction(opts?)` | `Promise<ITransaction<Schema>>` | Starts a new database transaction |
59
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initializes the underlying connection (from `IConfigurable`) |
59
60
 
60
- ## `AbstractDataSource` & `BaseDataSource`
61
+ > [!NOTE]
62
+ > `getCapabilities()` and `beginTransaction()` are not part of the `IDataSource` interface - they are declared on `AbstractDataSource` (below), which every connector extends.
61
63
 
62
- **File:** `packages/core/src/base/datasources/base.ts`
64
+ ## `AbstractDataSource` (Engine-Neutral Root)
63
65
 
64
- ### `AbstractDataSource`
66
+ **File:** `packages/core/src/base/datasources/abstract.ts`
65
67
 
66
- Top-level abstract class that implements `IDataSource`. Extends `BaseHelper` for scoped logging. Declares the core properties and abstract methods.
68
+ Top-level abstract class implemented by every engine. Extends `BaseHelper` for scoped logging. Contains **no SQL, no Drizzle, and no connection-pool members** - those are added by each connector.
67
69
 
68
70
  ```typescript
69
71
  abstract class AbstractDataSource<
@@ -79,45 +81,92 @@ abstract class AbstractDataSource<
79
81
  |----------|------|------------|-------------|
80
82
  | `name` | `string` | public | Datasource identifier |
81
83
  | `settings` | `Settings` | public | Connection configuration |
82
- | `connector` | `TNodePostgresConnector<Schema>` | public | Drizzle ORM instance |
83
- | `schema` | `Schema` | public | Combined schema (tables + relations) |
84
- | `pool` | `Pool` | protected | node-postgres connection pool |
84
+ | `schema` | `Schema` | public | Combined schema |
85
85
 
86
- **Abstract methods** (must be implemented by subclasses):
86
+ **Abstract methods** (must be implemented by connectors):
87
87
 
88
88
  | Method | Return Type | Description |
89
89
  |--------|-------------|-------------|
90
- | `configure(opts?)` | `ValueOrPromise<void>` | Initialize pool and Drizzle connector |
91
- | `getConnectionString()` | `ValueOrPromise<string>` | Return the database connection URL |
92
- | `beginTransaction(opts?)` | `Promise<ITransaction<Schema>>` | Start a new transaction |
90
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the underlying connection |
91
+ | `getConnectionString()` | `ValueOrPromise<string>` | Return the connection URL |
93
92
 
94
- **Concrete methods:**
93
+ **Concrete methods (defaults, overridable by connectors):**
95
94
 
96
- | Method | Return Type | Description |
97
- |--------|-------------|-------------|
95
+ | Method | Return Type | Default Behavior |
96
+ |--------|-------------|-------------------|
98
97
  | `getSettings()` | `Settings` | Returns `this.settings` |
99
- | `getConnector()` | `TNodePostgresConnector<Schema>` | Returns `this.connector` |
100
98
  | `getSchema()` | `Schema` | Returns `this.schema` (throws if not initialized) |
99
+ | `getCapabilities()` | `IDataSourceCapabilities` | Returns `{ transactions: false }` |
100
+ | `beginTransaction(opts?)` | `Promise<ITransaction>` | Calls `throwNotSupported({ scope: this.constructor.name, feature: 'Transactions', logger: this.logger })` - throws HTTP 501 with `messageCode: 'core.not_supported'` |
101
+
102
+ > [!NOTE] NotSupported convention
103
+ > Every capability an engine doesn't implement - transactions, row-level locking - uses the same `throwNotSupported` utility (`packages/core/src/utilities/error.utility.ts`), producing a consistent `501 Not Implemented` with `messageCode: 'core.not_supported'`. This is how the typesense connector signals "not applicable to this engine" instead of silently no-op-ing.
104
+
105
+ ### `IDataSourceCapabilities`
106
+
107
+ ```typescript
108
+ interface IDataSourceCapabilities {
109
+ transactions: boolean;
110
+ }
111
+ ```
112
+
113
+ Only `BasePostgresDataSource` overrides `getCapabilities()` to return `{ transactions: true }`. The typesense datasources inherit the neutral default.
114
+
115
+ ## PostgreSQL Connector: `AbstractPostgresDataSource` & `BasePostgresDataSource`
116
+
117
+ **Files:** `packages/core/src/connectors/postgres/datasources/abstract.ts`, `packages/core/src/connectors/postgres/datasources/base.ts`
101
118
 
102
- ### `BaseDataSource`
119
+ ### `AbstractPostgresDataSource`
103
120
 
104
- Extends `AbstractDataSource` with a constructor, **schema auto-discovery**, and a default `beginTransaction()` implementation.
121
+ Extends `AbstractDataSource` with PostgreSQL/Drizzle-specific members.
105
122
 
106
123
  ```typescript
107
- abstract class BaseDataSource<
124
+ abstract class AbstractPostgresDataSource<
108
125
  Settings extends object = {},
109
126
  Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
110
127
  ConfigurableOptions extends object = {},
111
128
  > extends AbstractDataSource<Settings, Schema, ConfigurableOptions>
112
129
  ```
113
130
 
131
+ **Additional properties:**
132
+
133
+ | Property | Type | Visibility | Description |
134
+ |----------|------|------------|-------------|
135
+ | `connector` | `TRelationalConnector<Schema>` | public | Drizzle ORM instance (any Drizzle pg driver satisfies this - see the driver seam below) |
136
+ | `driver` | `IRelationalDriver` | protected | The connection driver (`node-postgres` or `postgres-js`); built lazily by `wireDriverFromMetadata()` from the class named in `@datasource({ driver })`, or explicitly by `useDriver()` |
137
+ | `client` | `Client` (`Pool` by default) | protected | The raw driver client `configure()` built - a `pg.Pool`, or a postgres-js `Sql`. Assigning it alone is enough: `wireDriverFromMetadata()` instantiates the `@datasource({ driver })` class over it on first use. Absent once `useDriver()` wired a driver instead |
138
+
139
+ > [!NOTE] Driver seam
140
+ > `AbstractRelationalDataSource`/`BaseRelationalDataSource` (exported as `AbstractPostgresDataSource`/`BasePostgresDataSource`) now take a fourth generic - `<Settings, Schema, ConfigurableOptions, Client = Pool>` - so a `postgres-js` datasource can declare `Client = Sql` and keep `getClient()` honest. `@datasource({ driver })` names the driver **class** (`NodePostgresDriver` or `PostgresJsDriver`), never a string - a driver-name string cannot carry `pg`/`postgres` into the app's bundle, only a real class reference can. `configure()` only needs to assign `this.client`; the protected `wireDriverFromMetadata()` (called internally by `getConnector()`/`resolveDriver()`) instantiates the named class over it and builds `this.connector`, lazily and idempotently. The protected `useDriver({ driver, schema? })` stays available for a custom or third-party driver - it assigns `this.driver` **and** builds `this.connector` in one step, bypassing `@datasource({ driver })` entirely. `pg` and `postgres` are both optional peer dependencies; concrete drivers live at `@venizia/ignis/postgres/node-postgres` and `@venizia/ignis/postgres/postgres-js`, and Supabase support at `@venizia/ignis/postgres/supabase`. See [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers).
141
+
142
+ **Additional abstract method:**
143
+
144
+ | Method | Return Type | Description |
145
+ |--------|-------------|-------------|
146
+ | `beginTransaction(opts?)` | `Promise<IDatabaseTransaction<Schema>>` | Start a new PostgreSQL transaction |
147
+
148
+ ### `BasePostgresDataSource` (canonical name; `BaseDataSource` is a compatibility alias)
149
+
150
+ Extends `AbstractPostgresDataSource` with a constructor, **schema auto-discovery**, and a real `beginTransaction()` implementation backed by the connection pool.
151
+
152
+ ```typescript
153
+ abstract class BasePostgresDataSource<
154
+ Settings extends object = {},
155
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
156
+ ConfigurableOptions extends object = {},
157
+ > extends AbstractPostgresDataSource<Settings, Schema, ConfigurableOptions>
158
+ ```
159
+
160
+ > [!TIP] Naming
161
+ > `BasePostgresDataSource` is the canonical, engine-carrying name - prefer it in new code. `import { BaseDataSource } from '@venizia/ignis'` (or `@venizia/ignis/postgres`) still resolves to the exact same class via a re-export in `connectors/postgres/datasources/index.ts` (`export { BasePostgresDataSource as BaseDataSource } from './base-datasource'`), so existing code is unaffected.
162
+
114
163
  #### Key Features
115
164
 
116
165
  | Feature | Description |
117
- |---------|-------------|
166
+ |---------|--------------|
118
167
  | **Schema Auto-Discovery** | Schema is automatically built from registered `@repository` decorators |
119
168
  | **Manual Override** | You can manually provide schema in constructor for full control |
120
- | **Built-in Transaction Support** | `beginTransaction()` implemented using the `pool` property |
169
+ | **Built-in Transaction Support** | `beginTransaction()` acquires its connection from the resolved driver; overrides `getCapabilities()` to return `{ transactions: true }` |
121
170
 
122
171
  > [!TIP]
123
172
  > Set `autoDiscovery` to `false` in the `@datasource` decorator to disable automatic schema discovery. This is useful when you want to manually provide the schema.
@@ -153,9 +202,8 @@ When you use `@repository({ model: YourModel, dataSource: YourDataSource })`, th
153
202
  - It calls the `configure()` method on your instance
154
203
 
155
204
  3. **Your `configure()` method runs**:
156
- - Call `this.getSchema()` to get the auto-discovered schema
157
- - Create a `Pool` instance and assign it to `this.pool` (required for transaction support)
158
- - Create the Drizzle connector with the pool and schema
205
+ - Create a `Pool` instance and assign it to `this.client` - that is the whole method
206
+ - `getConnector()`/`beginTransaction()` lazily instantiate the class named in `@datasource({ driver })` over `this.client` and build the Drizzle connector from it - your `configure()` never touches `this.connector` directly
159
207
 
160
208
  ### Example Implementations
161
209
 
@@ -165,12 +213,12 @@ Simplest approach - schema is auto-discovered from repositories:
165
213
 
166
214
  ```typescript
167
215
  // src/datasources/postgres.datasource.ts
168
- import { BaseDataSource, datasource, ValueOrPromise } from '@venizia/ignis';
169
- import { applicationEnvironment, int } from '@venizia/ignis-helpers';
170
- import { drizzle } from 'drizzle-orm/node-postgres';
216
+ import { BasePostgresDataSource, datasource } from '@venizia/ignis';
217
+ import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
218
+ import { applicationEnvironment, int, ValueOrPromise } from '@venizia/ignis-helpers';
171
219
  import { Pool } from 'pg';
172
220
 
173
- interface IDSConfigs {
221
+ interface IDataSourceConfigs {
174
222
  host: string;
175
223
  port: number;
176
224
  database: string;
@@ -184,11 +232,11 @@ interface IDSConfigs {
184
232
  *
185
233
  * How it works:
186
234
  * 1. @repository decorator binds model to datasource
187
- * 2. When configure() is called, getSchema() auto-discovers all bound models
188
- * 3. Drizzle is initialized with the auto-discovered schema
235
+ * 2. getSchema() auto-discovers all bound models when the driver/connector are wired
236
+ * 3. Naming NodePostgresDriver in @datasource is what wires the driver and Drizzle connector
189
237
  */
190
- @datasource({ driver: 'node-postgres' })
191
- export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
238
+ @datasource({ driver: NodePostgresDriver })
239
+ export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
192
240
  private readonly protocol = 'postgresql';
193
241
 
194
242
  constructor() {
@@ -207,19 +255,15 @@ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
207
255
  }
208
256
 
209
257
  override configure(): ValueOrPromise<void> {
210
- // getSchema() auto-discovers models from @repository bindings
211
- const schema = this.getSchema();
212
-
213
- const dsSchema = Object.keys(schema);
258
+ const schema = Object.keys(this.getSchema());
214
259
  this.logger.debug(
215
260
  '[configure] Auto-discovered schema | Schema + Relations (%s): %o',
216
- dsSchema.length,
217
- dsSchema,
261
+ schema.length,
262
+ schema,
218
263
  );
219
264
 
220
- // Store pool reference for transaction support
221
- this.pool = new Pool(this.settings);
222
- this.connector = drizzle({ client: this.pool, schema });
265
+ // That is all - the base class wires the driver + connector from @datasource({ driver }).
266
+ this.client = new Pool(this.settings);
223
267
  }
224
268
 
225
269
  override getConnectionString(): ValueOrPromise<string> {
@@ -246,13 +290,14 @@ The `PostgresDataSource.schema` will automatically include User and Configuratio
246
290
  When you need explicit control over schema (e.g., subset of models, custom ordering):
247
291
 
248
292
  ```typescript
293
+ import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
249
294
  import {
250
295
  User, userTable, userRelations,
251
296
  Configuration, configurationTable, configurationRelations,
252
297
  } from '@/models/entities';
253
298
 
254
- @datasource({ driver: 'node-postgres' })
255
- export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
299
+ @datasource({ driver: NodePostgresDriver })
300
+ export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
256
301
  constructor() {
257
302
  super({
258
303
  name: PostgresDataSource.name,
@@ -274,9 +319,8 @@ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
274
319
  }
275
320
 
276
321
  override configure(): ValueOrPromise<void> {
277
- // When schema is manually provided, getSchema() returns it directly
278
- this.pool = new Pool(this.settings);
279
- this.connector = drizzle({ client: this.pool, schema: this.getSchema() });
322
+ // Manually-provided schema is used as-is by the connector the base class builds from this.client
323
+ this.client = new Pool(this.settings);
280
324
  }
281
325
 
282
326
  override getConnectionString(): ValueOrPromise<string> {
@@ -286,7 +330,7 @@ export class PostgresDataSource extends BaseDataSource<IDSConfigs> {
286
330
  ```
287
331
 
288
332
  > [!IMPORTANT]
289
- > You must assign `this.pool` in your `configure()` method. The built-in `beginTransaction()` uses `this.pool` to acquire a `PoolClient` for transaction isolation. If `this.pool` is not set, `beginTransaction()` will throw an error.
333
+ > Your `configure()` must leave the datasource with a way to reach the database: either assign the raw client to `this.client` (paired with naming the driver class in `@datasource({ driver })`), or wire a driver directly with `this.useDriver({ driver })` for a custom or third-party driver. `getConnector()`/`beginTransaction()` resolve the driver lazily from whichever you provided. With neither, it throws `No driver and no client`.
290
334
 
291
335
  ### `@datasource` Decorator
292
336
 
@@ -294,23 +338,23 @@ The `@datasource` decorator registers datasource metadata:
294
338
 
295
339
  ```typescript
296
340
  @datasource({
297
- driver: 'node-postgres', // Required - database driver
341
+ driver: NodePostgresDriver, // Required - driver CLASS (or a search engine's driver-name string)
298
342
  autoDiscovery?: true // Optional - defaults to true
299
343
  })
300
344
  ```
301
345
 
302
346
  | Option | Type | Default | Description |
303
347
  |--------|------|---------|-------------|
304
- | `driver` | `TDataSourceDriver` | - | Database driver name (currently only `'node-postgres'`) |
348
+ | `driver` | `TDataSourceDriverClass` | - | The driver **class** - `NodePostgresDriver` or `PostgresJsDriver` (imported from `@venizia/ignis/postgres/node-postgres` / `.../postgres-js`), never a driver-name string. A class reference is the only thing that carries `pg`/`postgres` into the app's bundle. **Omit it for a search datasource**: `extends TypesenseDataSource` already names the engine, and is what carries `typesense` into the bundle |
305
349
  | `autoDiscovery` | `boolean` | `true` | Enable/disable schema auto-discovery |
306
350
 
307
351
  ### Abstract Methods
308
352
 
309
- When extending `BaseDataSource`, these methods must be implemented:
353
+ When extending `BasePostgresDataSource`, these methods must be implemented:
310
354
 
311
355
  | Method | Return Type | Description |
312
356
  |--------|-------------|-------------|
313
- | `configure(opts?)` | `ValueOrPromise<void>` | Initialize pool and Drizzle connector. Must set `this.pool` and `this.connector`. |
357
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the client. Must set `this.client` (the base class wires the driver and Drizzle connector from `@datasource({ driver })`), or call `this.useDriver()` directly for a custom driver. |
314
358
  | `getConnectionString()` | `ValueOrPromise<string>` | Return the database connection string. |
315
359
 
316
360
  ### Helper Methods
@@ -330,61 +374,81 @@ When extending `BaseDataSource`, these methods must be implemented:
330
374
 
331
375
  ## Connector Types
332
376
 
333
- **File:** `packages/core/src/base/datasources/common/types.ts`
377
+ **File:** `packages/core/src/connectors/postgres/datasources/common/types.ts`
334
378
 
335
379
  | Type | Description |
336
380
  |------|-------------|
337
- | `TNodePostgresConnector<Schema>` | Drizzle connector using `NodePgClient` (Pool or PoolClient) |
338
- | `TNodePostgresTransactionConnector<Schema>` | Drizzle connector using `PoolClient` specifically (for transaction isolation) |
339
- | `TAnyConnector<Schema>` | Union of both connector types |
340
- | `TAnyDataSourceSchema` | `Record<string, any>` — base type for all schema objects |
381
+ | `TRelationalConnector<Schema>` | Canonical connector type - a Drizzle `PgDatabase` that **every** pg driver (`node-postgres`, `postgres-js`) satisfies. Use this in new code. |
382
+ | `TNodePostgresConnector<Schema>` | **`@deprecated`** compat alias for `TRelationalConnector<Schema>` |
383
+ | `TNodePostgresTransactionConnector<Schema>` | **`@deprecated`** compat alias - was the `PoolClient`-specific transaction connector; now aliases `TRelationalConnector<Schema>` |
384
+ | `TAnyConnector<Schema>` | Alias of `TRelationalConnector<Schema>` |
385
+ | `TAnyDataSourceSchema` | `Record<string, any>` - base type for all schema objects (defined in `src/base/datasources/common/types.ts`, shared across engines) |
341
386
 
342
387
  ### `DataSourceDrivers`
343
388
 
344
- Static class for driver validation:
389
+ Static class for driver validation (defined in `src/base/datasources/common/types.ts`, shared across engines):
345
390
 
346
391
  ```typescript
347
392
  DataSourceDrivers.NODE_POSTGRES // 'node-postgres'
393
+ DataSourceDrivers.POSTGRES_JS // 'postgres-js'
394
+ DataSourceDrivers.TYPESENSE // 'typesense'
395
+ DataSourceDrivers.MEILISEARCH // 'meilisearch'
348
396
  DataSourceDrivers.isValid('node-postgres') // true
349
397
  ```
350
398
 
399
+ > [!NOTE]
400
+ > `NODE_POSTGRES`/`POSTGRES_JS` remain valid `TDataSourceDriver` string values, but `@datasource({ driver })` on a **relational** datasource no longer accepts them - it takes the `NodePostgresDriver`/`PostgresJsDriver` class instead (see [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers)). Search connectors (`TYPESENSE`, `MEILISEARCH`) still take the driver-name string form.
401
+
351
402
  ## Transaction Support
352
403
 
353
- DataSources provide built-in transaction management through the `beginTransaction()` method. This allows you to perform atomic operations across multiple repositories.
404
+ Only engines that declare `getCapabilities().transactions === true` implement real transactions - currently just the PostgreSQL connector. Calling `beginTransaction()` on the typesense connector throws `NotSupported` (HTTP 501).
354
405
 
355
406
  ### How It Works
356
407
 
357
- `BaseDataSource.beginTransaction()` does the following:
408
+ `BasePostgresDataSource.beginTransaction()` does the following:
358
409
 
359
- 1. Acquires a `PoolClient` from `this.pool`
410
+ 1. Resolves a driver from `this.client` (or the one `useDriver()` wired) and acquires a connection from it
360
411
  2. Executes `BEGIN TRANSACTION ISOLATION LEVEL <level>` on the client
361
412
  3. Creates a separate Drizzle connector scoped to that client
362
- 4. Returns an `ITransaction` object with `commit()`, `rollback()`, and the scoped `connector`
413
+ 4. Returns an `IDatabaseTransaction` object with `commit()`, `rollback()`, and the scoped `connector`
363
414
 
364
- When `commit()` or `rollback()` is called, the client is released back to the pool.
415
+ When `commit()` or `rollback()` succeeds, the client is released back to the pool.
365
416
 
366
- ### Transaction Types
417
+ > [!WARNING] `commit()`/`rollback()` throw on failure
418
+ > A failed `COMMIT` or `ROLLBACK` **throws** (a failed `COMMIT` no longer resolves as success), and the poisoned connection is **destroyed** rather than returned to the pool - under the `node-postgres` driver, which can discard a connection; `postgres-js` has no destroy semantics and pools it anyway. A failed `BEGIN` also destroys the acquired connection rather than leaking it. Because `rollback()` can throw and is normally called from a `catch`, nest it in its own `try...catch` so the rollback error does not replace the original cause. See [Transactions](/guides/core-concepts/persistent/transactions) and [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers).
367
419
 
368
- | Type | Description |
369
- |------|-------------|
370
- | `ITransaction<Schema>` | Transaction object with `commit()`, `rollback()`, and `connector` |
371
- | `ITransactionOptions` | Options for starting a transaction (e.g., `isolationLevel`) |
372
- | `TIsolationLevel` | Union type: `'READ COMMITTED'` \| `'REPEATABLE READ'` \| `'SERIALIZABLE'` |
373
- | `IsolationLevels` | Static class with isolation level constants and validation |
420
+ ### Neutral vs. PostgreSQL Transaction Types
374
421
 
375
- ### ITransaction Interface
422
+ `src/base` declares the engine-neutral shape; the PostgreSQL connector narrows it with connection details.
376
423
 
377
424
  ```typescript
378
- interface ITransaction<Schema> {
379
- connector: TNodePostgresTransactionConnector<Schema>;
380
- isActive: boolean; // read-only getter, false after commit/rollback
381
- isolationLevel: TIsolationLevel;
382
-
425
+ // packages/core/src/base/datasources/common/types.ts - engine-neutral
426
+ interface ITransaction<_Schema = unknown> {
427
+ isActive: boolean;
383
428
  commit(): Promise<void>;
384
429
  rollback(): Promise<void>;
385
430
  }
431
+
432
+ // packages/core/src/connectors/postgres/datasources/common/types.ts - PostgreSQL
433
+ interface IDatabaseTransaction<Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema>
434
+ extends ITransaction<Schema> {
435
+ connector: TRelationalConnector<Schema>;
436
+ isolationLevel: TIsolationLevel;
437
+ }
386
438
  ```
387
439
 
440
+ | Type | Description |
441
+ |------|-------------|
442
+ | `ITransaction<Schema>` | Engine-neutral contract - `isActive`, `commit()`, `rollback()`. No connector field. |
443
+ | `IDatabaseTransaction<Schema>` | PostgreSQL transaction object - extends `ITransaction` with `connector` and `isolationLevel` |
444
+ | `IDatabaseTransactionOptions` | Options for starting a PostgreSQL transaction (`isolationLevel`); extends the neutral `ITransactionOptions` |
445
+ | `IDatabaseExtraOptions` | Extends the neutral `IExtraOptions`, narrowing `transaction?: IDatabaseTransaction` |
446
+ | `TIsolationLevel` | Union type: `'READ COMMITTED'` \| `'REPEATABLE READ'` \| `'SERIALIZABLE'` |
447
+ | `IsolationLevels` | Static class with isolation level constants and validation |
448
+
449
+ > [!NOTE]
450
+ > `AbstractRepository`, `PersistableRepository`, and every other engine-neutral repository type parameter is named `TOptions`/`IExtraOptions` in `src/base`. The PostgreSQL connector's `PostgresBaseRepository` narrows `ExtraOptions` to default to `IDatabaseExtraOptions`, so repository code written against a `PostgresBaseRepository` subclass sees `IDatabaseTransaction` (with `connector`/`isolationLevel`) rather than the bare neutral `ITransaction`.
451
+
388
452
  ### Isolation Levels
389
453
 
390
454
  Use the `IsolationLevels` static class for type-safe isolation level constants:
@@ -420,7 +484,12 @@ try {
420
484
 
421
485
  await tx.commit();
422
486
  } catch (error) {
423
- await tx.rollback();
487
+ // rollback() throws if ROLLBACK itself fails - nest it so it never replaces the original cause
488
+ try {
489
+ await tx.rollback();
490
+ } catch (rollbackError) {
491
+ logger.error('Rollback failed | %s', rollbackError);
492
+ }
424
493
  throw error;
425
494
  }
426
495
  ```
@@ -432,10 +501,12 @@ This architecture ensures that datasources are configured consistently and that
432
501
  ## See Also
433
502
 
434
503
  - **Related Concepts:**
504
+ - [Connectors](./connectors) - Base-vs-connectors architecture, dual-door exports, aliases
435
505
  - [DataSources Guide](/guides/core-concepts/persistent/datasources) - Creating DataSources tutorial
436
506
  - [Repositories](/guides/core-concepts/persistent/repositories) - Using DataSources for database access
437
507
  - [Models](/guides/core-concepts/persistent/models) - Entity schemas loaded by DataSource
438
508
  - [Transactions](/guides/core-concepts/persistent/transactions) - Multi-operation database transactions
509
+ - [Search & Typesense](/guides/core-concepts/persistent/search-typesense) - The typesense connector
439
510
 
440
511
  - **References:**
441
512
  - [Repositories API](/references/base/repositories/) - Data access layer
@@ -6,16 +6,16 @@ difficulty: advanced
6
6
 
7
7
  # Deep Dive: Dependency Injection
8
8
 
9
- Technical reference for the DI system in Ignis - managing resource lifecycles and dependency resolution.
9
+ Technical reference for the DI system in IGNIS - managing resource lifecycles and dependency resolution.
10
10
 
11
11
  **Files:**
12
- - `packages/inversion/src/container.ts` Base `Container` and `Binding` classes
13
- - `packages/inversion/src/registry.ts` Base `MetadataRegistry`
14
- - `packages/inversion/src/metadata/injectors.ts` Base `@inject` and `@injectable` decorators
15
- - `packages/inversion/src/common/types.ts` `BindingScopes`, `BindingValueTypes`, `BindingKeys`, `IProvider`
16
- - `packages/core/src/helpers/inversion/container.ts` Extended `Container` with `ApplicationLogger`
17
- - `packages/core/src/helpers/inversion/registry.ts` Extended `MetadataRegistry` (singleton, with model/repository/datasource mixins)
18
- - `packages/core/src/base/metadata/injectors.ts` Core `@inject` and `@injectable` (wired to extended registry)
12
+ - `packages/inversion/src/container.ts` - Base `Container` and `Binding` classes
13
+ - `packages/inversion/src/registry.ts` - Base `MetadataRegistry`
14
+ - `packages/inversion/src/metadata/injectors.ts` - Base `@inject` and `@injectable` decorators
15
+ - `packages/inversion/src/common/types.ts` - `BindingScopes`, `BindingValueTypes`, `BindingKeys`, `IProvider`
16
+ - `packages/core/src/helpers/inversion/container.ts` - Extended `Container` with `ApplicationLogger`
17
+ - `packages/core/src/helpers/inversion/registry.ts` - Extended `MetadataRegistry` (singleton, with model/repository/datasource mixins)
18
+ - `packages/core/src/base/metadata/injectors.ts` - Core `@inject` and `@injectable` (wired to extended registry)
19
19
 
20
20
  ## Quick Reference
21
21
 
@@ -25,7 +25,7 @@ Technical reference for the DI system in Ignis - managing resource lifecycles an
25
25
  | **Binding** | Single registered dependency configuration | `toClass()`, `toValue()`, `toProvider()`, `setScope()`, `setTags()`, `getValue()`, `clearCache()` |
26
26
  | **@inject** | Decorator marking injection points | Applied to constructor parameters and class properties |
27
27
  | **@injectable** | Decorator marking a class as injectable | Stores scope and tag metadata |
28
- | **MetadataRegistry** | Stores decorator metadata | Singleton base via `metadataRegistry` export, core via `MetadataRegistry.getInstance()` |
28
+ | **MetadataRegistry** | Stores decorator metadata | Singleton - base via `metadataRegistry` export, core via `MetadataRegistry.getInstance()` |
29
29
  | **BindingKeys** | Utility for building namespaced keys | `BindingKeys.build({ namespace, key })` |
30
30
  | **Boot System** | Automatic artifact discovery and binding | Integrates with Container via tags and bindings |
31
31
 
@@ -58,7 +58,7 @@ const container = new Container({ scope: 'MyApp' }); // scope is optional, defau
58
58
  | :--- | :--- | :--- |
59
59
  | **`bind`** | `bind<T>({ key: string \| symbol }): Binding<T>` | Creates and registers a new `Binding` for the given key. Returns the `Binding` for fluent configuration. |
60
60
  | **`get`** | `get<T>({ key, isOptional? }): T` | Retrieves a resolved dependency. `key` can be a `string`, `symbol`, or `{ namespace, key }` object. Throws if not found and `isOptional` is `false` (default). Returns `undefined` if `isOptional` is `true` and not found. |
61
- | **`gets`** | `gets<T>({ bindings }): T[]` | Resolves multiple dependencies at once. Each entry in `bindings` accepts `{ key, isOptional? }`. All lookups are treated as optional (returns `undefined` for missing). |
61
+ | **`gets`** | `gets<T extends unknown[]>({ bindings }): { [K in keyof T]: T[K] \| undefined }` | Resolves multiple dependencies at once. Each entry in `bindings` accepts `{ key, isOptional? }`. Returns a tuple-preserving array where missing bindings resolve to `undefined`. |
62
62
  | **`getBinding`** | `getBinding<T>({ key }): Binding<T> \| undefined` | Returns the raw `Binding` object without resolving it. `key` accepts `string`, `symbol`, or `{ namespace, key }`. |
63
63
  | **`set`** | `set<T>({ binding: Binding<T> }): void` | Directly sets a pre-built `Binding` into the container. |
64
64
  | **`isBound`** | `isBound({ key: string \| symbol }): boolean` | Checks if a binding exists for the given key. |
@@ -74,8 +74,8 @@ const container = new Container({ scope: 'MyApp' }); // scope is optional, defau
74
74
 
75
75
  When `container.instantiate(MyClass)` is called:
76
76
 
77
- 1. **Constructor injection** Reads `@inject` metadata from the class, sorts by parameter index, resolves each dependency from the container, and passes them as constructor arguments.
78
- 2. **Property injection** After the instance is created, reads property metadata, resolves each dependency, and assigns them directly to the instance properties.
77
+ 1. **Constructor injection** - Reads `@inject` metadata from the class by parameter index (the `Reflect`-stored array is already index-keyed, so there is no sort step), resolves each dependency from the container, and passes them as constructor arguments. If any index in range has no `@inject` metadata, `instantiate()` throws immediately rather than passing `undefined`.
78
+ 2. **Property injection** - After the instance is created, reads property metadata, resolves each dependency, and assigns them directly to the instance properties.
79
79
 
80
80
  ```typescript
81
81
  // Both constructor and property injection in action
@@ -90,6 +90,18 @@ class UserController {
90
90
  }
91
91
  ```
92
92
 
93
+ > [!IMPORTANT]
94
+ > **Every constructor parameter of a container-instantiated class must carry `@inject`.** Mixing decorated and undecorated parameters is forbidden - `@inject` stores its metadata at the parameter's index, so an undecorated parameter leaves a hole in that array, and there is no channel through which the container could supply it anyway (it would resolve to `undefined`). `instantiate()` refuses the shape by class name and parameter index instead of silently dereferencing the hole:
95
+ >
96
+ > ```
97
+ > [NoteController] Constructor parameter 0 has no @inject | Every parameter of a container-instantiated
98
+ > class must be decorated - the container cannot supply an undecorated one
99
+ > ```
100
+ >
101
+ > The check lives in `instantiate()`, not in the `@inject` decorator itself: parameter decorators run right-to-left, so when `@inject` on parameter 1 runs, parameter 0 has not been visited yet and nothing at that point can know whether it will end up decorated.
102
+ >
103
+ > This does not apply to `@repository`-decorated classes whose constructor appears undecorated at index 0 - the `@repository` decorator programmatically writes that inject metadata (`registry.setInjectMetadata({ target, index: 0, ... })`) even though no literal `@inject` appears in source. See [Repositories](./repositories/).
104
+
93
105
  ## `Binding` Class
94
106
 
95
107
  A `Binding` represents a single registered dependency in the container. It provides a fluent API to configure *how* a dependency should be created and managed.
@@ -114,7 +126,7 @@ When a binding key contains a dot (e.g., `services.MyService`), the namespace po
114
126
  | **`toValue`** | `toValue(value: T): this` | Binds to a constant value (e.g., a config object, string, number). |
115
127
  | **`toProvider`** | `toProvider(value: ((container) => T) \| TClass<IProvider<T>>): this` | Binds to a factory function or a class implementing `IProvider<T>`. |
116
128
  | **`setScope`** | `setScope(scope: TBindingScope): this` | Sets the lifecycle scope (`'singleton'` or `'transient'`). |
117
- | **`setTags`** | `setTags(...tags: string[]): this` | Adds one or more tags to the binding. Tags are additive calling this multiple times adds more tags. |
129
+ | **`setTags`** | `setTags(...tags: string[]): this` | Adds one or more tags to the binding. Tags are additive - calling this multiple times adds more tags. |
118
130
  | **`getValue`** | `getValue(container?: Container): T` | Resolves the binding's value. For `CLASS` and `PROVIDER` types, a `container` argument is required. Respects singleton caching. |
119
131
  | **`clearCache`** | `clearCache(): void` | Clears the cached singleton instance (if any). Next `getValue()` call will re-create it. |
120
132
  | **`hasTag`** | `hasTag(tag: string): boolean` | Checks if the binding has a specific tag. |
@@ -192,7 +204,7 @@ This is also used internally by `container.get()` and `container.getBinding()` w
192
204
 
193
205
  ## `@inject` Decorator
194
206
 
195
- The `@inject` decorator marks where dependencies should be injected either on constructor parameters or class properties.
207
+ The `@inject` decorator marks where dependencies should be injected - either on constructor parameters or class properties.
196
208
 
197
209
  **File:** `packages/inversion/src/metadata/injectors.ts` (base) & `packages/core/src/base/metadata/injectors.ts` (core wrapper)
198
210
 
@@ -204,7 +216,7 @@ The `@inject` decorator marks where dependencies should be injected — either o
204
216
 
205
217
  | Parameter | Type | Default | Description |
206
218
  | :--- | :--- | :--- | :--- |
207
- | `key` | `string \| symbol` | | The binding key to resolve from the container. |
219
+ | `key` | `string \| symbol` | - | The binding key to resolve from the container. |
208
220
  | `isOptional` | `boolean` | `false` | If `true`, returns `undefined` instead of throwing when the binding is not found. |
209
221
 
210
222
  ### Constructor Parameter Injection
@@ -262,8 +274,8 @@ Marks a class as injectable and attaches optional metadata.
262
274
 
263
275
  | Parameter | Type | Default | Description |
264
276
  | :--- | :--- | :--- | :--- |
265
- | `scope` | `'singleton' \| 'transient'` | | Optional scope hint for the binding. |
266
- | `tags` | `Record<string, any>` | | Optional metadata tags. |
277
+ | `scope` | `'singleton' \| 'transient'` | - | Optional scope hint for the binding. |
278
+ | `tags` | `Record<string, any>` | - | Optional metadata tags. |
267
279
 
268
280
  ### Example
269
281
 
@@ -272,7 +284,7 @@ Marks a class as injectable and attaches optional metadata.
272
284
  class UserService extends BaseService {
273
285
  constructor(
274
286
  @inject({ key: 'repositories.UserRepository' })
275
- private userRepo: UserRepository,
287
+ private userRepository: UserRepository,
276
288
  ) {
277
289
  super({ scope: UserService.name });
278
290
  }
@@ -390,16 +402,16 @@ This pattern allows the `Bootstrapper` to automatically discover and execute all
390
402
  Once artifacts are discovered and loaded, they're bound using consistent namespace patterns:
391
403
 
392
404
  ```typescript
393
- // Controllers auto-tagged with 'controllers'
405
+ // Controllers - auto-tagged with 'controllers'
394
406
  this.bind({ key: 'controllers.UserController' }).toClass(UserController);
395
407
 
396
- // Services auto-tagged with 'services'
408
+ // Services - auto-tagged with 'services'
397
409
  this.bind({ key: 'services.UserService' }).toClass(UserService);
398
410
 
399
- // Repositories auto-tagged with 'repositories'
411
+ // Repositories - auto-tagged with 'repositories'
400
412
  this.bind({ key: 'repositories.UserRepository' }).toClass(UserRepository);
401
413
 
402
- // Datasources auto-tagged with 'datasources'
414
+ // Datasources - auto-tagged with 'datasources'
403
415
  this.bind({ key: 'datasources.PostgresDataSource' }).toClass(PostgresDataSource);
404
416
  ```
405
417