@venizia/ignis-docs 0.0.8-3 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. package/README.md +7 -7
  2. package/{wiki → content}/best-practices/api-usage-examples.md +15 -12
  3. package/{wiki → content}/best-practices/architectural-patterns.md +70 -78
  4. package/{wiki → content}/best-practices/architecture-decisions.md +91 -60
  5. package/{wiki → content}/best-practices/code-style-standards/advanced-patterns.md +56 -44
  6. package/{wiki → content}/best-practices/code-style-standards/constants-configuration.md +11 -11
  7. package/{wiki → content}/best-practices/code-style-standards/control-flow.md +5 -2
  8. package/{wiki → content}/best-practices/code-style-standards/documentation.md +13 -13
  9. package/{wiki → content}/best-practices/code-style-standards/function-patterns.md +9 -10
  10. package/{wiki → content}/best-practices/code-style-standards/index.md +1 -1
  11. package/{wiki → content}/best-practices/code-style-standards/naming-conventions.md +10 -8
  12. package/{wiki → content}/best-practices/code-style-standards/route-definitions.md +30 -12
  13. package/{wiki → content}/best-practices/code-style-standards/tooling.md +8 -5
  14. package/{wiki → content}/best-practices/code-style-standards/type-safety.md +13 -12
  15. package/{wiki → content}/best-practices/common-pitfalls.md +56 -37
  16. package/{wiki → content}/best-practices/contribution-workflow.md +13 -14
  17. package/{wiki → content}/best-practices/data-modeling.md +44 -20
  18. package/{wiki → content}/best-practices/deployment-strategies.md +28 -27
  19. package/{wiki → content}/best-practices/error-handling.md +48 -24
  20. package/{wiki → content}/best-practices/index.md +5 -5
  21. package/{wiki → content}/best-practices/performance-optimization.md +36 -28
  22. package/{wiki → content}/best-practices/security-guidelines.md +52 -23
  23. package/{wiki → content}/best-practices/testing-strategies.md +65 -51
  24. package/{wiki → content}/best-practices/troubleshooting-tips.md +24 -24
  25. package/{wiki/extensions/components/swagger.md → content/extensions/components/api-reference.md} +40 -31
  26. package/{wiki → content}/extensions/components/authentication/api.md +19 -19
  27. package/{wiki → content}/extensions/components/authentication/errors.md +7 -7
  28. package/{wiki → content}/extensions/components/authentication/index.md +10 -8
  29. package/{wiki → content}/extensions/components/authentication/usage.md +101 -6
  30. package/{wiki → content}/extensions/components/authorization/api.md +45 -25
  31. package/{wiki → content}/extensions/components/authorization/errors.md +6 -6
  32. package/{wiki → content}/extensions/components/authorization/index.md +11 -10
  33. package/{wiki → content}/extensions/components/authorization/usage.md +21 -21
  34. package/{wiki → content}/extensions/components/health-check.md +1 -1
  35. package/{wiki → content}/extensions/components/index.md +5 -5
  36. package/{wiki → content}/extensions/components/mail/errors.md +15 -15
  37. package/{wiki → content}/extensions/components/mail/index.md +1 -2
  38. package/{wiki → content}/extensions/components/mail/usage.md +1 -1
  39. package/{wiki → content}/extensions/components/request-tracker.md +1 -1
  40. package/{wiki → content}/extensions/components/socket-io/api.md +9 -9
  41. package/{wiki → content}/extensions/components/socket-io/errors.md +5 -5
  42. package/{wiki → content}/extensions/components/socket-io/index.md +8 -8
  43. package/{wiki → content}/extensions/components/socket-io/usage.md +1 -1
  44. package/{wiki → content}/extensions/components/static-asset/api.md +17 -4
  45. package/{wiki → content}/extensions/components/static-asset/errors.md +4 -4
  46. package/{wiki → content}/extensions/components/static-asset/index.md +26 -28
  47. package/{wiki → content}/extensions/components/static-asset/usage.md +13 -12
  48. package/{wiki → content}/extensions/components/template/index.md +2 -2
  49. package/{wiki → content}/extensions/components/template/setup-page.md +1 -1
  50. package/{wiki → content}/extensions/components/websocket/api.md +3 -3
  51. package/{wiki → content}/extensions/components/websocket/errors.md +5 -5
  52. package/{wiki → content}/extensions/components/websocket/index.md +5 -5
  53. package/{wiki → content}/extensions/components/websocket/usage.md +3 -3
  54. package/{wiki → content}/extensions/helpers/cron/index.md +2 -2
  55. package/{wiki → content}/extensions/helpers/crypto/index.md +1 -1
  56. package/{wiki → content}/extensions/helpers/env/index.md +27 -12
  57. package/content/extensions/helpers/error/index.md +283 -0
  58. package/{wiki → content}/extensions/helpers/index.md +2 -3
  59. package/{wiki → content}/extensions/helpers/inversion/index.md +15 -7
  60. package/{wiki → content}/extensions/helpers/kafka/examples.md +1 -1
  61. package/{wiki → content}/extensions/helpers/logger/index.md +32 -2
  62. package/{wiki → content}/extensions/helpers/network/index.md +6 -0
  63. package/{wiki → content}/extensions/helpers/queue/index.md +14 -17
  64. package/content/extensions/helpers/redis/index.md +713 -0
  65. package/{wiki → content}/extensions/helpers/socket-io/index.md +14 -10
  66. package/{wiki → content}/extensions/helpers/storage/api.md +44 -8
  67. package/{wiki → content}/extensions/helpers/storage/index.md +43 -7
  68. package/{wiki → content}/extensions/helpers/template/index.md +6 -3
  69. package/{wiki → content}/extensions/helpers/types/index.md +11 -8
  70. package/{wiki → content}/extensions/helpers/websocket/api.md +9 -9
  71. package/{wiki → content}/extensions/helpers/websocket/index.md +7 -7
  72. package/{wiki → content}/extensions/helpers/worker-thread/index.md +2 -2
  73. package/{wiki → content}/extensions/index.md +3 -4
  74. package/{wiki → content}/extensions/src-details/mcp-server.md +18 -24
  75. package/{wiki → content}/guides/core-concepts/application/bootstrapping.md +11 -14
  76. package/{wiki → content}/guides/core-concepts/application/index.md +3 -3
  77. package/{wiki → content}/guides/core-concepts/components.md +19 -10
  78. package/{wiki → content}/guides/core-concepts/dependency-injection.md +6 -3
  79. package/{wiki → content}/guides/core-concepts/grpc-controllers.md +6 -5
  80. package/{wiki → content}/guides/core-concepts/persistent/datasources.md +33 -27
  81. package/{wiki → content}/guides/core-concepts/persistent/index.md +16 -5
  82. package/{wiki → content}/guides/core-concepts/persistent/models.md +24 -20
  83. package/content/guides/core-concepts/persistent/postgres-drivers.md +167 -0
  84. package/{wiki → content}/guides/core-concepts/persistent/repositories.md +40 -23
  85. package/content/guides/core-concepts/persistent/search-meilisearch.md +183 -0
  86. package/content/guides/core-concepts/persistent/search-typesense.md +429 -0
  87. package/{wiki → content}/guides/core-concepts/persistent/transactions.md +61 -25
  88. package/{wiki → content}/guides/core-concepts/rest-controllers.md +12 -9
  89. package/content/guides/core-concepts/services.md +389 -0
  90. package/{wiki → content}/guides/get-started/5-minute-quickstart.md +19 -19
  91. package/{wiki → content}/guides/get-started/philosophy.md +36 -36
  92. package/{wiki → content}/guides/get-started/setup.md +3 -3
  93. package/{wiki → content}/guides/index.md +3 -3
  94. package/content/guides/migrations/redis-helpers-migration.md +177 -0
  95. package/{wiki → content}/guides/migrations/scoped-rbac-migration.md +17 -17
  96. package/content/guides/migrations/unified-connectors-migration.md +113 -0
  97. package/{wiki → content}/guides/reference/glossary.md +19 -12
  98. package/{wiki → content}/guides/reference/mcp-docs-server.md +22 -18
  99. package/{wiki → content}/guides/tutorials/building-a-crud-api.md +30 -33
  100. package/{wiki → content}/guides/tutorials/complete-installation.md +17 -17
  101. package/{wiki → content}/guides/tutorials/ecommerce-api.md +158 -119
  102. package/{wiki → content}/guides/tutorials/realtime-chat.md +176 -130
  103. package/content/guides/tutorials/testing.md +264 -0
  104. package/content/index.md +5 -0
  105. package/content/public/apple-touch-icon.png +0 -0
  106. package/content/public/og-image.png +0 -0
  107. package/content/public/site.webmanifest +11 -0
  108. package/{wiki → content}/references/base/application.md +4 -5
  109. package/{wiki → content}/references/base/bootstrapping.md +18 -5
  110. package/{wiki → content}/references/base/components.md +149 -120
  111. package/content/references/base/connectors.md +178 -0
  112. package/{wiki → content}/references/base/controllers.md +41 -30
  113. package/content/references/base/datasources.md +527 -0
  114. package/{wiki → content}/references/base/dependency-injection.md +34 -22
  115. package/{wiki → content}/references/base/filter-system/application-usage.md +17 -14
  116. package/{wiki → content}/references/base/filter-system/array-operators.md +7 -2
  117. package/{wiki → content}/references/base/filter-system/comparison-operators.md +3 -0
  118. package/{wiki → content}/references/base/filter-system/default-filter.md +89 -71
  119. package/{wiki → content}/references/base/filter-system/fields-order-pagination.md +22 -22
  120. package/{wiki → content}/references/base/filter-system/index.md +6 -3
  121. package/{wiki → content}/references/base/filter-system/json-filtering.md +20 -1
  122. package/{wiki → content}/references/base/filter-system/list-operators.md +1 -1
  123. package/{wiki → content}/references/base/filter-system/logical-operators.md +33 -1
  124. package/{wiki → content}/references/base/filter-system/null-operators.md +30 -1
  125. package/{wiki → content}/references/base/filter-system/quick-reference.md +23 -4
  126. package/{wiki → content}/references/base/filter-system/tips.md +5 -5
  127. package/{wiki → content}/references/base/filter-system/use-cases.md +12 -12
  128. package/{wiki → content}/references/base/grpc-controllers.md +13 -13
  129. package/{wiki → content}/references/base/index.md +24 -12
  130. package/{wiki/references/base/middleware.md → content/references/base/middlewares.md} +205 -24
  131. package/{wiki → content}/references/base/models.md +63 -49
  132. package/{wiki → content}/references/base/providers.md +136 -130
  133. package/{wiki → content}/references/base/repositories/advanced.md +59 -58
  134. package/{wiki → content}/references/base/repositories/index.md +115 -91
  135. package/content/references/base/repositories/mixins.md +99 -0
  136. package/{wiki → content}/references/base/repositories/relations.md +54 -64
  137. package/{wiki → content}/references/base/repositories/soft-deletable.md +31 -30
  138. package/content/references/base/services.md +404 -0
  139. package/{wiki → content}/references/configuration/environment-variables.md +46 -30
  140. package/{wiki → content}/references/configuration/index.md +6 -6
  141. package/{wiki → content}/references/index.md +17 -12
  142. package/{wiki → content}/references/quick-reference.md +65 -106
  143. package/content/references/utilities/crypto.md +98 -0
  144. package/{wiki → content}/references/utilities/index.md +3 -3
  145. package/{wiki → content}/references/utilities/jsx.md +6 -4
  146. package/content/references/utilities/module.md +90 -0
  147. package/{wiki → content}/references/utilities/parse.md +4 -14
  148. package/{wiki → content}/references/utilities/promise.md +9 -7
  149. package/{wiki → content}/references/utilities/schema.md +5 -3
  150. package/dist/mcp-server/common/guards.d.ts +8 -0
  151. package/dist/mcp-server/common/guards.d.ts.map +1 -0
  152. package/dist/mcp-server/common/guards.js +14 -0
  153. package/dist/mcp-server/common/guards.js.map +1 -0
  154. package/dist/mcp-server/common/index.d.ts +1 -0
  155. package/dist/mcp-server/common/index.d.ts.map +1 -1
  156. package/dist/mcp-server/common/index.js +1 -0
  157. package/dist/mcp-server/common/index.js.map +1 -1
  158. package/dist/mcp-server/common/paths.d.ts.map +1 -1
  159. package/dist/mcp-server/common/paths.js +2 -2
  160. package/dist/mcp-server/common/paths.js.map +1 -1
  161. package/dist/mcp-server/helpers/docs.helper.d.ts.map +1 -1
  162. package/dist/mcp-server/helpers/docs.helper.js +4 -2
  163. package/dist/mcp-server/helpers/docs.helper.js.map +1 -1
  164. package/dist/mcp-server/helpers/github.helper.js +1 -1
  165. package/dist/mcp-server/index.js +7 -2
  166. package/dist/mcp-server/index.js.map +1 -1
  167. package/dist/mcp-server/tools/base.tool.d.ts +6 -2
  168. package/dist/mcp-server/tools/base.tool.d.ts.map +1 -1
  169. package/dist/mcp-server/tools/base.tool.js.map +1 -1
  170. package/dist/mcp-server/tools/docs/search-documents.tool.d.ts +1 -1
  171. package/dist/mcp-server/tools/github/list-project-files.tool.d.ts +1 -1
  172. package/dist/mcp-server/tools/github/search-code.tool.d.ts +1 -1
  173. package/dist/mcp-server/tools/github/search-code.tool.d.ts.map +1 -1
  174. package/dist/mcp-server/tools/github/search-code.tool.js +4 -1
  175. package/dist/mcp-server/tools/github/search-code.tool.js.map +1 -1
  176. package/dist/mcp-server/tools/github/verify-dependencies.tool.d.ts.map +1 -1
  177. package/dist/mcp-server/tools/github/verify-dependencies.tool.js +3 -1
  178. package/dist/mcp-server/tools/github/verify-dependencies.tool.js.map +1 -1
  179. package/package.json +12 -12
  180. package/wiki/extensions/helpers/error/index.md +0 -227
  181. package/wiki/extensions/helpers/redis/index.md +0 -488
  182. package/wiki/extensions/helpers/testing/index.md +0 -510
  183. package/wiki/guides/core-concepts/services.md +0 -119
  184. package/wiki/guides/tutorials/testing.md +0 -722
  185. package/wiki/index.md +0 -183
  186. package/wiki/references/base/datasources.md +0 -454
  187. package/wiki/references/base/middlewares.md +0 -590
  188. package/wiki/references/base/repositories/mixins.md +0 -335
  189. package/wiki/references/base/services.md +0 -201
  190. package/wiki/references/utilities/crypto.md +0 -56
  191. package/wiki/references/utilities/module.md +0 -42
  192. /package/{wiki → content}/extensions/components/mail/api.md +0 -0
  193. /package/{wiki → content}/extensions/components/template/api-page.md +0 -0
  194. /package/{wiki → content}/extensions/components/template/errors-page.md +0 -0
  195. /package/{wiki → content}/extensions/components/template/single-page.md +0 -0
  196. /package/{wiki → content}/extensions/components/template/usage-page.md +0 -0
  197. /package/{wiki → content}/extensions/helpers/kafka/admin.md +0 -0
  198. /package/{wiki → content}/extensions/helpers/kafka/consumer.md +0 -0
  199. /package/{wiki → content}/extensions/helpers/kafka/index.md +0 -0
  200. /package/{wiki → content}/extensions/helpers/kafka/producer.md +0 -0
  201. /package/{wiki → content}/extensions/helpers/kafka/schema-registry.md +0 -0
  202. /package/{wiki → content}/extensions/helpers/network/api.md +0 -0
  203. /package/{wiki → content}/extensions/helpers/socket-io/api.md +0 -0
  204. /package/{wiki → content}/extensions/helpers/template/single-page.md +0 -0
  205. /package/{wiki → content}/extensions/helpers/uid/index.md +0 -0
  206. /package/{wiki → content}/guides/core-concepts/components-guide.md +0 -0
  207. /package/{wiki → content}/public/logo.svg +0 -0
  208. /package/{wiki → content}/references/base/filter-system/pattern-matching.md +0 -0
  209. /package/{wiki → content}/references/base/filter-system/range-operators.md +0 -0
  210. /package/{wiki → content}/references/utilities/date.md +0 -0
  211. /package/{wiki → content}/references/utilities/performance.md +0 -0
  212. /package/{wiki → content}/references/utilities/request.md +0 -0
  213. /package/{wiki → content}/references/utilities/statuses.md +0 -0
