@venizia/ignis-docs 0.2.0 → 0.2.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 (142) hide show
  1. package/README.md +14 -14
  2. package/content/best-practices/api-usage-examples.md +39 -19
  3. package/content/best-practices/architectural-patterns.md +22 -11
  4. package/content/best-practices/architecture-decisions.md +29 -16
  5. package/content/best-practices/code-style-standards/advanced-patterns.md +43 -31
  6. package/content/best-practices/code-style-standards/constants-configuration.md +19 -4
  7. package/content/best-practices/code-style-standards/control-flow.md +33 -1
  8. package/content/best-practices/code-style-standards/documentation.md +28 -8
  9. package/content/best-practices/code-style-standards/function-patterns.md +15 -4
  10. package/content/best-practices/code-style-standards/index.md +7 -2
  11. package/content/best-practices/code-style-standards/naming-conventions.md +26 -2
  12. package/content/best-practices/code-style-standards/route-definitions.md +9 -5
  13. package/content/best-practices/code-style-standards/tooling.md +14 -1
  14. package/content/best-practices/code-style-standards/type-safety.md +39 -6
  15. package/content/best-practices/common-pitfalls.md +59 -35
  16. package/content/best-practices/contribution-workflow.md +6 -2
  17. package/content/best-practices/data-modeling.md +78 -62
  18. package/content/best-practices/deployment-strategies.md +56 -19
  19. package/content/best-practices/error-handling.md +182 -93
  20. package/content/best-practices/index.md +6 -0
  21. package/content/best-practices/performance-optimization.md +26 -13
  22. package/content/best-practices/security-guidelines.md +35 -9
  23. package/content/best-practices/testing-strategies.md +7 -2
  24. package/content/best-practices/troubleshooting-tips.md +27 -17
  25. package/content/extensions/components/api-reference.md +107 -322
  26. package/content/extensions/components/authentication/api.md +454 -603
  27. package/content/extensions/components/authentication/errors.md +121 -498
  28. package/content/extensions/components/authentication/index.md +88 -801
  29. package/content/extensions/components/authentication/usage.md +207 -956
  30. package/content/extensions/components/authorization/api.md +736 -656
  31. package/content/extensions/components/authorization/errors.md +168 -206
  32. package/content/extensions/components/authorization/index.md +82 -797
  33. package/content/extensions/components/authorization/usage.md +194 -527
  34. package/content/extensions/components/health-check.md +71 -243
  35. package/content/extensions/components/mail/api.md +504 -287
  36. package/content/extensions/components/mail/errors.md +73 -61
  37. package/content/extensions/components/mail/index.md +96 -467
  38. package/content/extensions/components/mail/usage.md +130 -172
  39. package/content/extensions/components/request-tracker.md +66 -173
  40. package/content/extensions/components/socket-io/api.md +195 -13
  41. package/content/extensions/components/socket-io/errors.md +3 -3
  42. package/content/extensions/components/socket-io/index.md +50 -337
  43. package/content/extensions/components/socket-io/usage.md +143 -26
  44. package/content/extensions/components/static-asset/api.md +410 -142
  45. package/content/extensions/components/static-asset/errors.md +110 -53
  46. package/content/extensions/components/static-asset/index.md +79 -608
  47. package/content/extensions/components/static-asset/usage.md +180 -300
  48. package/content/extensions/components/websocket/api.md +275 -399
  49. package/content/extensions/components/websocket/errors.md +47 -56
  50. package/content/extensions/components/websocket/index.md +74 -407
  51. package/content/extensions/components/websocket/usage.md +110 -341
  52. package/content/extensions/helpers/cron/index.md +51 -160
  53. package/content/extensions/helpers/crypto/index.md +62 -483
  54. package/content/extensions/helpers/crypto/reference.md +456 -0
  55. package/content/extensions/helpers/env/index.md +60 -178
  56. package/content/extensions/helpers/error/index.md +221 -207
  57. package/content/extensions/helpers/inversion/index.md +65 -556
  58. package/content/extensions/helpers/inversion/reference.md +522 -0
  59. package/content/extensions/helpers/kafka/admin.md +20 -1
  60. package/content/extensions/helpers/kafka/compile-binary.md +41 -35
  61. package/content/extensions/helpers/kafka/consumer.md +54 -20
  62. package/content/extensions/helpers/kafka/examples.md +21 -16
  63. package/content/extensions/helpers/kafka/index.md +80 -610
  64. package/content/extensions/helpers/kafka/producer.md +134 -5
  65. package/content/extensions/helpers/kafka/schema-registry.md +45 -70
  66. package/content/extensions/helpers/logger/hf-logger.md +193 -0
  67. package/content/extensions/helpers/logger/index.md +64 -563
  68. package/content/extensions/helpers/logger/pino.md +85 -0
  69. package/content/extensions/helpers/logger/reference.md +746 -0
  70. package/content/extensions/helpers/network/api.md +241 -195
  71. package/content/extensions/helpers/network/index.md +72 -530
  72. package/content/extensions/helpers/queue/index.md +72 -900
  73. package/content/extensions/helpers/queue/reference.md +467 -0
  74. package/content/extensions/helpers/redis/index.md +73 -645
  75. package/content/extensions/helpers/redis/reference.md +727 -0
  76. package/content/extensions/helpers/secrets/index.md +66 -0
  77. package/content/extensions/helpers/socket-io/api.md +305 -203
  78. package/content/extensions/helpers/socket-io/index.md +66 -432
  79. package/content/extensions/helpers/storage/api.md +564 -462
  80. package/content/extensions/helpers/storage/index.md +77 -573
  81. package/content/extensions/helpers/types/index.md +66 -499
  82. package/content/extensions/helpers/types/reference.md +650 -0
  83. package/content/extensions/helpers/uid/index.md +58 -227
  84. package/content/extensions/helpers/websocket/api.md +329 -216
  85. package/content/extensions/helpers/websocket/index.md +65 -503
  86. package/content/extensions/helpers/worker-thread/index.md +58 -396
  87. package/content/extensions/helpers/worker-thread/reference.md +428 -0
  88. package/content/guides/core-concepts/persistent/models.md +1 -1
  89. package/content/guides/core-concepts/persistent/search-typesense.md +3 -3
  90. package/content/guides/core-concepts/persistent/transactions.md +1 -1
  91. package/content/guides/core-concepts/secrets-vault.md +177 -0
  92. package/content/guides/core-concepts/services.md +1 -1
  93. package/content/guides/migrations/redis-helpers-migration.md +1 -1
  94. package/content/guides/migrations/unified-connectors-migration.md +2 -2
  95. package/content/guides/tutorials/ecommerce-api.md +3 -8
  96. package/content/references/base/application.md +1 -1
  97. package/content/references/base/connectors.md +79 -136
  98. package/content/references/base/datasources-reference.md +599 -0
  99. package/content/references/base/datasources.md +84 -444
  100. package/content/references/base/dependency-injection.md +17 -39
  101. package/content/references/base/filter-system/application-usage.md +69 -121
  102. package/content/references/base/filter-system/array-operators.md +12 -0
  103. package/content/references/base/filter-system/comparison-operators.md +12 -0
  104. package/content/references/base/filter-system/default-filter.md +136 -348
  105. package/content/references/base/filter-system/fields-order-pagination.md +38 -16
  106. package/content/references/base/filter-system/index.md +106 -257
  107. package/content/references/base/filter-system/json-filtering.md +12 -2
  108. package/content/references/base/filter-system/list-operators.md +16 -2
  109. package/content/references/base/filter-system/logical-operators.md +13 -0
  110. package/content/references/base/filter-system/null-operators.md +13 -0
  111. package/content/references/base/filter-system/pattern-matching.md +12 -0
  112. package/content/references/base/filter-system/quick-reference.md +11 -2
  113. package/content/references/base/filter-system/range-operators.md +12 -0
  114. package/content/references/base/filter-system/tips.md +70 -133
  115. package/content/references/base/filter-system/use-cases.md +156 -233
  116. package/content/references/base/middlewares.md +35 -21
  117. package/content/references/base/models-reference.md +886 -0
  118. package/content/references/base/models.md +80 -1452
  119. package/content/references/base/repositories/advanced.md +156 -192
  120. package/content/references/base/repositories/index.md +77 -650
  121. package/content/references/base/repositories/mixins.md +22 -18
  122. package/content/references/base/repositories/relations.md +123 -171
  123. package/content/references/base/repositories/soft-deletable.md +58 -56
  124. package/content/references/base/secrets.md +263 -0
  125. package/content/references/base/services.md +2 -2
  126. package/content/references/configuration/environment-variables.md +48 -4
  127. package/content/references/configuration/index.md +49 -31
  128. package/content/references/quick-reference.md +3 -16
  129. package/content/references/utilities/crypto.md +35 -76
  130. package/content/references/utilities/date.md +33 -73
  131. package/content/references/utilities/index.md +1 -1
  132. package/content/references/utilities/jsx-reference.md +298 -0
  133. package/content/references/utilities/jsx.md +82 -525
  134. package/content/references/utilities/module.md +29 -62
  135. package/content/references/utilities/parse.md +34 -64
  136. package/content/references/utilities/performance.md +33 -58
  137. package/content/references/utilities/promise.md +28 -62
  138. package/content/references/utilities/request.md +57 -218
  139. package/content/references/utilities/schema.md +43 -137
  140. package/content/references/utilities/statuses-reference.md +361 -0
  141. package/content/references/utilities/statuses.md +63 -667
  142. package/package.json +8 -8
