@kequtech/tenon-orm 0.4.3 → 0.4.5

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.
@@ -1,56 +1,120 @@
1
1
  # Shared runtime and CLI configuration
2
2
 
3
- Keep connection settings and codec choices in one module used by the application and Tenon's CLI.
4
- From the project root, create the configuration file:
3
+ A `TenonConfig` describes one database: its connection, codecs, runtime query observer, and CLI
4
+ defaults. The application and CLI use the same object, so generated types and query checks follow
5
+ the representations returned at runtime.
6
+
7
+ Create the configuration from the project root:
5
8
 
6
9
  ```sh
7
10
  npx tenon init
8
11
  ```
9
12
 
10
- Initialization opens no database connection, imports no application code, and never overwrites a
11
- file. Without `--config`, it refuses when a config is already discoverable in this directory or a
12
- parent. Use `tenon init --config path.mjs` for an explicit additional destination or JavaScript output. The generated TypeScript config looks like this:
13
+ The command writes `tenon.config.ts` with a named `main` export:
13
14
 
14
15
  ```ts
15
16
  import type { TenonConfig } from '@kequtech/tenon-orm';
16
17
 
17
- const connectionString = process.env.DATABASE_URL;
18
- if (!connectionString) throw new Error('DATABASE_URL is required');
19
-
20
- export default {
21
- connection: {
22
- connectionString,
23
- },
18
+ export const main = {
19
+ connection: { connectionString: process.env.DATABASE_URL },
24
20
  migrations: { directory: 'migrations', namespace: 'public' },
25
21
  generate: { out: 'src/generated-schema.d.ts' },
26
22
  check: { project: 'tsconfig.json' },
27
23
  } satisfies TenonConfig;
28
24
  ```
29
25
 
30
- Add the application's registry as `connection.codecs` when choosing custom representations.
26
+ Add `connection.codecs` when choosing custom representations. Initialization needs no credentials,
27
+ imports no application code, and never overwrites a file. It refuses to create a default config when
28
+ one is already discoverable in the current directory or a parent. Use `--config path.mjs` for an
29
+ explicit destination or JavaScript output.
31
30
 
32
- `TenonConfig` and `ConnectionConfig` are exported TypeScript types; configuration is a plain
33
- default-exported object. Use that connection when constructing the application instance:
31
+ Import the config into the application and pass it directly to `createTenon`:
34
32
 
35
33
  ```ts
36
34
  // src/db.ts
37
35
  import { createTenon } from '@kequtech/tenon-orm';
38
- import config from '../tenon.config.ts';
36
+ import { main } from '../tenon.config.ts';
39
37
 
40
- export const db = createTenon({ connection: config.connection });
38
+ export const db = createTenon(main);
41
39
  ```
42
40
 
43
- The `.ts` imports above run directly with Node.js 24+ TypeScript support. For typechecking with
44
- `noEmit`, enable `allowImportingTsExtensions`; emitting projects can use
45
- `rewriteRelativeImportExtensions`. Config and its runtime imports must be runnable by Node without
46
- bundler aliases or transformations. ESM JavaScript is also supported.
41
+ ## Initialization and imports
42
+
43
+ Constructing `db` checks the config's shape without validating credentials or creating a pool. The
44
+ first query or transaction initializes one pool, which subsequent and concurrent calls reuse.
45
+ Accessing `db.native` also initializes the pool and validates its settings; the driver connects when
46
+ an operation needs a session. Closing an unused instance creates no pool and needs no credentials.
47
+
48
+ Read environment values in the config without a top-level credential check. This lets builds import
49
+ application modules without database access. An undefined or empty `connection`, or an explicitly
50
+ undefined or empty `connectionString`, fails when the database is used and never selects the driver's
51
+ localhost defaults. Environment values are captured when the module is imported, so supply runtime
52
+ variables before starting the application. Queries performed during a build still need credentials.
47
53
 