@@ -0,0 +1,527 @@
1
+ ---
2
+ title: DataSources Reference
3
+ description: Technical reference for the engine-neutral DataSource contract and the PostgreSQL connector implementation
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # Deep Dive: DataSources
8
+
9
+ Technical reference for DataSource classes - managing database and search engine connections in IGNIS.
10
+
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)
15
+
16
+ ## Quick Reference
17
+
18
+ | Class/Interface | Purpose | Key Members |
19
+ |-----------------|---------|-------------|
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` |
27
+
28
+ ## `IDataSource` Interface
29
+
30
+ Engine-neutral contract implemented by every datasource in the framework, regardless of engine.
31
+
32
+ **File:** `packages/core/src/base/datasources/common/types.ts`
33
+
34
+ ### Type Parameters
35
+
36
+ ```typescript
37
+ interface IDataSource<
38
+ Settings extends object = {},
39
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
40
+ ConfigurableOptions extends object = {},
41
+ > extends IConfigurable<ConfigurableOptions>
42
+ ```
43
+
44
+ | Type Parameter | Default | Description |
45
+ |----------------|---------|-------------|
46
+ | `Settings` | `{}` | Connection configuration type (host, port, etc.) |
47
+ | `Schema` | `TAnyDataSourceSchema` | Combined schema type (shape depends on the connector - Drizzle tables for PostgreSQL, collection definitions for typesense) |
48
+ | `ConfigurableOptions` | `{}` | Options passed to `configure()` |
49
+
50
+ ### Properties & Methods
51
+
52
+ | Member | Type | Description |
53
+ |--------|------|-------------|
54
+ | `name` | `string` | Datasource name |
55
+ | `settings` | `Settings` | Configuration object |
56
+ | `schema` | `Schema` | Combined schema (auto-discovered or manual) |
57
+ | `getSchema()` | `Schema` | Returns combined schema |
58
+ | `getSettings()` | `Settings` | Returns connection settings |
59
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initializes the underlying connection (from `IConfigurable`) |
60
+
61
+ > [!NOTE]
62
+ > `getCapabilities()` and `beginTransaction()` are not part of the `IDataSource` interface - they are declared on `AbstractDataSource` (below), which every connector extends.
63
+
64
+ ## `AbstractDataSource` (Engine-Neutral Root)
65
+
66
+ **File:** `packages/core/src/base/datasources/abstract.ts`
67
+
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.
69
+
70
+ ```typescript
71
+ abstract class AbstractDataSource<
72
+ Settings extends object = {},
73
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
74
+ ConfigurableOptions extends object = {},
75
+ > extends BaseHelper implements IDataSource<Settings, Schema, ConfigurableOptions>
76
+ ```
77
+
78
+ **Properties:**
79
+
80
+ | Property | Type | Visibility | Description |
81
+ |----------|------|------------|-------------|
82
+ | `name` | `string` | public | Datasource identifier |
83
+ | `settings` | `Settings` | public | Connection configuration |
84
+ | `schema` | `Schema` | public | Combined schema |
85
+
86
+ **Abstract methods** (must be implemented by connectors):
87
+
88
+ | Method | Return Type | Description |
89
+ |--------|-------------|-------------|
90
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the underlying connection |
91
+ | `getConnectionString()` | `ValueOrPromise<string>` | Return the connection URL |
92
+
93
+ **Concrete methods (defaults, overridable by connectors):**
94
+
95
+ | Method | Return Type | Default Behavior |
96
+ |--------|-------------|-------------------|
97
+ | `getSettings()` | `Settings` | Returns `this.settings` |
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`
118
+
119
+ ### `AbstractPostgresDataSource`
120
+
121
+ Extends `AbstractDataSource` with PostgreSQL/Drizzle-specific members.
122
+
123
+ ```typescript
124
+ abstract class AbstractPostgresDataSource<
125
+ Settings extends object = {},
126
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
127
+ ConfigurableOptions extends object = {},
128
+ > extends AbstractDataSource<Settings, Schema, ConfigurableOptions>
129
+ ```
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 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: a driver is resolved from 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. The protected `useDriver({ driver, schema? })` assigns `this.driver` **and** builds `this.connector` in one step. `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
+
163
+ #### Key Features
164
+
165
+ | Feature | Description |
166
+ |---------|--------------|
167
+ | **Schema Auto-Discovery** | Schema is automatically built from registered `@repository` decorators |
168
+ | **Manual Override** | You can manually provide schema in constructor for full control |
169
+ | **Built-in Transaction Support** | `beginTransaction()` acquires its connection from the resolved driver; overrides `getCapabilities()` to return `{ transactions: true }` |
170
+
171
+ > [!TIP]
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.
173
+
174
+ ### Constructor Options
175
+
176
+ ```typescript
177
+ constructor(opts: {
178
+ name: string; // DataSource name (usually class name)
179
+ config: Settings; // Database connection settings
180
+ schema?: Schema; // Optional - auto-discovered if not provided
181
+ })
182
+ ```
183
+
184
+ ### Schema Auto-Discovery
185
+
186
+ When you use `@repository({ model: YourModel, dataSource: YourDataSource })`, the framework automatically:
187
+
188
+ 1. Registers the model-datasource binding in the MetadataRegistry
189
+ 2. When `getSchema()` is called, discovers all models bound to this datasource
190
+ 3. Builds the combined schema (tables + relations) automatically
191
+
192
+ **This means you no longer need to manually merge tables and relations in the DataSource constructor!**
193
+
194
+ ### Configuration Flow
195
+
196
+ 1. **Your DataSource's `constructor` is called**:
197
+ - You call `super()` with `name` and `config`
198
+ - Schema is auto-discovered from `@repository` bindings (or manually provided via `schema`)
199
+
200
+ 2. **`Application.registerDataSources()` is called during startup**:
201
+ - The application gets your `DataSource` instance from the DI container
202
+ - It calls the `configure()` method on your instance
203
+
204
+ 3. **Your `configure()` method runs**:
205
+ - Call `this.getSchema()` to get the auto-discovered schema
206
+ - Create a `Pool` instance and assign it to `this.client` (required for transaction support)
207
+ - Create the Drizzle connector from that client and the schema
208
+
209
+ ### Example Implementations
210
+
211
+ #### Pattern 1: Auto-Discovery (Recommended)
212
+
213
+ Simplest approach - schema is auto-discovered from repositories:
214
+
215
+ ```typescript
216
+ // src/datasources/postgres.datasource.ts
217
+ import { BasePostgresDataSource, datasource } from '@venizia/ignis';
218
+ import { applicationEnvironment, int, ValueOrPromise } from '@venizia/ignis-helpers';
219
+ import { drizzle } from 'drizzle-orm/node-postgres';
220
+ import { Pool } from 'pg';
221
+
222
+ interface IDataSourceConfigs {
223
+ host: string;
224
+ port: number;
225
+ database: string;
226
+ user: string;
227
+ password: string;
228
+ ssl: boolean;
229
+ }
230
+
231
+ /**
232
+ * PostgresDataSource with auto-discovery support.
233
+ *
234
+ * How it works:
235
+ * 1. @repository decorator binds model to datasource
236
+ * 2. When configure() is called, getSchema() auto-discovers all bound models
237
+ * 3. Drizzle is initialized with the auto-discovered schema
238
+ */
239
+ @datasource({ driver: 'node-postgres' })
240
+ export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
241
+ private readonly protocol = 'postgresql';
242
+
243
+ constructor() {
244
+ super({
245
+ name: PostgresDataSource.name,
246
+ config: {
247
+ host: applicationEnvironment.get<string>('APP_ENV_POSTGRES_HOST'),
248
+ port: int(applicationEnvironment.get<string>('APP_ENV_POSTGRES_PORT')),
249
+ database: applicationEnvironment.get<string>('APP_ENV_POSTGRES_DATABASE'),
250
+ user: applicationEnvironment.get<string>('APP_ENV_POSTGRES_USERNAME'),
251
+ password: applicationEnvironment.get<string>('APP_ENV_POSTGRES_PASSWORD'),
252
+ ssl: false,
253
+ },
254
+ // NO schema property - auto-discovered from @repository bindings!
255
+ });
256
+ }
257
+
258
+ override configure(): ValueOrPromise<void> {
259
+ // getSchema() auto-discovers models from @repository bindings
260
+ const schema = this.getSchema();
261
+
262
+ const dataSourceSchema = Object.keys(schema);
263
+ this.logger.debug(
264
+ '[configure] Auto-discovered schema | Schema + Relations (%s): %o',
265
+ dataSourceSchema.length,
266
+ dataSourceSchema,
267
+ );
268
+
269
+ // The client slot is what beginTransaction() resolves its driver from
270
+ this.client = new Pool(this.settings);
271
+ this.connector = drizzle({ client: this.client, schema });
272
+ }
273
+
274
+ override getConnectionString(): ValueOrPromise<string> {
275
+ const { host, port, user, password, database } = this.settings;
276
+ return `${this.protocol}://${user}:${password}@${host}:${port}/${database}`;
277
+ }
278
+ }
279
+ ```
280
+
281
+ With this pattern, when you define repositories:
282
+
283
+ ```typescript
284
+ @repository({ model: User, dataSource: PostgresDataSource })
285
+ export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
286
+
287
+ @repository({ model: Configuration, dataSource: PostgresDataSource })
288
+ export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {}
289
+ ```
290
+
291
+ The `PostgresDataSource.schema` will automatically include User and Configuration tables and their relations.
292
+
293
+ #### Pattern 2: Manual Schema (Full Control)
294
+
295
+ When you need explicit control over schema (e.g., subset of models, custom ordering):
296
+
297
+ ```typescript
298
+ import {
299
+ User, userTable, userRelations,
300
+ Configuration, configurationTable, configurationRelations,
301
+ } from '@/models/entities';
302
+
303
+ @datasource({ driver: 'node-postgres' })
304
+ export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
305
+ constructor() {
306
+ super({
307
+ name: PostgresDataSource.name,
308
+ config: {
309
+ host: process.env.APP_ENV_POSTGRES_HOST ?? 'localhost',
310
+ port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
311
+ database: process.env.APP_ENV_POSTGRES_DATABASE ?? 'mydb',
312
+ user: process.env.APP_ENV_POSTGRES_USERNAME ?? 'postgres',
313
+ password: process.env.APP_ENV_POSTGRES_PASSWORD ?? '',
314
+ },
315
+ // Manually provide schema using spread syntax
316
+ schema: {
317
+ [User.TABLE_NAME]: userTable,
318
+ [Configuration.TABLE_NAME]: configurationTable,
319
+ ...userRelations.relations,
320
+ ...configurationRelations.relations,
321
+ },
322
+ });
323
+ }
324
+
325
+ override configure(): ValueOrPromise<void> {
326
+ // When schema is manually provided, getSchema() returns it directly
327
+ this.client = new Pool(this.settings);
328
+ this.connector = drizzle({ client: this.client, schema: this.getSchema() });
329
+ }
330
+
331
+ override getConnectionString(): ValueOrPromise<string> {
332
+ // ...
333
+ }
334
+ }
335
+ ```
336
+
337
+ > [!IMPORTANT]
338
+ > Your `configure()` must leave the datasource with a way to reach the database: either assign the raw client to `this.client`, or wire a driver with `this.useDriver({ driver })`. `beginTransaction()` resolves a driver lazily from whichever you provided. With neither, it throws `No driver and no client`.
339
+
340
+ ### `@datasource` Decorator
341
+
342
+ The `@datasource` decorator registers datasource metadata:
343
+
344
+ ```typescript
345
+ @datasource({
346
+ driver: 'node-postgres', // Required - database driver
347
+ autoDiscovery?: true // Optional - defaults to true
348
+ })
349
+ ```
350
+
351
+ | Option | Type | Default | Description |
352
+ |--------|------|---------|-------------|
353
+ | `driver` | `TDataSourceDriver` | - | Driver name - `DataSourceDrivers` defines four constants: `'node-postgres'` and `'postgres-js'` (relational), `'typesense'` and `'meilisearch'` (search); any other engine-driver string is also accepted |
354
+ | `autoDiscovery` | `boolean` | `true` | Enable/disable schema auto-discovery |
355
+
356
+ ### Abstract Methods
357
+
358
+ When extending `BasePostgresDataSource`, these methods must be implemented:
359
+
360
+ | Method | Return Type | Description |
361
+ |--------|-------------|-------------|
362
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the client and Drizzle connector. Must set `this.client` (or call `this.useDriver()`) and `this.connector`. |
363
+ | `getConnectionString()` | `ValueOrPromise<string>` | Return the database connection string. |
364
+
365
+ ### Helper Methods
366
+
367
+ | Method | Description |
368
+ |--------|-------------|
369
+ | `getSchema()` | Returns the schema (auto-discovers via `discoverSchema()` if not manually provided) |
370
+ | `getSettings()` | Returns connection settings |
371
+ | `getConnector()` | Returns the Drizzle connector |
372
+ | `hasDiscoverableModels()` | Returns `true` if there are models registered for this datasource via `@repository` |
373
+
374
+ ### Protected Methods
375
+
376
+ | Method | Description |
377
+ |--------|-------------|
378
+ | `discoverSchema()` | Queries the `MetadataRegistry` for all `@repository` bindings targeting this datasource, then calls `registry.buildSchema()` to merge tables and relations into a single schema object. |
379
+
380
+ ## Connector Types
381
+
382
+ **File:** `packages/core/src/connectors/postgres/datasources/common/types.ts`
383
+
384
+ | Type | Description |
385
+ |------|-------------|
386
+ | `TRelationalConnector<Schema>` | Canonical connector type - a Drizzle `PgDatabase` that **every** pg driver (`node-postgres`, `postgres-js`) satisfies. Use this in new code. |
387
+ | `TNodePostgresConnector<Schema>` | **`@deprecated`** compat alias for `TRelationalConnector<Schema>` |
388
+ | `TNodePostgresTransactionConnector<Schema>` | **`@deprecated`** compat alias - was the `PoolClient`-specific transaction connector; now aliases `TRelationalConnector<Schema>` |
389
+ | `TAnyConnector<Schema>` | Alias of `TRelationalConnector<Schema>` |
390
+ | `TAnyDataSourceSchema` | `Record<string, any>` - base type for all schema objects (defined in `src/base/datasources/common/types.ts`, shared across engines) |
391
+
392
+ ### `DataSourceDrivers`
393
+
394
+ Static class for driver validation (defined in `src/base/datasources/common/types.ts`, shared across engines):
395
+
396
+ ```typescript
397
+ DataSourceDrivers.NODE_POSTGRES // 'node-postgres'
398
+ DataSourceDrivers.POSTGRES_JS // 'postgres-js'
399
+ DataSourceDrivers.TYPESENSE // 'typesense'
400
+ DataSourceDrivers.MEILISEARCH // 'meilisearch'
401
+ DataSourceDrivers.isValid('node-postgres') // true
402
+ ```
403
+
404
+ ## Transaction Support
405
+
406
+ 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).
407
+
408
+ ### How It Works
409
+
410
+ `BasePostgresDataSource.beginTransaction()` does the following:
411
+
412
+ 1. Resolves a driver from `this.client` (or the one `useDriver()` wired) and acquires a connection from it
413
+ 2. Executes `BEGIN TRANSACTION ISOLATION LEVEL <level>` on the client
414
+ 3. Creates a separate Drizzle connector scoped to that client
415
+ 4. Returns an `IDatabaseTransaction` object with `commit()`, `rollback()`, and the scoped `connector`
416
+
417
+ When `commit()` or `rollback()` succeeds, the client is released back to the pool.
418
+
419
+ > [!WARNING] `commit()`/`rollback()` throw on failure
420
+ > 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).
421
+
422
+ ### Neutral vs. PostgreSQL Transaction Types
423
+
424
+ `src/base` declares the engine-neutral shape; the PostgreSQL connector narrows it with connection details.
425
+
426
+ ```typescript
427
+ // packages/core/src/base/datasources/common/types.ts - engine-neutral
428
+ interface ITransaction<_Schema = unknown> {
429
+ isActive: boolean;
430
+ commit(): Promise<void>;
431
+ rollback(): Promise<void>;
432
+ }
433
+
434
+ // packages/core/src/connectors/postgres/datasources/common/types.ts - PostgreSQL
435
+ interface IDatabaseTransaction<Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema>
436
+ extends ITransaction<Schema> {
437
+ connector: TRelationalConnector<Schema>;
438
+ isolationLevel: TIsolationLevel;
439
+ }
440
+ ```
441
+
442
+ | Type | Description |
443
+ |------|-------------|
444
+ | `ITransaction<Schema>` | Engine-neutral contract - `isActive`, `commit()`, `rollback()`. No connector field. |
445
+ | `IDatabaseTransaction<Schema>` | PostgreSQL transaction object - extends `ITransaction` with `connector` and `isolationLevel` |
446
+ | `IDatabaseTransactionOptions` | Options for starting a PostgreSQL transaction (`isolationLevel`); extends the neutral `ITransactionOptions` |
447
+ | `IDatabaseExtraOptions` | Extends the neutral `IExtraOptions`, narrowing `transaction?: IDatabaseTransaction` |
448
+ | `TIsolationLevel` | Union type: `'READ COMMITTED'` \| `'REPEATABLE READ'` \| `'SERIALIZABLE'` |
449
+ | `IsolationLevels` | Static class with isolation level constants and validation |
450
+
451
+ > [!NOTE]
452
+ > `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`.
453
+
454
+ ### Isolation Levels
455
+
456
+ Use the `IsolationLevels` static class for type-safe isolation level constants:
457
+
458
+ ```typescript
459
+ import { IsolationLevels } from '@venizia/ignis';
460
+
461
+ // Available levels
462
+ IsolationLevels.READ_COMMITTED // Default - prevents dirty reads
463
+ IsolationLevels.REPEATABLE_READ // Consistent reads within transaction
464
+ IsolationLevels.SERIALIZABLE // Strictest isolation
465
+
466
+ // Validation
467
+ IsolationLevels.isValid('READ COMMITTED'); // true
468
+ IsolationLevels.isValid('INVALID'); // false
469
+ ```
470
+
471
+ > [!NOTE]
472
+ > The default isolation level is `READ COMMITTED` when no `isolationLevel` option is provided.
473
+
474
+ ### Usage Example
475
+
476
+ ```typescript
477
+ // Start transaction from datasource or repository
478
+ const tx = await dataSource.beginTransaction({
479
+ isolationLevel: IsolationLevels.SERIALIZABLE
480
+ });
481
+
482
+ try {
483
+ // Use tx.connector for operations
484
+ await tx.connector.insert(userTable).values({ name: 'Alice' });
485
+ await tx.connector.insert(profileTable).values({ userId: '...', bio: 'Hello' });
486
+
487
+ await tx.commit();
488
+ } catch (error) {
489
+ // rollback() throws if ROLLBACK itself fails - nest it so it never replaces the original cause
490
+ try {
491
+ await tx.rollback();
492
+ } catch (rollbackError) {
493
+ logger.error('Rollback failed | %s', rollbackError);
494
+ }
495
+ throw error;
496
+ }
497
+ ```
498
+
499
+ > **Note:** For most use cases, prefer using `repository.beginTransaction()` which provides a higher-level API. See [Repositories Reference](./repositories/#transactions) for details.
500
+
501
+ This architecture ensures that datasources are configured consistently and that the fully-initialized Drizzle connector, aware of all schemas and relations, is available to repositories for querying.
502
+
503
+ ## See Also
504
+
505
+ - **Related Concepts:**
506
+ - [Connectors](./connectors) - Base-vs-connectors architecture, dual-door exports, aliases
507
+ - [DataSources Guide](/guides/core-concepts/persistent/datasources) - Creating DataSources tutorial
508
+ - [Repositories](/guides/core-concepts/persistent/repositories) - Using DataSources for database access
509
+ - [Models](/guides/core-concepts/persistent/models) - Entity schemas loaded by DataSource
510
+ - [Transactions](/guides/core-concepts/persistent/transactions) - Multi-operation database transactions
511
+ - [Search & Typesense](/guides/core-concepts/persistent/search-typesense) - The typesense connector
512
+
513
+ - **References:**
514
+ - [Repositories API](/references/base/repositories/) - Data access layer
515
+ - [Environment Variables](/references/configuration/environment-variables) - Configuration management
516
+
517
+ - **External Resources:**
518
+ - [Drizzle ORM Documentation](https://orm.drizzle.team/) - ORM configuration
519
+ - [node-postgres Documentation](https://node-postgres.com/) - Connection pooling guide
520
+
521
+ - **Best Practices:**
522
+ - [Performance Optimization](/best-practices/performance-optimization) - Connection pool tuning
523
+ - [Security Guidelines](/best-practices/security-guidelines) - Database credential management
524
+
525
+ - **Tutorials:**
526
+ - [Complete Installation](/guides/tutorials/complete-installation) - Database setup
527
+ - [Building a CRUD API](/guides/tutorials/building-a-crud-api) - DataSource configuration
@@ -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