@@ -0,0 +1,599 @@
1
+ ---
2
+ title: DataSources - Full Reference
3
+ description: Complete reference for the engine-neutral DataSource contract, the PostgreSQL connector, driver seam, and transaction API
4
+ difficulty: intermediate
5
+ ---
6
+
7
+ # DataSources - Full Reference
8
+
9
+ Exhaustive reference for `IDataSource`, `AbstractDataSource`, the PostgreSQL connector (`AbstractPostgresDataSource`/`BasePostgresDataSource`), the driver seam, and transactions. For a readable introduction and the common tasks, start with the [DataSources overview](/references/base/datasources).
10
+
11
+ **Files:**
12
+
13
+ - [`packages/core/src/base/datasources/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/datasources/abstract.ts) - neutral `AbstractDataSource`
14
+ - [`packages/core/src/base/datasources/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/datasources/common/types.ts) - `IDataSource`, `DataSourceDrivers`, neutral transaction types
15
+ - [`packages/core/src/connectors/postgres/datasources/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/abstract.ts) - `AbstractPostgresDataSource`
16
+ - [`packages/core/src/connectors/postgres/datasources/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/base.ts) - `BasePostgresDataSource`
17
+ - [`packages/core/src/connectors/postgres/datasources/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/common/types.ts) - PostgreSQL connector types, `IsolationLevels`
18
+ - [`packages/core/src/connectors/postgres/drivers`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/drivers) - `IRelationalDriver`, `NodePostgresDriver`, `PostgresJsDriver`
19
+
20
+ > [!IMPORTANT] Base vs. connectors
21
+ > - **Split.** Engine-neutral root at `src/base/datasources/`; per-engine connectors at `src/connectors/{postgres,typesense}/datasources/`.
22
+ > - **`AbstractDataSource` has no SQL, no Drizzle, no `pool`.** Those live only in the PostgreSQL connector. See [Connectors](/references/base/connectors) for the full base-vs-connectors architecture, dual-door exports, and how to add a new engine.
23
+ > - **Scope of this page.** The neutral contract plus the PostgreSQL connector in depth; see [Search & Typesense](/guides/core-concepts/persistent/search-typesense) for the other engine.
24
+
25
+ ## Quick reference
26
+
27
+ | Class / interface | Purpose | Key members |
28
+ |---|---|---|
29
+ | `IDataSource` | Engine-neutral contract for all datasources | `name`, `settings`, `schema`, `getSchema()`, `getSettings()`, `configure()` |
30
+ | `AbstractDataSource` | Engine-neutral base implementation with logging | Extends `BaseHelper`; `getCapabilities()` defaults to `{ transactions: false }`; `beginTransaction()` defaults to `throwNotSupported(...)` |
31
+ | `AbstractPostgresDataSource` | PostgreSQL-aware abstraction | Adds `connector`, `client`, `driver`; abstract `getConnectionString()` / `beginTransaction()` |
32
+ | `BasePostgresDataSource` | Concrete class to extend for PostgreSQL | Constructor, schema auto-discovery, real transaction support. Canonical name - `BaseDataSource` is a compatibility alias re-exporting the same class |
33
+ | `IRelationalDriver` | Driver seam - connection acquisition + control statements | `createConnector()`, `acquire()`, `getClient()`, `end()` |
34
+ | `ITransaction` | Engine-neutral transaction contract | `isActive`, `commit()`, `rollback()` (no `connector` field) |
35
+ | `IDatabaseTransaction` | PostgreSQL transaction object | Extends `ITransaction`, adds `connector`, `isolationLevel` |
36
+ | `IsolationLevels` | Isolation level constants (PostgreSQL) | `READ_COMMITTED`, `REPEATABLE_READ`, `SERIALIZABLE` |
37
+
38
+ ## `IDataSource` interface
39
+
40
+ Engine-neutral contract implemented by every datasource in the framework, regardless of engine.
41
+
42
+ `Source ->` [`packages/core/src/base/datasources/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/datasources/common/types.ts)
43
+
44
+ ```typescript
45
+ interface IDataSource<
46
+ Settings extends object = {},
47
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
48
+ ConfigurableOptions extends object = {},
49
+ > extends IConfigurable<ConfigurableOptions> {
50
+ name: string;
51
+ settings: Settings;
52
+ schema: Schema;
53
+
54
+ getSettings(): Settings;
55
+ getSchema(): Schema;
56
+ }
57
+ ```
58
+
59
+ ### Type parameters
60
+
61
+ | Type parameter | Default | Description |
62
+ |---|---|---|
63
+ | `Settings` | `{}` | Connection configuration type (host, port, etc.) |
64
+ | `Schema` | `TAnyDataSourceSchema` | Combined schema type - shape depends on the connector: Drizzle tables for PostgreSQL, collection definitions for typesense |
65
+ | `ConfigurableOptions` | `{}` | Options passed to `configure()` |
66
+
67
+ ### Members
68
+
69
+ | Member | Type | Description |
70
+ |---|---|---|
71
+ | `name` | `string` | Datasource name |
72
+ | `settings` | `Settings` | Configuration object |
73
+ | `schema` | `Schema` | Combined schema - auto-discovered or manual |
74
+ | `getSchema()` | `Schema` | Returns the combined schema |
75
+ | `getSettings()` | `Settings` | Returns connection settings |
76
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initializes the underlying connection - inherited from `IConfigurable` |
77
+
78
+ > [!NOTE]
79
+ > `getCapabilities()` and `beginTransaction()` are not part of `IDataSource` - they are declared on `AbstractDataSource` (below), which every connector extends.
80
+
81
+ ## `AbstractDataSource` (engine-neutral root)
82
+
83
+ Top-level abstract class extended by every engine. Extends `BaseHelper` for scoped logging. Contains **no SQL, no Drizzle, and no connection-pool members** - those are added by each connector.
84
+
85
+ `Source ->` [`packages/core/src/base/datasources/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/datasources/abstract.ts)
86
+
87
+ ```typescript
88
+ abstract class AbstractDataSource<
89
+ Settings extends object = {},
90
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
91
+ ConfigurableOptions extends object = {},
92
+ > extends BaseHelper implements IDataSource<Settings, Schema, ConfigurableOptions>
93
+ ```
94
+
95
+ **Properties:**
96
+
97
+ | Property | Type | Visibility | Description |
98
+ |---|---|---|---|
99
+ | `name` | `string` | public | Datasource identifier |
100
+ | `settings` | `Settings` | public | Connection configuration |
101
+ | `schema` | `Schema` | public | Combined schema |
102
+
103
+ **Abstract methods** (must be implemented by connectors):
104
+
105
+ | Method | Return type | Description |
106
+ |---|---|---|
107
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the underlying connection. This is the **only** abstract member of the neutral root - `getConnectionString()` is not part of it; it is declared one level down, on `AbstractPostgresDataSource` (see below) |
108
+
109
+ **Concrete methods** (defaults, overridable by connectors):
110
+
111
+ | Method | Return type | Default behavior |
112
+ |---|---|---|
113
+ | `getSettings()` | `Settings` | Returns `this.settings` |
114
+ | `getSchema()` | `Schema` | Returns `this.schema`; throws if not initialized |
115
+ | `getCapabilities()` | `IDataSourceCapabilities` | Returns `{ transactions: false }` |
116
+ | `beginTransaction(opts?)` | `Promise<ITransaction>` | Calls `throwNotSupported({ scope: this.constructor.name, feature: 'Transactions', logger: this.logger })` - throws HTTP 501 whose `normalized.code` resolves to `'core.not_supported'` |
117
+
118
+ **Protected helpers:**
119
+
120
+ | Method | Description |
121
+ |---|---|
122
+ | `getBoundModelClasses()` | Returns the model classes bound to this datasource via `@repository` metadata, read from `MetadataRegistry` |
123
+ | `discoverDefinitions({ read, kind })` | Walks the bound model classes, reads a connector-specific artifact via `read`, and returns a name-keyed registry. Skips undefined reads, throws on duplicate names, honors `autoDiscovery: false`. Shared plumbing every connector's own `discoverSchema()`-equivalent builds on |
124
+
125
+ > [!NOTE] NotSupported convention
126
+ > Every capability an engine does not implement - transactions, row-level locking - uses the same `throwNotSupported` utility ([`packages/core/src/utilities/error.utility.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/utilities/error.utility.ts)), producing a consistent `501 Not Implemented` whose `normalized.code` resolves to `'core.not_supported'`. This is how the typesense connector signals "not applicable to this engine" instead of silently no-op-ing.
127
+
128
+ ### `IDataSourceCapabilities`
129
+
130
+ ```typescript
131
+ interface IDataSourceCapabilities {
132
+ transactions: boolean;
133
+ }
134
+ ```
135
+
136
+ Only `BasePostgresDataSource` overrides `getCapabilities()` to return `{ transactions: true }`. The typesense datasources inherit the neutral default (`{ transactions: false }`).
137
+
138
+ ## PostgreSQL connector: `AbstractPostgresDataSource` and `BasePostgresDataSource`
139
+
140
+ `Source ->` [`packages/core/src/connectors/postgres/datasources/abstract.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/abstract.ts), [`packages/core/src/connectors/postgres/datasources/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/base.ts)
141
+
142
+ ### `AbstractPostgresDataSource`
143
+
144
+ Extends `AbstractDataSource` with PostgreSQL/Drizzle-specific members. Internally named `AbstractRelationalDataSource`; `AbstractPostgresDataSource` is the exported, engine-carrying name.
145
+
146
+ ```typescript
147
+ abstract class AbstractRelationalDataSource<
148
+ Settings extends object = {},
149
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
150
+ ConfigurableOptions extends object = {},
151
+ Client = Pool,
152
+ > extends AbstractDataSource<Settings, Schema, ConfigurableOptions>
153
+ implements IPostgresDataSource<Settings, Schema, ConfigurableOptions, Client>
154
+ ```
155
+
156
+ **Additional properties:**
157
+
158
+ | Property | Type | Visibility | Description |
159
+ |---|---|---|---|
160
+ | `connector` | `TRelationalConnector<Schema>` | public | Drizzle ORM instance - any Drizzle pg driver satisfies this |
161
+ | `client` | `Client` (`Pool` by default) | protected, optional | The raw driver client `configure()` builds - a `pg.Pool`, or a postgres-js `Sql`. Assigning it is enough: `wireDriverFromMetadata()` instantiates the `@datasource({ driver })` class over it on first use. Absent once `useDriver()` wired a driver instead |
162
+ | `driver` | `IRelationalDriver<Schema>` | protected, optional | The connection driver (`node-postgres` or `postgres-js`); built lazily by `wireDriverFromMetadata()` from the class named in `@datasource({ driver })`, or explicitly by `useDriver()` |
163
+
164
+ The fourth generic, `Client = Pool`, is what lets a postgres-js datasource declare `Client = Sql` and keep `getClient()` honestly typed.
165
+
166
+ **Abstract methods:**
167
+
168
+ | Method | Return type | Description |
169
+ |---|---|---|
170
+ | `getConnectionString()` | `ValueOrPromise<string>` | Return the connection URL. Declared here, not on the neutral root - only a connector that has a notion of "connection string" needs it |
171
+ | `beginTransaction(opts?)` | `Promise<IDatabaseTransaction<Schema>>` | Start a new PostgreSQL transaction; overrides the neutral `beginTransaction()` |
172
+
173
+ **Concrete methods:**
174
+
175
+ | Method | Return type | Description |
176
+ |---|---|---|
177
+ | `getConnector()` | `TRelationalConnector<Schema>` | Wires the driver on first use (via `wireDriverFromMetadata()`), then returns `this.connector` |
178
+ | `getClient()` | `Client` | Raw driver client escape hatch - `pg.Pool` for node-postgres, `Sql` for postgres-js. Reads `this.driver.getClient()` if a driver is resolved, else `this.client` directly. Throws if neither is set |
179
+ | `getQueryDialect()` | `IRelationalQueryDialect` | Returns a shared, lazily-constructed `FilterBuilder` instance (static, one per process) |
180
+ | `onSecretRotated(opts)` | `Promise<void>` | Applies rotated credentials to `this.settings`, rebuilds the driver/connector/client against a fresh pool, calls `this.configure()` and `this.resolveDriver()`, then drains the old pool once the new one is in place. See [Secrets & Vault](/guides/core-concepts/secrets-vault) |
181
+
182
+ **Protected methods:**
183
+
184
+ | Method | Description |
185
+ |---|---|
186
+ | `wireDriverFromMetadata()` | Idempotent, lazy. If `this.connector` already exists, no-ops. If `this.driver` exists but `this.connector` does not, builds the connector from it. Otherwise reads the class named in `@datasource({ driver })` from `MetadataRegistry`, instantiates it over `this.client`, and calls `useDriver()`. Throws if neither `client` nor `driver` is set, or if the named `driver` metadata is not a class (a string, historically valid for search engines, is rejected here with a message pointing at `NodePostgresDriver`) |
187
+ | `resolveDriver()` | Calls `wireDriverFromMetadata()`, then returns `this.driver` |
188
+ | `useDriver({ driver, schema? })` | Assigns `this.driver` **and** builds `this.connector` from it in one step - the two-step form (driver set, connector forgotten) is unrepresentable. `schema` defaults to `getSchema()`. The public escape hatch for a custom or third-party driver, bypassing `@datasource({ driver })` entirely |
189
+ | `mapSecretToSettings({ secret })` | Maps Vault's `{ username, password }` secret shape to `pg`'s `{ user, password }` settings shape, for `onSecretRotated()` |
190
+
191
+ > [!NOTE] Driver seam
192
+ > - **Class, not a string.** `@datasource({ driver })` names the driver **class** (`NodePostgresDriver` or `PostgresJsDriver`) - a driver-name string cannot carry `pg`/`postgres` into the app's bundle, only a real class reference can.
193
+ > - **`configure()` only assigns `this.client`.** The protected `wireDriverFromMetadata()` (called internally by `getConnector()`/`resolveDriver()`) instantiates the named class over it and builds `this.connector`, lazily and idempotently.
194
+ > - **Where the drivers live.** `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).
195
+
196
+ ### `BasePostgresDataSource` (canonical name; `BaseDataSource` is a compatibility alias)
197
+
198
+ Extends `AbstractPostgresDataSource` with a constructor, **schema auto-discovery**, and a real `beginTransaction()` implementation backed by the connection pool. Internally named `BaseRelationalDataSource`.
199
+
200
+ `Source ->` [`packages/core/src/connectors/postgres/datasources/base.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/base.ts)
201
+
202
+ ```typescript
203
+ abstract class BaseRelationalDataSource<
204
+ Settings extends object = {},
205
+ Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema,
206
+ ConfigurableOptions extends object = {},
207
+ Client = Pool,
208
+ > extends AbstractRelationalDataSource<Settings, Schema, ConfigurableOptions, Client>
209
+ ```
210
+
211
+ > [!TIP] Naming
212
+ > `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`, so existing code is unaffected.
213
+
214
+ #### Key features
215
+
216
+ | Feature | Description |
217
+ |---|---|
218
+ | Schema auto-discovery | Schema is automatically built from registered `@repository` decorators |
219
+ | Manual override | You can pass `schema` in the constructor for full control |
220
+ | Built-in transaction support | `beginTransaction()` acquires its connection from the resolved driver; overrides `getCapabilities()` to return `{ transactions: true }` |
221
+
222
+ > [!TIP]
223
+ > Set `autoDiscovery: false` in the `@datasource` decorator to disable automatic schema discovery, when you want to provide the schema manually.
224
+
225
+ ### Constructor options
226
+
227
+ ```typescript
228
+ constructor(opts: {
229
+ name: string; // DataSource name (usually the class name)
230
+ config: Settings; // Database connection settings
231
+ schema?: Schema; // Optional - auto-discovered if not provided
232
+ })
233
+ ```
234
+
235
+ ### Schema auto-discovery
236
+
237
+ When a model is bound via `@repository({ model: YourModel, dataSource: YourDataSource })`, the framework automatically:
238
+
239
+ 1. Registers the model-datasource binding in `MetadataRegistry`
240
+ 2. When `getSchema()` is called and `this.schema` is not already set, calls `discoverSchema()`
241
+ 3. `discoverSchema()` queries `MetadataRegistry.buildSchema({ dataSource })` for every model bound to this datasource and merges their tables and relations into a single schema object
242
+
243
+ This means tables and relations never need to be manually merged in the datasource constructor.
244
+
245
+ ### Configuration flow
246
+
247
+ 1. **Your DataSource's `constructor` runs.** You call `super()` with `name` and `config`. Schema is auto-discovered from `@repository` bindings unless `schema` is provided manually.
248
+ 2. **`Application.registerDataSources()` runs during startup.** The application fetches your datasource instance from the DI container and calls `configure()` on it.
249
+ 3. **Your `configure()` method runs.** Its only job is to create the raw client and assign it to `this.client` - `getConnector()`/`beginTransaction()` lazily instantiate the class named in `@datasource({ driver })` over it and build the Drizzle connector. `configure()` never touches `this.connector` directly.
250
+
251
+ ### Example implementations
252
+
253
+ #### Pattern 1: auto-discovery (recommended)
254
+
255
+ ```typescript
256
+ // src/datasources/postgres.datasource.ts
257
+ import { datasource, ValueOrPromise } from '@venizia/ignis';
258
+ import { BasePostgresDataSource } from '@venizia/ignis/postgres';
259
+ import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
260
+ import { applicationEnvironment, int } from '@venizia/ignis-helpers';
261
+ import { Pool } from 'pg';
262
+
263
+ interface IDataSourceConfigs {
264
+ host: string;
265
+ port: number;
266
+ database: string;
267
+ user: string;
268
+ password: string;
269
+ ssl: boolean;
270
+ }
271
+
272
+ @datasource({ driver: NodePostgresDriver })
273
+ export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
274
+ private readonly protocol = 'postgresql';
275
+
276
+ constructor() {
277
+ super({
278
+ name: PostgresDataSource.name,
279
+ config: {
280
+ host: applicationEnvironment.get<string>('APP_ENV_POSTGRES_HOST'),
281
+ port: int(applicationEnvironment.get<string>('APP_ENV_POSTGRES_PORT')),
282
+ database: applicationEnvironment.get<string>('APP_ENV_POSTGRES_DATABASE'),
283
+ user: applicationEnvironment.get<string>('APP_ENV_POSTGRES_USERNAME'),
284
+ password: applicationEnvironment.get<string>('APP_ENV_POSTGRES_PASSWORD'),
285
+ ssl: false,
286
+ },
287
+ // NO schema property - auto-discovered from @repository bindings.
288
+ });
289
+ }
290
+
291
+ override configure(): ValueOrPromise<void> {
292
+ const schema = Object.keys(this.getSchema());
293
+ this.logger.debug(
294
+ '[configure] Auto-discovered schema | Schema + Relations (%s): %o',
295
+ schema.length,
296
+ schema,
297
+ );
298
+
299
+ // That is all - the base class wires the driver + connector from @datasource({ driver }).
300
+ this.client = new Pool(this.settings);
301
+ }
302
+
303
+ override getConnectionString(): ValueOrPromise<string> {
304
+ const { host, port, user, password, database } = this.settings;
305
+ return `${this.protocol}://${user}:${password}@${host}:${port}/${database}`;
306
+ }
307
+ }
308
+ ```
309
+
310
+ With this pattern, defining repositories is enough for `PostgresDataSource.schema` to include their tables and relations:
311
+
312
+ ```typescript
313
+ @repository({ model: User, dataSource: PostgresDataSource })
314
+ export class UserRepository extends DefaultCRUDRepository<typeof User.schema> {}
315
+
316
+ @repository({ model: Configuration, dataSource: PostgresDataSource })
317
+ export class ConfigurationRepository extends DefaultCRUDRepository<typeof Configuration.schema> {}
318
+ ```
319
+
320
+ #### Pattern 2: manual schema (full control)
321
+
322
+ ```typescript
323
+ import { datasource, ValueOrPromise } from '@venizia/ignis';
324
+ import { BasePostgresDataSource } from '@venizia/ignis/postgres';
325
+ import { NodePostgresDriver } from '@venizia/ignis/postgres/node-postgres';
326
+ import { Pool } from 'pg';
327
+ import { User, userTable, userRelations } from '@/models/user.model';
328
+ import { Configuration, configurationTable, configurationRelations } from '@/models/configuration.model';
329
+
330
+ @datasource({ driver: NodePostgresDriver })
331
+ export class PostgresDataSource extends BasePostgresDataSource<IDataSourceConfigs> {
332
+ constructor() {
333
+ super({
334
+ name: PostgresDataSource.name,
335
+ config: {
336
+ host: process.env.APP_ENV_POSTGRES_HOST ?? 'localhost',
337
+ port: +(process.env.APP_ENV_POSTGRES_PORT ?? 5432),
338
+ database: process.env.APP_ENV_POSTGRES_DATABASE ?? 'mydb',
339
+ user: process.env.APP_ENV_POSTGRES_USERNAME ?? 'postgres',
340
+ password: process.env.APP_ENV_POSTGRES_PASSWORD ?? '',
341
+ },
342
+ // Manually provide schema using spread syntax.
343
+ schema: {
344
+ [User.TABLE_NAME]: userTable,
345
+ [Configuration.TABLE_NAME]: configurationTable,
346
+ ...userRelations.relations,
347
+ ...configurationRelations.relations,
348
+ },
349
+ });
350
+ }
351
+
352
+ override configure(): ValueOrPromise<void> {
353
+ // Manually-provided schema is used as-is by the connector the base class builds from this.client.
354
+ this.client = new Pool(this.settings);
355
+ }
356
+
357
+ override getConnectionString(): ValueOrPromise<string> {
358
+ const { host, port, user, password, database } = this.settings;
359
+ return `postgresql://${user}:${password}@${host}:${port}/${database}`;
360
+ }
361
+ }
362
+ ```
363
+
364
+ > [!IMPORTANT]
365
+ > `configure()` must leave the datasource with a way to reach the database: either assign the raw client to `this.client` (paired with naming the driver class in `@datasource({ driver })`), or wire a driver directly with `this.useDriver({ driver })` for a custom or third-party driver. `getConnector()`/`beginTransaction()` resolve the driver lazily from whichever you provided. With neither, `wireDriverFromMetadata()` throws `No driver and no client`.
366
+
367
+ ### `@datasource` decorator
368
+
369
+ `Source ->` [`packages/core/src/base/metadata/persistents.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/metadata/persistents.ts)
370
+
371
+ ```typescript
372
+ @datasource({
373
+ driver: NodePostgresDriver, // Driver CLASS (relational), or a driver-name string (search)
374
+ autoDiscovery?: true, // Optional - defaults to true
375
+ })
376
+ ```
377
+
378
+ | Option | Type | Default | Description |
379
+ |---|---|---|---|
380
+ | `driver` | `TDataSourceDriverClass` | - | The driver **class** - `NodePostgresDriver` or `PostgresJsDriver` (imported from `@venizia/ignis/postgres/node-postgres` / `.../postgres-js`), never a driver-name string on a **relational** datasource. A class reference is the only thing that carries `pg`/`postgres` into the app's bundle. **Omit it for a search datasource**: `extends TypesenseDataSource` already names the engine, and is what carries `typesense` into the bundle |
381
+ | `autoDiscovery` | `boolean` | `true` | Enable/disable schema auto-discovery. `false` makes `discoverSchema()`/`discoverDefinitions()` return an empty object instead of querying `MetadataRegistry` |
382
+
383
+ ### Abstract methods (extending `BasePostgresDataSource`)
384
+
385
+ | Method | Return type | Description |
386
+ |---|---|---|
387
+ | `configure(opts?)` | `ValueOrPromise<void>` | Initialize the client. Must set `this.client` (the base class wires the driver and Drizzle connector from `@datasource({ driver })`), or call `this.useDriver()` directly for a custom driver |
388
+ | `getConnectionString()` | `ValueOrPromise<string>` | Return the database connection string |
389
+
390
+ ### Helper methods
391
+
392
+ | Method | Description |
393
+ |---|---|
394
+ | `getSchema()` | Returns the schema, auto-discovering via `discoverSchema()` if not manually provided |
395
+ | `getSettings()` | Returns connection settings |
396
+ | `getConnector()` | Returns the Drizzle connector, wiring the driver first if needed |
397
+ | `hasDiscoverableModels()` | Returns `true` if there are models registered for this datasource via `@repository` |
398
+
399
+ ### Protected methods
400
+
401
+ | Method | Description |
402
+ |---|---|
403
+ | `discoverSchema()` | Queries `MetadataRegistry` for all `@repository` bindings targeting this datasource, then calls `registry.buildSchema()` to merge tables and relations into a single schema object. Returns `{}` when `autoDiscovery: false` |
404
+
405
+ ## Driver interface
406
+
407
+ `Source ->` [`packages/core/src/connectors/postgres/drivers`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/drivers)
408
+
409
+ `IRelationalDriver` owns connection acquisition and the raw control statements (`BEGIN`/`COMMIT`/`ROLLBACK`) - the only two places the connector is hard-wired to a specific client library.
410
+
411
+ ```typescript
412
+ interface IRelationalDriver<Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema, Client = unknown> {
413
+ createConnector(opts: { schema: Schema }): TRelationalConnector<Schema>;
414
+ acquire(opts: { schema: Schema }): Promise<IRelationalConnection<Schema>>;
415
+ getClient(): Client;
416
+ end(): Promise<void>;
417
+ }
418
+
419
+ interface IRelationalConnection<Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema> {
420
+ connector: TRelationalConnector<Schema>;
421
+ execute(opts: { statement: string }): Promise<IStatementResult>;
422
+ release(opts?: { destroy?: boolean }): void;
423
+ }
424
+
425
+ interface IStatementResult {
426
+ count: number;
427
+ }
428
+ ```
429
+
430
+ | Member | Description |
431
+ |---|---|
432
+ | `createConnector({ schema })` | Builds the pooled Drizzle connector - what `wireDriverFromMetadata()` assigns to `this.connector` |
433
+ | `acquire({ schema })` | Checks out one dedicated physical connection for an explicit transaction, returning a connector bound to that connection plus `execute()`/`release()` |
434
+ | `getClient()` | Raw client escape - `pg.Pool` for node-postgres, `Sql` for postgres-js |
435
+ | `end()` | Closes the underlying client/pool |
436
+ | `IRelationalConnection.execute({ statement })` | Runs a control statement verbatim (never parameterized - `BEGIN TRANSACTION ISOLATION LEVEL $1` is not valid SQL) |
437
+ | `IRelationalConnection.release({ destroy? })` | Returns the connection to the pool, or discards it when `destroy: true` - required after a failed `COMMIT`/`ROLLBACK`, since the session may still hold an open transaction |
438
+
439
+ Two concrete drivers ship today, both satisfying `IRelationalDriver` and both proven by the same conformance suite:
440
+
441
+ | Driver | Package | Client shape validated in the constructor |
442
+ |---|---|---|
443
+ | `NodePostgresDriver` | `pg` | Rejects a client without `connect()` **and** `totalCount` (pool accounting) - catches a bare `pg.Client` |
444
+ | `PostgresJsDriver` | `postgres` | Rejects a client without `reserve()` **and** `unsafe()` - catches a `pg.Pool` passed to the wrong driver |
445
+
446
+ - **Driver asymmetry, deliberate.** After a failed `COMMIT`, `pg` can destroy the poisoned connection (`release(err)`); postgres-js has no destroy semantics (`ReservedSql.release()` takes no argument), so it returns the connection to the pool regardless.
447
+ - **Both drivers accept the same call.** `IRelationalConnection.release({ destroy: true })` is accepted by both drivers and honored by one. See [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers) for the full driver comparison and Supabase's transaction-pooler requirements.
448
+
449
+ ## Connector types
450
+
451
+ `Source ->` [`packages/core/src/connectors/postgres/datasources/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/connectors/postgres/datasources/common/types.ts)
452
+
453
+ | Type | Description |
454
+ |---|---|
455
+ | `TRelationalConnector<Schema>` | Canonical connector type - a Drizzle `PgDatabase` that **every** pg driver (`node-postgres`, `postgres-js`) satisfies. Use this in new code |
456
+ | `TNodePostgresConnector<Schema>` | `@deprecated` compat alias for `TRelationalConnector<Schema>` |
457
+ | `TNodePostgresTransactionConnector<Schema>` | `@deprecated` compat alias - was the `PoolClient`-specific transaction connector; now aliases `TRelationalConnector<Schema>` |
458
+ | `TAnyConnector<Schema>` | Alias of `TRelationalConnector<Schema>` |
459
+ | `TAnyDataSourceSchema` | `Record<string, any>` - base type for all schema objects, defined in `src/base/datasources/common/types.ts`, shared across engines |
460
+
461
+ ### `DataSourceDrivers`
462
+
463
+ `Source ->` [`packages/core/src/base/datasources/common/types.ts`](https://github.com/VENIZIA-AI/ignis/blob/main/packages/core/src/base/datasources/common/types.ts)
464
+
465
+ An identity-only const-class - the engine actually used is chosen by which driver **class** `@datasource({ driver })` names, not by this constant. `DataSourceDrivers` is read by nothing else in `src`; it never routes connector configuration.
466
+
467
+ ```typescript
468
+ class DataSourceDrivers {
469
+ static readonly NODE_POSTGRES = 'node-postgres';
470
+ static readonly POSTGRES_JS = 'postgres-js';
471
+ static readonly TYPESENSE = 'typesense';
472
+ static readonly MEILISEARCH = 'meilisearch';
473
+
474
+ static isValid(value: string): boolean;
475
+ }
476
+ ```
477
+
478
+ ```typescript
479
+ DataSourceDrivers.NODE_POSTGRES // 'node-postgres'
480
+ DataSourceDrivers.POSTGRES_JS // 'postgres-js'
481
+ DataSourceDrivers.TYPESENSE // 'typesense'
482
+ DataSourceDrivers.MEILISEARCH // 'meilisearch'
483
+ DataSourceDrivers.isValid('node-postgres') // true
484
+ ```
485
+
486
+ > [!NOTE]
487
+ > `NODE_POSTGRES`/`POSTGRES_JS` remain valid `TDataSourceDriver` string values, but `@datasource({ driver })` on a **relational** datasource no longer accepts them - it takes the `NodePostgresDriver`/`PostgresJsDriver` class instead (see [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers)). Search connectors (`TYPESENSE`, `MEILISEARCH`) still take the driver-name string form, because `extends TypesenseDataSource` already names the engine and is what carries the client into the bundle.
488
+
489
+ ## Transaction support
490
+
491
+ 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).
492
+
493
+ ### How it works
494
+
495
+ `BasePostgresDataSource.beginTransaction()`:
496
+
497
+ 1. Resolves a driver (via `resolveDriver()`) and calls `driver.acquire({ schema })` to check out a dedicated physical connection
498
+ 2. Executes `BEGIN TRANSACTION ISOLATION LEVEL <level>` on that connection
499
+ 3. On a failed `BEGIN`, destroys the connection (`release({ destroy: true })`) and rethrows - it is never leaked back to the pool in an unknown state
500
+ 4. Returns an `IDatabaseTransaction` object exposing `isActive`, `commit()`, `rollback()`, and the connection-scoped `connector`
501
+
502
+ - **Shared `finish()`.** `commit()`/`rollback()` share one internal `finish()` that flips `isActive` to `false` **before** issuing the statement, so a commit racing a rollback cannot double-release the same connection - then runs `COMMIT`/`ROLLBACK`.
503
+ - **Outcome handling.** On success the connection is released back to the pool; on failure it is destroyed and the error is rethrown.
504
+
505
+ > [!WARNING] `commit()`/`rollback()` throw on failure
506
+ > - **A failed `COMMIT` or `ROLLBACK` throws** - a failed `COMMIT` never resolves as success.
507
+ > - **The poisoned connection is destroyed**, not returned to the pool, where the driver supports it: `node-postgres` can discard a connection; `postgres-js` has no destroy semantics and pools it anyway.
508
+ > - **Nest `rollback()` in its own `try...catch`.** It can throw and is normally called from a `catch`, so nesting keeps the rollback error from replacing the original cause.
509
+ > - **Calling `rollback()` twice is safe.** After a transaction already ended by failure, `rollback()` is a silent no-op (already torn down), so the canonical `catch { await tx.rollback(); throw error; }` pattern always works. See [Transactions](/guides/core-concepts/persistent/transactions) and [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers).
510
+
511
+ ### Neutral vs. PostgreSQL transaction types
512
+
513
+ `src/base` declares the engine-neutral shape; the PostgreSQL connector narrows it with connection details.
514
+
515
+ ```typescript
516
+ // packages/core/src/base/datasources/common/types.ts - engine-neutral
517
+ interface ITransaction {
518
+ isActive: boolean;
519
+ commit(): Promise<void>;
520
+ rollback(): Promise<void>;
521
+ }
522
+
523
+ // packages/core/src/connectors/postgres/datasources/common/types.ts - PostgreSQL
524
+ interface IDatabaseTransaction<Schema extends TAnyDataSourceSchema = TAnyDataSourceSchema>
525
+ extends ITransaction {
526
+ connector: TRelationalConnector<Schema>;
527
+ isolationLevel: TIsolationLevel;
528
+ }
529
+ ```
530
+
531
+ | Type | Description |
532
+ |---|---|
533
+ | `ITransactionOptions` | Engine-neutral transaction options - `{ isolationLevel?: string }`. Loose on purpose: isolation levels are engine vocabulary, not universal |
534
+ | `ITransaction` | Engine-neutral contract - `isActive`, `commit()`, `rollback()`. No `connector` field |
535
+ | `IDatabaseTransactionOptions` | PostgreSQL transaction options - narrows `isolationLevel` to `TIsolationLevel`; extends the neutral `ITransactionOptions` |
536
+ | `IDatabaseTransaction<Schema>` | PostgreSQL transaction object - extends `ITransaction` with `connector` and `isolationLevel` |
537
+ | `TIsolationLevel` | Union type: `'READ COMMITTED'` \| `'REPEATABLE READ'` \| `'SERIALIZABLE'` |
538
+ | `IsolationLevels` | Const-class with isolation level constants and validation |
539
+
540
+ > [!NOTE]
541
+ > `AbstractRepository` and every other engine-neutral repository type parameter is named `TOptions` in `src/base`. The PostgreSQL connector's `PostgresBaseRepository` narrows it so repository code bound to a PostgreSQL repository sees `IDatabaseTransaction` (with `connector`/`isolationLevel`) rather than the bare neutral `ITransaction`.
542
+
543
+ ### Isolation levels
544
+
545
+ ```typescript
546
+ import { IsolationLevels } from '@venizia/ignis/postgres';
547
+
548
+ IsolationLevels.READ_COMMITTED // 'READ COMMITTED' - default, prevents dirty reads
549
+ IsolationLevels.REPEATABLE_READ // 'REPEATABLE READ' - consistent reads within the transaction
550
+ IsolationLevels.SERIALIZABLE // 'SERIALIZABLE' - strictest isolation
551
+
552
+ IsolationLevels.isValid('READ COMMITTED'); // true
553
+ IsolationLevels.isValid('INVALID'); // false
554
+ ```
555
+
556
+ > [!NOTE]
557
+ > `READ COMMITTED` is used when `beginTransaction()` is called without an `isolationLevel` option.
558
+
559
+ ### Usage example
560
+
561
+ ```typescript
562
+ import { IsolationLevels } from '@venizia/ignis/postgres';
563
+ import { userTable, profileTable } from '@/schemas';
564
+
565
+ const transaction = await postgresDataSource.beginTransaction({
566
+ isolationLevel: IsolationLevels.SERIALIZABLE,
567
+ });
568
+
569
+ try {
570
+ await transaction.connector.insert(userTable).values({ name: 'Alice' });
571
+ await transaction.connector.insert(profileTable).values({ userId: '...', bio: 'Hello' });
572
+
573
+ await transaction.commit();
574
+ } catch (error) {
575
+ // rollback() throws if ROLLBACK itself fails - nest it so it never replaces the original cause.
576
+ try {
577
+ await transaction.rollback();
578
+ } catch (rollbackError) {
579
+ console.error('Rollback failed | %s', rollbackError);
580
+ }
581
+ throw error;
582
+ }
583
+ ```
584
+
585
+ > [!TIP]
586
+ > For most use cases, prefer `repository.beginTransaction()`, which provides a higher-level API. See [Repositories](/references/base/repositories/#run-inside-a-transaction).
587
+
588
+ This architecture ensures datasources are configured consistently and that the fully-initialized Drizzle connector, aware of all schemas and relations, is available to repositories for querying.
589
+
590
+ ## See also
591
+
592
+ - [DataSources overview](/references/base/datasources) - introduction and common tasks
593
+ - [Tutorial](/guides/core-concepts/persistent/datasources) - creating datasources step by step
594
+ - [Connectors](/references/base/connectors) - the base-vs-connector architecture, dual-door exports
595
+ - [Postgres Drivers & Supabase](/guides/core-concepts/persistent/postgres-drivers) - node-postgres vs. postgres-js, Supabase presets
596
+ - [Repositories](/references/base/repositories/) - the data access layer that consumes a DataSource
597
+ - [Transactions](/guides/core-concepts/persistent/transactions) - multi-operation database transactions
598
+ - [Search & Typesense](/guides/core-concepts/persistent/search-typesense) - the typesense connector
599
+ - [Secrets & Vault](/guides/core-concepts/secrets-vault) - `onSecretRotated()` and credential rotation