48
- Export configuration values, not an open pool, database instance, or factory. Importing the config
49
- must not start the server, open listeners, or depend on generated runtime files. Tenon opens and
50
- closes its own tooling resources. `createTenon` still accepts application-owned pools separately.
54
+ The `.ts` imports above run with Node.js 24+ TypeScript support. For typechecking with `noEmit`, enable
55
+ `allowImportingTsExtensions`; emitting projects can use `rewriteRelativeImportExtensions`. Config
56
+ files and their imports must run in Node without bundler aliases or transformations. ESM JavaScript
57
+ is also supported.
58
+
59
+ Keep config files free of resource initialization: export connection values and codecs, and let Tenon
60
+ open and close its tooling resources. The application reuses its database instance and closes it at
61
+ shutdown. Programmatic configs can also borrow an existing pool; see [pool ownership](connections.md#use-an-application-owned-pool).
62
+
63
+ ## Multiple databases
64
+
65
+ Export one config per database from the same file. For example, add `analytics` beside `main` in
66
+ `tenon.config.ts`:
67
+
68
+ ```ts
69
+ export const analytics = {
70
+ connection: { connectionString: process.env.ANALYTICS_DATABASE_URL },
71
+ migrations: { directory: 'analytics/migrations', namespace: 'public' },
72
+ generate: { out: 'src/analytics-schema.d.ts' },
73
+ check: { project: 'tsconfig.json' },
74
+ } satisfies TenonConfig;
75
+ ```
76
+
77
+ Each export has its own connection settings and codecs. Use the same `connection.codecs` value when
78
+ databases share a registry, and give each database its own migrations and generated output.
79
+ Application code selects the export through an ordinary import:
80
+
81
+ ```ts
82
+ import { createTenon } from '@kequtech/tenon-orm';
83
+ import { main, analytics } from '../tenon.config.ts';
84
+
85
+ export const db = createTenon(main);
86
+ export const analyticsDb = createTenon(analytics);
87
+ ```
88
+
89
+ The CLI selects the corresponding export with `--database`:
90
+
91
+ ```sh
92
+ npx tenon migrate --database main
93
+ npx tenon migrate --database analytics
94
+ npx tenon generate --database analytics
95
+ npx tenon check src/analytics/queries.ts --database analytics
96
+ npx tenon new add_events --database analytics
97
+ ```
98
+
99
+ A file with one config is selected automatically. Multiple database exports require `--database`;
100
+ an unknown name fails without selecting another connection. Named objects with a `connection` field
101
+ are database configs, while other helper exports are ignored. Existing default exports remain
102
+ supported and can be selected explicitly as `--database default`.
103
+
104
+ Only the selected config's settings are validated, although importing the file evaluates the whole
105
+ module. Keep credentials as plain environment reads so one database does not require another's
106
+ credentials. Runtime selection uses the imported object and does not depend on the working directory.
107
+ Close each instance separately. For databases with different schemas, use separate checker projects
108
+ or select the relevant query file when running `tenon check`.
109
+
110
+ `--config` selects a file and `--database` selects an export within it. For example,
111
+ `tenon init --config config/tenon.production.ts` creates another file, and
112
+ `tenon migrate --config config/tenon.production.ts --database main` selects its `main` export.
51
113
 
52
114
  ## Run commands
53
115
 
116
+ With the single config generated by `tenon init`:
117
+
54
118
  ```sh
55
119
  npx tenon new create_users
56
120
  npx tenon migrate
@@ -61,86 +125,87 @@ npx tenon check src/users.ts:23 --json
61
125
  npx tenon describe query.sql
62
126
  ```
63
127
 
64
- `generate`, `check`, and `describe` require configuration. They use its connection and codec registry
65
- and never fall back to `DATABASE_URL` merely because a config is missing or invalid. Your config
66
- chooses which environment variables to read; Tenon does not load `.env` files automatically.
128
+ Generation, checking, and inspection require configuration and use its connection and codecs. A
129
+ missing or invalid config fails instead of falling back to `DATABASE_URL`. The config chooses which
130
+ environment variables to read; Tenon does not load `.env` files.
67
131
 
68
- `new`, migration/status/rollback, and baseline commands also discover configuration. Without a
69
- config, they retain the existing migration-directory defaults and `DATABASE_URL` connection
70
- workflow. `init`, `new`, and `--help` open no database connection. Config imports may still require environment
71
- values, according to the module you wrote. Invalid session/timeout settings are rejected when config
72
- loads, even for commands that do not inspect queries. Migration SQL and history reads use default decoding;
73
- application codec overrides do not change migration bookkeeping.
132
+ Migration creation, migration/status/rollback, and baseline commands also discover configuration.
133
+ Without a config or an explicit `--database`, they retain the migration-directory defaults and
134
+ `DATABASE_URL` workflow. `init`, `new`, and `--help` need no database credentials. `new` reads migration
135
+ settings without validating the connection; `--help` loads no config. Any eager checks in your own
136
+ config module still run when that module is imported.
137
+
138
+ The CLI validates the selected config's tooling sections when loading it, including session and
139
+ timeout settings for commands that do not inspect queries. Migration SQL and history reads use
140
+ default decoding, independently of application codec overrides.
74
141
 
75
142
  ## Discovery, paths, and overrides
76
143
 
77
- Tenon searches the working directory and then parents for the nearest directory containing
78
- `tenon.config.ts`, `.mts`, `.js`, or `.mjs`. Multiple recognized files in that directory are an error.
79
- Select one explicitly when needed:
144
+ Tenon searches the working directory and then its parents for the nearest directory containing
145
+ `tenon.config.ts`, `.mts`, `.js`, or `.mjs`. Multiple recognized files in that directory require an
146
+ explicit selection:
80
147
 
81
148
  ```sh
82
149
  npx tenon generate --config config/tenon.production.mts --check
83
150
  ```
84
151
 
85
- `--config` resolves from the working directory. An explicit missing file or an import failure is an
86
- error; Tenon never switches to another configuration after that failure.
152
+ `--config` resolves from the working directory. A missing file, missing database export, or import
153
+ failure is an error; Tenon does not switch to another configuration after a failure.
87
154
 
88
- Paths stored in config resolve from the config file's directory. Configured migration directories,
89
- generation output, and checker projects therefore work when a command runs from a nested directory.
90
- An omitted migration directory means `migrations` beside the config; an omitted checker project
91
- means `tsconfig.json` beside it. Relative CLI path arguments and flags resolve from the working
92
- directory. CLI flags override the corresponding configured values.
155
+ Configured paths resolve from the config file's directory. An omitted migration directory means
156
+ `migrations` beside the config, and an omitted checker project means `tsconfig.json` beside it. CLI
157
+ path flags resolve from the working directory and override the corresponding configured values.
93
158
 
94
159
  ## Configuration fields
95
160
 
96
- | Field | Purpose |
97
- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
98
- | `connection` | Required PostgreSQL URI or pgclear pool-option values, including TLS, password callbacks, startup parameters, and codecs. Open pools are rejected. |
99
- | `migrations.directory` | Migration files; defaults to `migrations` relative to config. |
100
- | `migrations.namespace` | Migration/history schema; defaults to `public`. Also supplies the generation namespace when generation does not specify one. |
101
- | `generate.out` | Required when a `generate` section is present. Otherwise supply `--out` to the generation command. |
102
- | `generate.namespace` | Schema to generate; overrides the migration namespace for generation only. |
103
- | `baseline.schemas` | Optional nonempty list of schemas for native baseline capture. Omission captures the whole database, independently of the migration namespace. |
104
- | `baseline.connection` | Optional native libpq connection for baseline capture; `--connection` overrides it. |
105
- | `check.project` | TypeScript project; defaults to `tsconfig.json` relative to config. |
106
- | `check.readTypes` | Explicit type expressions for custom decoders whose representations are unidentified. See [checking](query-checking.md#programmatic-use-and-custom-codecs). |
107
- | `session` | Optional inspection/checking `role`, `searchPath`, and `timeZone` overrides. They do not configure runtime queries or migrations. |
108
- | `timeouts` | Optional inspection/checking `connectMs`, `statementMs`, and `lockMs` bounds. |
109
-
110
- Generation and runtime queries use `connection.codecs`. Checking and inspection inherit that registry
111
- too. Their internal catalog queries keep default decoding. Runtime startup parameters are retained,
112
- with inspection's statement/lock timeout bounds applied regardless of startup-parameter casing; explicit `session` settings and CLI flags
113
- then override role, search path, or timezone for inspection. See [connections](connections.md) and
114
- [inspection limits](query-inspection.md#session-and-timeouts).
115
-
116
- Keep credentials and deployment-specific addresses in environment variables or the existing secret
117
- provider. For a migration role different from the application role, select a separate config that
118
- imports shared settings and supplies that role's connection. Avoid duplicating codec definitions.
119
-
120
- Baseline capture uses native `pg_dump`, which accepts a libpq connection string rather than pgclear
121
- option objects or password callbacks. Configure its native connection and scope explicitly when
122
- needed:
161
+ | Field | Purpose |
162
+ | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
163
+ | `connection` | PostgreSQL URI or pgclear pool options, including TLS, password callbacks, startup parameters, and codecs. Required when the database is used. |
164
+ | `onQuery` | Optional observer for completed runtime queries. CLI tooling and native calls do not invoke it. |
165
+ | `migrations.directory` | Migration files; defaults to `migrations` beside the config. |
166
+ | `migrations.namespace` | Migration/history schema; defaults to `public`. Also supplies the generation namespace unless overridden. |
167
+ | `generate.out` | Required when `generate` is present. Otherwise supply `--out`. |
168
+ | `generate.namespace` | Schema to generate; overrides the migration namespace for generation. |
169
+ | `baseline.schemas` | Optional nonempty schema list for native baseline capture. Omission captures the whole database. |
170
+ | `baseline.connection` | Optional native libpq connection for baseline capture; `--connection` overrides it. |
171
+ | `check.project` | TypeScript project; defaults to `tsconfig.json` beside the config. |
172
+ | `check.readTypes` | Fallback type expressions for unidentified decoder representations. See [query checking](query-checking.md#programmatic-use-and-custom-codecs). |
173
+ | `session` | Inspection/checking overrides for `role`, `searchPath`, and `timeZone`. |
174
+ | `timeouts` | Inspection/checking bounds for `connectMs`, `statementMs`, and `lockMs`. |
175
+
176
+ Generation, checking, and inspection inherit `connection.codecs`; their internal catalog queries
177
+ keep default decoding. Runtime startup parameters are retained. Inspection applies its statement
178
+ and lock timeout bounds regardless of startup-parameter casing, then applies explicit session and
179
+ CLI overrides. These inspection settings do not change runtime queries or migrations. See
180
+ [connection settings](connections.md) and [inspection limits](query-inspection.md#session-and-timeouts).
181
+
182
+ Keep credentials and deployment addresses in environment variables or the existing secret provider.
183
+ For a separate migration role, add a config export with that role's connection and the shared codecs.
184
+
185
+ ## Native baseline capture
186
+
187
+ Baseline capture uses `pg_dump`, which requires a native libpq connection string. If the main
188
+ connection uses a driver-options object, add the native URI to the config:
123
189
 
124
190
  ```ts
125
- // Additional fields in the shared config:
126
- baseline: { connection: connectionString, schemas: ['public'] }
191
+ // Within the database config:
192
+ baseline: { connection: process.env.DATABASE_URL, schemas: ['public'] }
127
193
  ```
128
194
 
129
195
  Capture uses `--connection`, then `baseline.connection`, then a string main connection or the
130
196
  no-config `DATABASE_URL` fallback. `--schema` flags replace `baseline.schemas`. Adoption ignores
131
- capture-only settings. Supply the same database and required native TLS/authentication settings;
132
- Tenon does not translate driver-specific settings into libpq settings.
197
+ capture-only settings. Supply the same database and its required native TLS/authentication settings;
198
+ Tenon does not translate driver options into libpq settings.
133
199
 
134
200
  ## Programmatic APIs
135
201
 
136
- The CLI owns generated-file output and freshness checks. `generateDefinitions` returns source text
137
- without file handling for callers building custom tooling; it accepts explicit connection/options.
202
+ `createTenon(config)` accepts the shared object and initializes its connection lazily. Other
203
+ programmatic APIs take explicit connection and operation options; none discover config files.
138
204
 
139
- `describeQuery` and `checkQueries` accept a URI or connection-option values and inherit
140
- `connection.codecs`. An explicit `codecs` argument selects the read mappings reported by those
141
- inspection APIs. They own separate, short-lived sessions and do not join callback transactions.
142
- Migration APIs continue accepting explicit connection/options. No programmatic API discovers config.
205
+ The CLI owns generated-file output and freshness checks. `generateDefinitions` returns source text
206
+ for custom tooling without writing files. `describeQuery` and `checkQueries` inherit codecs from
207
+ connection-option values; an explicit `codecs` argument selects their reported read mappings. These
208
+ inspection APIs own separate, short-lived sessions and do not join callback transactions.
143
209
 
144
- The CLI imports the selected trusted config and its dependencies. Source checking subsequently reads
145
- query source without importing those query modules or executing application SQL. TypeScript loads
146
- only for source checking, not for normal queries, migrations, generation, or inspection.
210
+ The CLI imports the selected trusted config and its dependencies. Source checking then reads query
211
+ modules without importing them or executing their SQL. TypeScript loads only for source checking.
@@ -5,32 +5,49 @@ needed only when your application imports native pgclear APIs directly.
5
5
 
6
6
  ## Configure a database instance
7
7
 
8
- ```ts
9
- import { createTenon } from '@kequtech/tenon-orm';
8
+ Define connection settings in the shared config:
10
9
 
11
- const connection = process.env.DATABASE_URL;
12
- if (!connection) throw new Error('DATABASE_URL is required');
10
+ ```ts
11
+ // tenon.config.ts
12
+ import type { TenonConfig } from '@kequtech/tenon-orm';
13
13
 
14
- export const db = createTenon({
14
+ export const main = {
15
15
  connection: {
16
- connectionString: connection,
16
+ connectionString: process.env.DATABASE_URL,
17
17
  maxConnections: 10,
18
18
  connectTimeoutMs: 5000,
19
19
  acquireTimeoutMs: 5000,
20
20
  idleTimeoutMs: 30_000,
21
21
  startupParameters: { search_path: 'app,public' },
22
22
  },
23
- });
23
+ } satisfies TenonConfig;
24
24
  ```
25
25
 
26
- `connection` accepts a PostgreSQL URI, pgclear `PoolOptions`, or an existing pgclear `Pool`.
27
- `ConnectionOptions` is Tenon's alias for `PoolOptions`. Object fields override URI settings.
28
- Reuse an instance for each database configuration.
26
+ Pass the config to the constructor:
29
27
 
30
- The driver does not load environment variables, `.env`, pgpass, or service files.
31
- [Shared configuration](configuration.md) supplies connection values to the CLI and application.
32
- Migration commands retain `DATABASE_URL` fallback when no config exists. An empty options object
33
- uses localhost:5432, the current OS user and a database with that user's name.
28
+ ```ts
29
+ import { createTenon } from '@kequtech/tenon-orm';
30
+ import { main } from '../tenon.config.ts';
31
+
32
+ export const db = createTenon(main);
33
+ ```
34
+
35
+ `createTenon` takes one `TenonConfig` object. Connection validation and pool creation wait until the
36
+ first query, transaction, or `db.native` access. The pool connects when it needs a session. Closing an
37
+ unused instance does not resolve its connection. Subsequent operations reuse the same pool.
38
+
39
+ The config's `connection` field accepts a PostgreSQL URI or pgclear pool-option values.
40
+ `ConnectionOptions` is Tenon's alias for `PoolOptions`. Object fields override URI settings. A
41
+ programmatically constructed config may also supply an existing pool, preserving its ownership.
42
+
43
+ Keep codecs in `connection.codecs` so runtime and tooling use the same representations. See
44
+ [shared configuration](configuration.md) for build-safe environment values and
45
+ [multiple databases](configuration.md#multiple-databases) for named exports and CLI selection.
46
+
47
+ The config supplies environment values to the driver, which does not load `.env`, pgpass, or service
48
+ files. An explicitly missing URI fails when the instance is used. Deliberately configuring
49
+ `connection: {}` uses the driver's local defaults: localhost:5432, the current OS user, and a database
50
+ with that user's name.
34
51
 
35
52
  Use `tls: {}` for TLS with certificate and hostname verification using system trust. Supply the
36
53
  provider's CA or other required TLS settings through pgclear's `tls` options. Preserve the
@@ -68,10 +85,9 @@ without closing the instance.
68
85
  When another part of the application already owns a pgclear pool:
69
86
 
70
87
  ```ts
71
- import { createPool } from '@kequtech/pgclear';
72
88
  import { createTenon } from '@kequtech/tenon-orm';
89
+ import { pool } from './existing-pool.js';
73
90
 
74
- const pool = createPool(connection);
75
91
  const db = createTenon({ connection: pool });
76
92
  // db.native === pool
77
93
 
@@ -84,37 +100,42 @@ outstanding work. Tool APIs that accept `Connection` follow the same ownership r
84
100
 
85
101
  ## Choose output conversions
86
102
 
87
- Outputs follow the pool's codec registry. For example, to read int8 as JavaScript bigint and
88
- `timestamptz` as Date:
103
+ Outputs follow the configured codec registry. To read int8 as JavaScript bigint and `timestamptz` as
104
+ Date, set the registry in `tenon.config.ts`:
89
105
 
90
106
  ```ts
91
- import { builtin, createTenon, defaultCodecs } from '@kequtech/tenon-orm';
107
+ import { builtin, defaultCodecs, type TenonConfig } from '@kequtech/tenon-orm';
92
108
 
93
109
  const codecs = defaultCodecs.with(builtin.int8BigInt).with(builtin.timestamptzDate);
94
- const db = createTenon({ connection: { connectionString: connection, codecs } });
110
+ export const main = {
111
+ connection: { connectionString: process.env.DATABASE_URL, codecs },
112
+ } satisfies TenonConfig;
95
113
  ```
96
114
 
97
115
  These choices apply across the database instance, including callback transactions. Tenon's query
98
116
  options have no per-query codec override. Input encoders remain separate: a `param(value, codec)`
99
117
  carries its own encoder, and passing a Date does not select a Date output decoder.
100
118
 
101
- Place the registry in shared `connection.codecs` for runtime queries and CLI generation, inspection,
102
- and checking. Programmatic
103
- `describeQuery` and `checkQueries` inherit codecs from connection-option values too. See
119
+ The CLI uses that registry for generation, inspection, and checking. Programmatic `describeQuery`
120
+ and `checkQueries` inherit codecs from connection-option values too. See
104
121
  [schema types and values](schema-types.md#custom-codecs) for custom representations and Temporal.
105
122
 
106
123
  ## Observe completed queries
107
124
 
125
+ Set `onQuery` on the database config:
126
+
108
127
  ```ts
109
- const db = createTenon({
110
- connection,
128
+ import type { TenonConfig } from '@kequtech/tenon-orm';
129
+
130
+ export const main = {
131
+ connection: { connectionString: process.env.DATABASE_URL },
111
132
  onQuery(event) {
112
133
  console.log(event.label, event.durationMs, event.outcome, event.rowCount);
113
134
  },
114
- });
135
+ } satisfies TenonConfig;
115
136
  ```
116
137
 
117
- Set `{ label: 'user-by-email' }` on a query to identify it. `onQuery` receives duration, outcome,
138
+ Set `{ label: 'user-by-email' }` on a query to identify it. The observer receives duration, outcome,
118
139
  and available command/count/error-code metadata, with no SQL text or bound values. Validation
119
140
  failures before execution are not observed. Native driver calls and tooling are outside this hook.
120
141
 
@@ -153,26 +174,36 @@ import the native one as `pgSql`.
153
174
 
154
175
  ### Listen for notifications
155
176
 
156
- Import `listen` directly from pgclear for a dedicated connection:
177
+ During application startup, pass a validated PostgreSQL URI (`connection` below) to pgclear's
178
+ `listen` for a dedicated notification connection:
157
179
 
158
180
  ```ts
159
181
  import { listen } from '@kequtech/pgclear';
160
182
 
161
183
  const listener = await listen(connection, {
162
- channels: ['users_changed'],
184
+ channels: [],
163
185
  onNotification: ({ payload }) => console.log(payload),
164
186
  reconnect: true,
165
187
  });
188
+ // Membership can change on this same connection. Await server registration:
189
+ await listener.subscribe('users_changed');
190
+ // When the channel is no longer needed:
191
+ await listener.unsubscribe('users_changed');
166
192
  // At shutdown:
167
193
  await listener.close();
168
194
  ```
169
195
 
170
- A listener has a separate lifetime and is not closed by `db.close()`. Notifications are not replayed
171
- after a disconnection; the application owns its recovery policy.
196
+ A listener has a separate lifetime and is not closed by `db.close()`. Subscription changes share its
197
+ dedicated connection; reconnect restores the last acknowledged channel set. Subscriptions are set
198
+ operations: repeated subscribes do not add reference counts. Applications own subscriber counts,
199
+ authorization, and routing. Notifications are not replayed after a disconnection; use the listener's
200
+ status/generation changes to detect a possible gap and reconcile persistent state. See
201
+ [pgclear's notification guide](https://github.com/kequtech/pgclear/blob/main/docs/notifications.md)
202
+ for deadlines, cancellation, and changes queued during reconnect.
172
203
 
173
204
  ## Update the driver
174
205
 
175
- Tenon depends on `@kequtech/pgclear: ^0.3.0`, which permits compatible 0.3.x patch updates. With npm:
206
+ Tenon depends on `@kequtech/pgclear: ^0.3.3`, which permits compatible 0.3.x patch updates. With npm:
176
207
 
177
208
  ```sh
178
209
  npm update @kequtech/pgclear
@@ -34,10 +34,8 @@ The command writes `tenon.config.ts` without opening a database or overwriting e
34
34
  // tenon.config.ts
35
35
  import type { TenonConfig } from '@kequtech/tenon-orm';
36
36
 
37
- const connectionString = process.env.DATABASE_URL;
38
- if (!connectionString) throw new Error('DATABASE_URL is required');
39
- export default {
40
- connection: { connectionString },
37
+ export const main = {
38
+ connection: { connectionString: process.env.DATABASE_URL },
41
39
  migrations: { directory: 'migrations', namespace: 'public' },
42
40
  generate: { out: 'src/generated-schema.d.ts' },
43
41
  check: { project: 'tsconfig.json' },
@@ -49,14 +47,20 @@ Use it in the application too:
49
47
  ```ts
50
48
  // src/db.ts
51
49
  import { createTenon } from '@kequtech/tenon-orm';
52
- import config from '../tenon.config.ts';
50
+ import { main } from '../tenon.config.ts';
53
51
 
54
- export const db = createTenon({ connection: config.connection });
52
+ export const db = createTenon(main);
55
53
  ```
56
54
 
57
55
  These `.ts` imports use Node.js 24+ TypeScript support. See
58
56
  [shared configuration](configuration.md) for import conventions, custom codecs, discovery, and overrides.
59
57
 
58
+ Importing the config and constructing `db` need no database credentials. The first query or transaction
59
+ validates the connection and initializes its pool; `db.native` access also initializes it. Keep
60
+ database operations out of module imports that run during builds. For another database, add a named
61
+ config export in the same file, pass it to `createTenon`, and select that export in CLI commands with
62
+ `--database`.
63
+
60
64
  Reuse `db` across requests. Call `await db.close()` when a standalone script finishes or a server
61
65
  shuts down. See [connections](connections.md) for pool options and application-owned pools.
62
66
 
@@ -9,9 +9,9 @@ compare TypeScript source annotations instead, use [query checking](query-checki
9
9
  ```ts
10
10
  // scripts/inspect-query.ts
11
11
  import { describeQuery, sql } from '@kequtech/tenon-orm';
12
- import config from '../tenon.config.ts';
12
+ import { main } from '../tenon.config.ts';
13
13
 
14
- const connection = config.connection;
14
+ const connection = main.connection;
15
15
 
16
16
  const query = sql<{ id: number; total: string }>`
17
17
  SELECT ${42}::integer AS id, count(*) AS total FROM pg_catalog.pg_class