@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
@@ -0,0 +1,178 @@
1
+ ---
2
+ title: Connectors Reference
3
+ description: How IGNIS separates engine-neutral base classes from per-engine connectors, dual-door exports, and adding a new engine
4
+ difficulty: advanced
5
+ ---
6
+
7
+ # Deep Dive: Connectors
8
+
9
+ IGNIS's persistence layer is split into two layers: an **engine-neutral root** and a set of **connectors**, one per storage engine. This page documents that architecture, the dual-door export strategy, naming symmetry between engines, and how to add a new connector.
10
+
11
+ **Files:** `packages/core/src/base/**` (neutral) and `packages/core/src/connectors/{postgres,typesense,meilisearch,search}/**` (per-engine)
12
+
13
+ ## Why the split
14
+
15
+ Before the restructure, `AbstractDataSource`/`AbstractRepository`/`AbstractEntity` were PostgreSQL-and-Drizzle-aware from the start - `pool`, `connector` (Drizzle instance), and SQL-shaped `TWhere`/`TFilter` lived directly in `src/base`. Adding typesense (a document search engine, not a relational database) exposed the coupling: it has no connection pool, doesn't speak SQL, and doesn't support transactions or row-level locking.
16
+
17
+ The fix moves everything engine-specific out of `src/base` and into `src/connectors/<engine>/`, leaving `src/base` with only what every engine can implement:
18
+
19
+ | Concept | Neutral (`src/base`) | PostgreSQL (`connectors/postgres`) | Typesense (`connectors/typesense`) |
20
+ |---|---|---|---|
21
+ | DataSource | `AbstractDataSource` | `AbstractPostgresDataSource` → `BasePostgresDataSource` | `AbstractSearchDataSource` → `BaseSearchDataSource` → `TypesenseDataSource` |
22
+ | Entity | `AbstractEntity` | `BasePostgresEntity` (alias `BaseEntity`) | `BaseSearchEntity` |
23
+ | Repository | `AbstractRepository` | `PostgresBaseRepository` → `ReadableRepository`/`PersistableRepository`/`DefaultCRUDRepository`/`SoftDeletableRepository` | `TypesenseBaseRepository` → `ReadableSearchRepository`/`PersistableSearchRepository`/`DefaultSearchRepository` |
24
+ | Transactions | throws `NotSupported` by default | real transactions, 3 isolation levels | throws `NotSupported` |
25
+ | Filtering | neutral `TWhere`/`TFilter` | SQL WHERE via Drizzle | typesense query DSL via `TypesenseQueryDialect` |
26
+
27
+ ## The neutral contract
28
+
29
+ ### `AbstractDataSource`
30
+
31
+ `packages/core/src/base/datasources/abstract.ts` declares:
32
+
33
+ ```typescript
34
+ abstract class AbstractDataSource<
35
+ Settings extends object = {},
36
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
37
+ ConfigurableOptions extends object = {},
38
+ > extends BaseHelper implements IDataSource<Settings, Schema, ConfigurableOptions> {
39
+ abstract configure(opts?: ConfigurableOptions): ValueOrPromise<void>;
40
+
41
+ getCapabilities(): IDataSourceCapabilities {
42
+ return { transactions: false };
43
+ }
44
+
45
+ async beginTransaction(_opts?: ITransactionOptions): Promise<ITransaction> {
46
+ return throwNotSupported({
47
+ scope: this.constructor.name,
48
+ feature: 'Transactions',
49
+ logger: this.logger,
50
+ });
51
+ }
52
+ }
53
+ ```
54
+
55
+ No `pool`, no `connector`, no SQL types. Every engine either overrides `getCapabilities()`/`beginTransaction()` (PostgreSQL) or accepts the neutral default (typesense).
56
+
57
+ ### `AbstractEntity`
58
+
59
+ `packages/core/src/base/models/base.ts` declares the minimal entity contract, including `getIdType()`:
60
+
61
+ ```typescript
62
+ abstract class AbstractEntity extends BaseHelper {
63
+ getIdType(): TIdSchemaType {
64
+ return 'string';
65
+ }
66
+ }
67
+ ```
68
+
69
+ `BasePostgresEntity` overrides `getIdType()` based on the Drizzle column's actual data type; other engines can override it per their own ID representation.
70
+
71
+ ### `AbstractRepository`
72
+
73
+ `packages/core/src/base/repositories/core/abstract.ts`:
74
+
75
+ ```typescript
76
+ abstract class AbstractRepository<
77
+ TDataObject extends object,
78
+ TPersistObject extends object = TDataObject,
79
+ TOptions extends IExtraOptions = IExtraOptions,
80
+ > extends BaseHelper implements IPersistableRepository<TDataObject, TPersistObject, TOptions>
81
+ ```
82
+
83
+ Notice the generic parameters are named for their role (`TDataObject`, `TPersistObject`, `TOptions`), not for Drizzle concepts (`EntitySchema`, `TTableObject`) - those Drizzle-flavored names now belong to `PostgresBaseRepository` and its subclasses, which narrow `AbstractRepository`'s generics into a `TTableSchemaWithId`-based signature. `AbstractRepository` does **not** compose `FieldsVisibilityMixin`/`DefaultFilterMixin` - see [Repository Mixins](./repositories/mixins) for what happened to those.
84
+
85
+ ### Neutral `TFilter`/`TWhere` and `ITransaction`
86
+
87
+ `packages/core/src/base/repositories/common/types.ts` and `packages/core/src/base/datasources/common/types.ts` define engine-neutral filter and transaction shapes shared by both connectors. `ITransaction` has no `connector` field - each engine that supports transactions (currently only PostgreSQL) narrows it with `IDatabaseTransaction`, whose `connector` is a `TRelationalConnector<Schema>` bound to the transaction's dedicated connection (the older `TNodePostgresTransactionConnector` name is kept as a `@deprecated` alias). Its `commit()`/`rollback()` **throw** on failure (a failed `COMMIT` no longer resolves as success) and destroy the poisoned connection rather than pooling it under the `node-postgres` driver; a failed `BEGIN` likewise destroys the acquired connection instead of leaking it. See [Transactions](/guides/core-concepts/persistent/transactions).
88
+
89
+ ## Dual-door exports
90
+
91
+ IGNIS ships both a **root barrel** (backward-compatible default) and **per-engine subpaths** (opt-in, tree-shakeable). Both resolve to the exact same compiled classes - there is no duplicated implementation.
92
+
93
+ ```json
94
+ // packages/core/package.json - exports map (persistence-relevant entries)
95
+ {
96
+ ".": "dist/index.js", // framework + postgres (compatibility default)
97
+ "./postgres": "dist/connectors/postgres/index.js", // relational connector - loads ZERO client library
98
+ "./postgres/node-postgres": "dist/connectors/postgres/drivers/node-postgres.js", // concrete driver (value-imports `pg`)
99
+ "./postgres/postgres-js": "dist/connectors/postgres/drivers/postgres-js.js", // concrete driver (value-imports `postgres`)
100
+ "./postgres/supabase": "dist/connectors/postgres/supabase/index.js", // Supabase pooler + RLS helpers
101
+ "./search": "dist/connectors/search/index.js", // engine-neutral search paradigm (no engine client)
102
+ "./typesense": "dist/connectors/typesense/index.js", // subpath-only, optional peer
103
+ "./meilisearch": "dist/connectors/meilisearch/index.js" // subpath-only, optional peer
104
+ }
105
+ ```
106
+
107
+ > [!IMPORTANT] Concrete Postgres drivers are sub-path only
108
+ > `pg` and `postgres` are **both optional peer dependencies** (`peerDependenciesMeta.pg.optional` / `.postgres.optional`). The root barrel, `@venizia/ignis/postgres`, and the drivers barrel (`connectors/postgres/drivers/index.ts`) load **zero** `pg`/`postgres` modules - the drivers barrel exports only the neutral `IRelationalDriver` contract. A concrete driver value-imports its own client, so reach it by its own sub-path only: `@venizia/ignis/postgres/node-postgres` or `@venizia/ignis/postgres/postgres-js`. There is no structural client sniffing: `@datasource({ driver })` names the driver **class** directly (`NodePostgresDriver`/`PostgresJsDriver`), and the base datasource's `wireDriverFromMetadata()` instantiates that named class over `this.client` lazily, on first `getConnector()`/`beginTransaction()`. Naming a class rather than a driver-name string is what carries `pg`/`postgres` into the app's bundle - a bundler only packages a real value reference, never text, and a bare side-effect import would not survive `sideEffects: false`. This is what keeps IGNIS from forcing a database client on a project that does not use one. See [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers).
109
+
110
+ > [!NOTE] `internal/` modules are not public API
111
+ > The search connectors (`search`/`typesense`/`meilisearch`) no longer re-export their `internal/` barrels (`SearchConnectorInternal`, `TypesenseInternal`, `MeilisearchInternal`) from the connector barrel. There is no sub-path export for them either, and package `exports` blocks deep imports - so no specifier reaches them from outside the package. They are implementation detail; if you need what they do, ask for a supported API.
112
+
113
+ ```typescript
114
+ // packages/core/src/index.ts - root barrel
115
+ export * from './base';
116
+ export * from './common';
117
+ export * from './components';
118
+ export * from './connectors'; // -> connectors/index.ts: export * from './postgres';
119
+ export * from './helpers';
120
+ export * from './utilities';
121
+ ```
122
+
123
+ - **Root (`@venizia/ignis`)** re-exports the framework plus the `postgres` connector - existing code that imports `BaseDataSource`/`DefaultCRUDRepository` from the root keeps working unchanged.
124
+ - **`@venizia/ignis/postgres`** re-exports the same connector in isolation, for code that wants to be explicit about which engine it depends on.
125
+ - **`@venizia/ignis/typesense`** is **subpath-only** - it is deliberately excluded from the root barrel because `typesense` is an optional peer dependency (`"peerDependenciesMeta": { "typesense": { "optional": true } }`). Importing from the root must never force-install the typesense client for apps that don't use search.
126
+
127
+ ```typescript
128
+ // Both of these resolve to the same BasePostgresDataSource class:
129
+ import { BaseDataSource } from '@venizia/ignis';
130
+ import { BasePostgresDataSource } from '@venizia/ignis/postgres';
131
+
132
+ // Typesense must be imported from its subpath - not available at the root:
133
+ import { TypesenseDataSource, defineSearchCollection } from '@venizia/ignis/typesense';
134
+ ```
135
+
136
+ ## Naming symmetry and compatibility aliases
137
+
138
+ Each connector's canonical class names carry the engine name (`BasePostgresDataSource`, `BasePostgresEntity`), matching the pattern typesense follows (`TypesenseDataSource`). PostgreSQL additionally re-exports the pre-restructure names as aliases, so existing imports do not break:
139
+
140
+ ```typescript
141
+ // packages/core/src/connectors/postgres/datasources/index.ts
142
+ export { BasePostgresDataSource as BaseDataSource } from './base-datasource';
143
+
144
+ // packages/core/src/connectors/postgres/models/index.ts
145
+ export { BasePostgresEntity as BaseEntity } from './base';
146
+ ```
147
+
148
+ | Canonical (new, engine-carrying) | Alias (compatibility) | Notes |
149
+ |---|---|---|
150
+ | `BasePostgresDataSource` | `BaseDataSource` | Same class, re-exported |
151
+ | `BasePostgresEntity` | `BaseEntity` | Same class, re-exported |
152
+
153
+ Write new code against the canonical names - they make it unambiguous which engine a class belongs to once you have more than one connector in play. Existing code using the aliases needs no changes.
154
+
155
+ ## Adding a new engine connector
156
+
157
+ To add a third connector (e.g. a Redis-backed cache connector), follow the shape the two existing connectors share:
158
+
159
+ 1. **`src/connectors/<engine>/datasources/`** - `Abstract<Engine>DataSource extends AbstractDataSource`, then `Base<Engine>DataSource` (or a single concrete `<Engine>DataSource` if there's no useful abstract/base split) implementing `configure()`/`getConnectionString()`. Override `getCapabilities()` and `beginTransaction()` only if the engine truly supports transactions - otherwise inherit the neutral `NotSupported` default.
160
+ 2. **`src/connectors/<engine>/models/`** (if the engine needs entity definitions beyond plain objects) - extend `AbstractEntity`, override `getIdType()` if the engine's ID representation differs from the neutral default.
161
+ 3. **`src/connectors/<engine>/repositories/core/`** - `<Engine>BaseRepository extends AbstractRepository`, narrowing the generics to the engine's data/persist/options shapes, followed by a `Readable`/`Persistable`/`DefaultCRUD`-style tier ladder mirroring the postgres and typesense connectors. Any operation the engine cannot support (transactions, locks) should call `throwNotSupported({ scope, feature, logger: this.logger })` for consistency.
162
+ 4. **`src/connectors/<engine>/index.ts`** - barrel export for the connector.
163
+ 5. **`src/connectors/index.ts`** - add `export * from './<engine>'` **only if** the engine has no optional runtime peer dependency (mirroring `postgres`). If the engine driver is an optional peer (like `typesense`), leave it out of `connectors/index.ts` and register it as a subpath-only export in `package.json`.
164
+ 6. **`package.json` `exports`** - add `"./​<engine>": "dist/connectors/<engine>/index.js"`. If the driver is an optional peer, add it to `peerDependencies` + `peerDependenciesMeta.<driver>.optional: true`.
165
+
166
+ ## See Also
167
+
168
+ - **Related Concepts:**
169
+ - [DataSources](./datasources) - Full DataSource reference (neutral contract + PostgreSQL connector)
170
+ - [Models & Enrichers](./models) - Entity reference (neutral `AbstractEntity` + PostgreSQL `BasePostgresEntity`)
171
+ - [Repositories](./repositories/) - Repository hierarchy reference
172
+ - [Repository Mixins](./repositories/mixins) - Legacy mixins, now orphaned from the public API
173
+
174
+ - **Guides:**
175
+ - [Search & Typesense](/guides/core-concepts/persistent/search-typesense) - The typesense connector in depth
176
+
177
+ - **Best Practices:**
178
+ - [Architectural Patterns](/best-practices/architectural-patterns) - Layered architecture and separation of concerns
@@ -6,7 +6,7 @@ difficulty: beginner
6
6
 
7
7
  # Deep Dive: REST Controllers
8
8
 
9
- Technical reference for REST controller classes - the foundation for creating HTTP/JSON API endpoints in Ignis.
9
+ Technical reference for REST controller classes - the foundation for creating HTTP/JSON API endpoints in IGNIS.
10
10
 
11
11
  > [!NOTE]
12
12
  > This page covers **REST controllers** (HTTP/JSON). For gRPC controllers using ConnectRPC, see the [gRPC Controllers Reference](./grpc-controllers.md).
@@ -32,7 +32,7 @@ Technical reference for REST controller classes - the foundation for creating HT
32
32
 
33
33
  ## Controller Transport System
34
34
 
35
- Ignis supports multiple controller transports. The `@controller` decorator accepts a `transport` field to distinguish between REST and gRPC controllers.
35
+ IGNIS supports multiple controller transports. The `@controller` decorator accepts a `transport` field to distinguish between REST and gRPC controllers.
36
36
 
37
37
  ### `ControllerTransports`
38
38
 
@@ -57,7 +57,7 @@ interface IBaseControllerMetadata {
57
57
  }
58
58
 
59
59
  interface IRestControllerMetadata extends IBaseControllerMetadata {
60
- transport?: typeof ControllerTransports.REST; // Optional defaults to REST
60
+ transport?: typeof ControllerTransports.REST; // Optional - defaults to REST
61
61
  }
62
62
 
63
63
  interface IGrpcControllerMetadata<ServiceType = unknown> extends IBaseControllerMetadata {
@@ -68,7 +68,7 @@ interface IGrpcControllerMetadata<ServiceType = unknown> extends IBaseController
68
68
  type TControllerMetadata = IRestControllerMetadata | IGrpcControllerMetadata;
69
69
  ```
70
70
 
71
- REST controllers do not need to specify `transport` explicitly it defaults to REST when omitted.
71
+ REST controllers do not need to specify `transport` explicitly - it defaults to REST when omitted.
72
72
 
73
73
  ### Application Transport Configuration
74
74
 
@@ -130,7 +130,7 @@ export class RestComponent extends BaseComponent {
130
130
 
131
131
  ## `AbstractRestController`
132
132
 
133
- Base class integrating Hono routing with Ignis DI, authentication/authorization middleware, and OpenAPI generation.
133
+ Base class integrating Hono routing with IGNIS DI, authentication/authorization middleware, and OpenAPI generation.
134
134
 
135
135
  **Generic Parameters:**
136
136
 
@@ -153,7 +153,7 @@ constructor(opts: IControllerOptions)
153
153
  | Option | Type | Default | Description |
154
154
  | :--- | :--- | :--- | :--- |
155
155
  | `scope` | `string` | Required | Logger scope name |
156
- | `path` | `string` | | Route base path. Falls back to `@controller` decorator path if not provided |
156
+ | `path` | `string` | - | Route base path. Falls back to `@controller` decorator path if not provided |
157
157
  | `isStrict` | `boolean` | `true` | When `true`, `/users` and `/users/` are different routes |
158
158
 
159
159
  Path resolution priority: `@controller` decorator metadata > constructor `path` option. Throws if neither provides a path.
@@ -171,7 +171,7 @@ Path resolution priority: `@controller` decorator metadata > constructor `path`
171
171
 
172
172
  #### `configure(opts?): Promise<OpenAPIHono>`
173
173
 
174
- Configures the controller. Idempotent returns the router immediately if already configured.
174
+ Configures the controller. Idempotent - returns the router immediately if already configured.
175
175
 
176
176
  1. Calls `binding()` (your manual route definitions)
177
177
  2. Calls `registerRoutesFromRegistry()` (decorator-based routes)
@@ -281,7 +281,11 @@ interface IControllerOptions {
281
281
  Lightweight typed context that provides type-safe `req.valid()` calls:
282
282
 
283
283
  ```typescript
284
- type TRouteContext<RouteEnv extends Env = Env> = TContext<RouteEnv, keyof IValidRequestProps>;
284
+ type TRouteContext<RouteEnv extends Env = Env, ResponseBody = unknown> = TContext<
285
+ RouteEnv,
286
+ keyof IValidRequestProps,
287
+ ResponseBody
288
+ >;
285
289
  ```
286
290
 
287
291
  Where `IValidRequestProps` supports: `json`, `query`, `param`, `header`, `cookie`, `form`.
@@ -290,7 +294,7 @@ Where `IValidRequestProps` supports: `json`, `query`, `param`, `header`, `cookie
290
294
 
291
295
  ```typescript
292
296
  type TRouteHandler<ResponseType = unknown, RouteEnv extends Env = Env> = (
293
- context: TRouteContext<RouteEnv>,
297
+ context: TRouteContext<RouteEnv, ResponseType>,
294
298
  ) => ValueOrPromise<Response | TypedResponse<ResponseType>>;
295
299
  ```
296
300
 
@@ -361,6 +365,8 @@ Per-route customization for CRUD controller endpoints:
361
365
 
362
366
  ```typescript
363
367
  type TCustomizableRouteConfig = TRouteAuthConfig & {
368
+ /** Whether this route is registered. Defaults to true. */
369
+ enabled?: boolean;
364
370
  request?: {
365
371
  params?: TAnyObjectSchema;
366
372
  query?: TAnyObjectSchema;
@@ -416,7 +422,8 @@ export class UserController extends BaseRestController { ... }
416
422
  Generic route decorator. Registers route config in the metadata registry.
417
423
 
418
424
  ```typescript
419
- import { api, BaseRestController, controller, jsonResponse, z, TRouteContext } from '@venizia/ignis';
425
+ import { z } from '@hono/zod-openapi';
426
+ import { api, BaseRestController, controller, jsonResponse, TRouteContext } from '@venizia/ignis';
420
427
  import { HTTP } from '@venizia/ignis-helpers';
421
428
 
422
429
  const MyRouteConfig = {
@@ -448,7 +455,8 @@ Each decorator calls `@api` internally with the appropriate `HTTP.Methods.*` val
448
455
  **Example using `@get` and `@post`:**
449
456
 
450
457
  ```typescript
451
- import { get, post, z, jsonContent, jsonResponse, Authentication, TRouteContext } from '@venizia/ignis';
458
+ import { z } from '@hono/zod-openapi';
459
+ import { get, post, jsonContent, jsonResponse, Authentication, TRouteContext } from '@venizia/ignis';
452
460
  import { HTTP } from '@venizia/ignis-helpers';
453
461
 
454
462
  const UserRoutes = {
@@ -508,12 +516,12 @@ const UserRoutes = {
508
516
 
509
517
  - The `binding()` method is not required if you use only decorator-based routing
510
518
  - Routes are discovered and registered during `configure()` via `registerRoutesFromRegistry()`
511
- - TypeScript automatically infers and validates return types against the OpenAPI response schema no need for explicit `TRouteResponse` annotations
519
+ - TypeScript automatically infers and validates return types against the OpenAPI response schema - no need for explicit `TRouteResponse` annotations
512
520
  - Use `as const` on route config objects for strict type inference
513
521
 
514
522
  ## Manual Route Definition
515
523
 
516
- For advanced use cases dynamic routes, feature flags, programmatic control define routes inside `binding()`.
524
+ For advanced use cases - dynamic routes, feature flags, programmatic control - define routes inside `binding()`.
517
525
 
518
526
  ### `defineRoute` Example
519
527
 
@@ -524,7 +532,7 @@ this.defineRoute({
524
532
  path: '/status',
525
533
  responses: jsonResponse({ schema: z.object({ ok: z.boolean() }) }),
526
534
  authenticate: { strategies: ['jwt'] },
527
- authorize: { resource: 'status', scopes: ['read'] },
535
+ authorize: { action: 'read', resource: 'status' },
528
536
  },
529
537
  handler: async (context) => {
530
538
  return context.json({ ok: true }, 200);
@@ -626,21 +634,21 @@ class RestPaths {
626
634
 
627
635
  | Constant | Headers Included |
628
636
  | :--- | :--- |
629
- | `trackableHeaders` | `x-request-id`, `x-request-channel`, `x-request-device-info` (all optional) |
630
- | `countableHeaders` | `x-request-count` controls `{count, data}` vs data-only response format |
637
+ | `trackableHeaders` | `x-request-id`, `x-request-channel`, `x-device-info` (all optional) |
638
+ | `countableHeaders` | `x-request-count` - controls `{count, data}` vs data-only response format |
631
639
  | `defaultRequestHeaders` | `trackableHeaders` + `countableHeaders` combined |
632
640
  | `commonResponseHeaders` | `x-request-id` (echo), `x-response-count`, `x-response-format` |
633
641
  | `findResponseHeaders` | `commonResponseHeaders` + `content-range` for pagination |
634
642
 
635
643
  ## `ControllerFactory`
636
644
 
637
- The `ControllerFactory` provides a static method `defineCrudController` to quickly generate a pre-configured CRUD controller for any given `BaseEntity` and its corresponding repository.
645
+ The `ControllerFactory` provides a static method `defineCrudController` to quickly generate a pre-configured CRUD controller for any given `AbstractEntity` subclass (e.g. `BaseEntity` for Postgres) and its corresponding repository.
638
646
 
639
647
  **File:** `packages/core/src/base/controllers/factory/controller.ts`
640
648
 
641
- ### `static defineCrudController<EntitySchema>(opts: ICrudControllerOptions<EntitySchema>)`
649
+ ### `static defineCrudController<TDataObject, TPersistObject = TDataObject, Routes extends ICustomizableRoutes = ICustomizableRoutes>(opts: ICrudControllerOptions<Routes>)`
642
650
 
643
- Returns a `BaseRestController` subclass with standard CRUD endpoints pre-configured. The returned class is dynamically named using `controller.name` from the options.
651
+ Returns a `BaseRestController` subclass with standard CRUD endpoints pre-configured. The returned class is dynamically named using `controller.name` from the options. `TDataObject`/`TPersistObject` can't be inferred from `entity` - pass them explicitly for typed fields.
644
652
 
645
653
  | Route Name | Method | Path | Description |
646
654
  | :--- | :--- | :--- | :--- |
@@ -654,15 +662,16 @@ Returns a `BaseRestController` subclass with standard CRUD endpoints pre-configu
654
662
  | `deleteById` | `DELETE` | `/{id}` | Delete a record by its ID |
655
663
  | `deleteBy` | `DELETE` | `/` | Bulk delete records matching a `where` filter |
656
664
 
657
- ### `ICrudControllerOptions<EntitySchema>`
665
+ ### `ICrudControllerOptions<Routes>`
658
666
 
659
667
  | Option | Type | Description |
660
668
  | :--- | :--- | :--- |
661
- | `entity` | `TClass<BaseEntity<EntitySchema>> \| TResolver<TClass<BaseEntity<EntitySchema>>>` | Entity class or resolver function returning it. Used to derive request/response schemas |
669
+ | `entity` | `TClass<AbstractEntity> \| TResolver<TClass<AbstractEntity>>` | Entity class or resolver function returning it. Used to derive request/response schemas |
662
670
  | `repository.name` | `string` | Repository binding key name in the IoC container (e.g., `'ConfigurationRepository'`) |
663
671
  | `controller.name` | `string` | Unique name for the generated controller (e.g., `'ConfigurationController'`) |
664
672
  | `controller.basePath` | `string` | Base path for all routes (e.g., `'/configurations'`). Required |
665
673
  | `controller.readonly` | `boolean` | If `true`, only read operations (count, find, findOne, findById) are generated. Defaults to `false` |
674
+ | `controller.enabledRoutes` | `Array<keyof ICustomizableRoutes>` | Whitelist of routes to register; overrides per-route `enabled` flags in `routes` when set |
666
675
  | `controller.isStrict` | `{ path?: boolean; requestSchema?: boolean }` | `path` (default `true`): strict path matching. `requestSchema` (default `true`): strict query parameter validation |
667
676
  | `authenticate` | `{ strategies?: TAuthStrategy[]; mode?: TAuthMode }` | Authentication config applied to all routes (unless overridden per-route) |
668
677
  | `authorize` | `IAuthorizationSpec \| IAuthorizationSpec[]` | Authorization config applied to all routes (unless overridden per-route) |
@@ -679,6 +688,8 @@ type TRouteAuthConfig = {
679
688
  };
680
689
 
681
690
  type TCustomizableRouteConfig = TRouteAuthConfig & {
691
+ /** Whether this route is registered. Defaults to true. */
692
+ enabled?: boolean;
682
693
  request?: {
683
694
  params?: TAnyObjectSchema;
684
695
  query?: TAnyObjectSchema;
@@ -708,16 +719,16 @@ type TCustomizableRouteConfig = TRouteAuthConfig & {
708
719
 
709
720
  When resolving authentication for a route:
710
721
 
711
- 1. **Endpoint `authenticate: { skip: true }`** No auth (ignores controller `authenticate`)
712
- 2. **Endpoint `authenticate: { strategies }`** Override controller (empty array = no auth)
713
- 3. **Controller `authenticate`** Default fallback
722
+ 1. **Endpoint `authenticate: { skip: true }`** - No auth (ignores controller `authenticate`)
723
+ 2. **Endpoint `authenticate: { strategies }`** - Override controller (empty array = no auth)
724
+ 3. **Controller `authenticate`** - Default fallback
714
725
 
715
726
  When resolving authorization for a route:
716
727
 
717
- 1. **Endpoint `authenticate: { skip: true }`** No authorize (auth skipped entirely)
718
- 2. **Endpoint `authorize: { skip: true }`** No authorize (explicitly skipped)
719
- 3. **Endpoint `authorize: { ... }`** Override controller authorize
720
- 4. **Controller `authorize`** Default fallback
728
+ 1. **Endpoint `authenticate: { skip: true }`** - No authorize (auth skipped entirely)
729
+ 2. **Endpoint `authorize: { skip: true }`** - No authorize (explicitly skipped)
730
+ 3. **Endpoint `authorize: { ... }`** - Override controller authorize
731
+ 4. **Controller `authorize`** - Default fallback
721
732
 
722
733
  ### Authentication Examples
723
734
 
@@ -763,14 +774,14 @@ const OrderController = ControllerFactory.defineCrudController({
763
774
  repository: { name: 'OrderRepository' },
764
775
  controller: { name: 'OrderController', basePath: '/orders' },
765
776
  authenticate: { strategies: [Authentication.STRATEGY_JWT] },
766
- authorize: { resource: 'orders', scopes: ['read'] },
777
+ authorize: { action: 'read', resource: 'orders' },
767
778
  routes: {
768
779
  find: {
769
780
  authenticate: { skip: true },
770
781
  response: { schema: CustomOrderListSchema },
771
782
  },
772
783
  create: {
773
- authorize: { resource: 'orders', scopes: ['write'] },
784
+ authorize: { action: 'write', resource: 'orders' },
774
785
  request: { body: CustomOrderCreateSchema },
775
786
  response: { schema: CustomOrderResponseSchema },
776
787
  },