@kequtech/tenon-orm 0.3.1 → 0.4.2
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.
- package/CHANGELOG.md +66 -0
- package/README.md +81 -26
- package/dist/affected-rows.js +0 -1
- package/dist/baseline.js +3 -5
- package/dist/check/contracts.d.ts +6 -0
- package/dist/check/contracts.js +206 -0
- package/dist/check/project.d.ts +18 -0
- package/dist/check/project.js +224 -0
- package/dist/check/source.d.ts +51 -0
- package/dist/check/source.js +629 -0
- package/dist/check/types.d.ts +36 -0
- package/dist/check/types.js +1 -0
- package/dist/check.d.ts +4 -0
- package/dist/check.js +147 -0
- package/dist/cli.js +40 -27
- package/dist/connection.d.ts +5 -12
- package/dist/connection.js +12 -121
- package/dist/database.js +10 -8
- package/dist/describe.js +10 -3
- package/dist/generate.d.ts +13 -3
- package/dist/generate.js +52 -25
- package/dist/generation/catalog.d.ts +5 -6
- package/dist/generation/catalog.js +7 -9
- package/dist/generation/output.js +2 -4
- package/dist/generation/render.d.ts +3 -0
- package/dist/generation/render.js +60 -0
- package/dist/generation/schema-summary.d.ts +3 -3
- package/dist/generation/schema-summary.js +2 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/inspection/catalog.d.ts +3 -6
- package/dist/inspection/catalog.js +9 -18
- package/dist/inspection/cli.d.ts +12 -0
- package/dist/inspection/cli.js +9 -4
- package/dist/inspection/connection.d.ts +2 -3
- package/dist/inspection/connection.js +20 -22
- package/dist/inspection/options.js +0 -6
- package/dist/inspection/result.d.ts +3 -2
- package/dist/inspection/result.js +13 -26
- package/dist/inspection/types.d.ts +6 -4
- package/dist/migrations/connection.d.ts +3 -3
- package/dist/migrations/connection.js +14 -21
- package/dist/migrations/history.d.ts +4 -4
- package/dist/migrations/history.js +5 -5
- package/dist/migrations.js +7 -9
- package/dist/query-options.d.ts +1 -1
- package/dist/query.js +1 -1
- package/dist/transaction.d.ts +2 -2
- package/dist/transaction.js +21 -77
- package/dist/types.d.ts +4 -1
- package/docs/adoption-guide.md +88 -111
- package/docs/agent-guide.md +94 -147
- package/docs/connections.md +185 -0
- package/docs/human-guide.md +133 -146
- package/docs/migrations.md +133 -0
- package/docs/mutations.md +110 -60
- package/docs/query-checking.md +199 -0
- package/docs/query-inspection.md +89 -58
- package/docs/reference.md +232 -437
- package/docs/schema-types.md +263 -0
- package/docs/sql.md +212 -0
- package/package.json +11 -7
- package/dist/driver/types.d.ts +0 -10
- package/dist/driver/types.js +0 -49
- package/dist/generation/table-types.d.ts +0 -2
- package/dist/generation/table-types.js +0 -96
- package/dist/inspection/protocol.d.ts +0 -7
- package/dist/inspection/protocol.js +0 -45
package/docs/agent-guide.md
CHANGED
|
@@ -1,167 +1,114 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Agent guide
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
guide
|
|
5
|
-
[human guide](human-guide.md) gives a step-by-step introduction.
|
|
3
|
+
Use this as a short workflow for changing an existing Tenon application. For first setup, follow
|
|
4
|
+
[Getting started](human-guide.md). Exact contracts are in the [reference](reference.md).
|
|
6
5
|
|
|
7
|
-
##
|
|
6
|
+
## Read the project first
|
|
8
7
|
|
|
9
|
-
Inspect
|
|
10
|
-
commands.
|
|
11
|
-
|
|
12
|
-
application.
|
|
8
|
+
Inspect the database module, nearby query functions, migration runner, generated types, and check
|
|
9
|
+
commands. Reuse the application's package manager, connection configuration, namespace, and
|
|
10
|
+
conventions. Read the docs shipped with the installed Tenon version.
|
|
13
11
|
|
|
14
|
-
For an upgrade,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
[adoption guide](adoption-guide.md) when replacing another ORM or transferring migration ownership.
|
|
12
|
+
For an upgrade, find the previous resolved version in the lockfile and read the
|
|
13
|
+
[changelog](../CHANGELOG.md) through the target version and follow its API and driver upgrade
|
|
14
|
+
instructions. Check both Tenon and pgclear versions before changing native
|
|
15
|
+
calls or representations.
|
|
19
16
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
Requires Node.js 24+, PostgreSQL 16+, ESM, TypeScript 6+, and Node development typings. Install with
|
|
23
|
-
`npm install @kequtech/tenon-orm`; retain the project's package manager and TypeScript
|
|
24
|
-
configuration.
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
import { createTenon } from '@kequtech/tenon-orm';
|
|
28
|
-
|
|
29
|
-
const connection = process.env.DATABASE_URL;
|
|
30
|
-
if (!connection) throw new Error('DATABASE_URL is required');
|
|
31
|
-
export const db = createTenon({ connection });
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
`connection` accepts a URI string, pg `PoolConfig` (including `connectionString`), or an existing
|
|
35
|
-
`pg.Pool`. Reuse an instance for each database configuration. `db.close()` ends internally created
|
|
36
|
-
pools with a default 5,000 ms grace period; supplied pools remain application-owned. Configure
|
|
37
|
-
shutdown with `db.close({ timeoutMs })`; concurrent callers share its outcome. Finish outstanding
|
|
38
|
-
work before closing. Tenon loads no environment files. Qualify table names or set runtime
|
|
39
|
-
`search_path`; CLI `--namespace` selects tooling scope, not runtime query names. See
|
|
40
|
-
[connection options](reference.md#installation-and-connection).
|
|
41
|
-
|
|
42
|
-
## Query and result contracts
|
|
17
|
+
## Write a query function
|
|
43
18
|
|
|
44
19
|
```ts
|
|
45
20
|
import { sql, type QueryClient } from '@kequtech/tenon-orm';
|
|
46
|
-
import type {
|
|
21
|
+
import type { Database } from './generated-schema.js';
|
|
47
22
|
|
|
48
|
-
type Summary = Pick<
|
|
23
|
+
type Summary = Pick<Database['users']['row'], 'id' | 'email'>;
|
|
49
24
|
|
|
50
|
-
function userById(client: QueryClient, { id }: { id: number }) {
|
|
25
|
+
export function userById(client: QueryClient, { id }: { id: number }) {
|
|
51
26
|
return client.query(sql<Summary>`SELECT id, email FROM public.users WHERE id = ${id}`, {
|
|
52
27
|
returns: 'oneOrNone',
|
|
53
28
|
});
|
|
54
29
|
}
|
|
55
30
|
```
|
|
56
31
|
|
|
57
|
-
`QueryClient`
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Use
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
Use
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
For an existing user with id 42:
|
|
32
|
+
Use a `QueryClient` parameter when callers may pass `db` or `tx`. Keep SQL and execution options
|
|
33
|
+
together. Qualify table names or follow the configured runtime search path.
|
|
34
|
+
|
|
35
|
+
- Bind values without quotes. Do not mix manual `$n` placeholders into templates.
|
|
36
|
+
- Declare the actual projection with `sql<Row>`. Generated table rows do not describe arbitrary
|
|
37
|
+
joins/aggregates, and annotations do not validate runtime output.
|
|
38
|
+
- Choose `many`, `oneOrNone`, `one`, or default `native` deliberately. Modes add no SQL limits.
|
|
39
|
+
A cardinality error alone does not undo an autocommitted write.
|
|
40
|
+
- Use approved `identifier(...)` names and fixed fragments for SQL syntax. Keep mandatory
|
|
41
|
+
authorization predicates in every branch.
|
|
42
|
+
- Use fragments for real reuse/composition. Runtime loops work but may remain unchecked by the
|
|
43
|
+
optional checker. See [writing queries](sql.md).
|
|
44
|
+
|
|
45
|
+
## Preserve representations and validate inputs
|
|
46
|
+
|
|
47
|
+
Bigint/bigserial and `count(*)` read as exact strings by default; command `rowCount` is a number or
|
|
48
|
+
null. Decimals and temporal values default to strings, bytes to Uint8Array, and JSON to unknown.
|
|
49
|
+
Built-in arrays preserve scalar representations and null elements.
|
|
50
|
+
|
|
51
|
+
Plain bigint, valid Date, and supported Temporal inputs are accepted; inputs do not change output
|
|
52
|
+
decoders. Plain arrays/objects require `json`, `param`, or encoded text. Direct undefined rejects.
|
|
53
|
+
JSON encoding still rejects bigint. Import parameter/codec helpers from Tenon.
|
|
54
|
+
|
|
55
|
+
Use the application's registry for queries and generation, and pass it explicitly to programmatic
|
|
56
|
+
inspection/checking. Retain application validation, authorization, domain conversion, and retry
|
|
57
|
+
policy. See [schema types and values](schema-types.md).
|
|
58
|
+
|
|
59
|
+
## Change data deliberately
|
|
60
|
+
|
|
61
|
+
Use `sql.insertValues` and `sql.assignments` for object fields while keeping table, predicates,
|
|
62
|
+
expressions, conflicts, and `RETURNING` in SQL. Apply generated insert/update types and narrow
|
|
63
|
+
editable fields with `Pick`.
|
|
64
|
+
|
|
65
|
+
Missing/undefined insert fields use defaults; assignment fields are omitted; null remains null.
|
|
66
|
+
Empty batches and assignments reject. Explicit column lists select/order fields and can discard
|
|
67
|
+
required inputs. Batch below the complete query's 65,535-parameter limit. See [writing data](mutations.md).
|
|
68
|
+
|
|
69
|
+
For work that commits together, use `db.transaction` and await every operation through `tx`. A
|
|
70
|
+
callback client expires when it finishes; root/native pool queries do not join it. Tenon owns
|
|
71
|
+
transaction boundaries and provides no automatic retries.
|
|
72
|
+
|
|
73
|
+
Use `affectedRows: { exact: n }` or `{ max: n }` for write bounds. A root guard uses a transaction;
|
|
74
|
+
a callback guard uses a savepoint. A rejected guarded operation rolls back before throwing, so a
|
|
75
|
+
caught guard failure permits continued work. An unguarded server error normally aborts the
|
|
76
|
+
transaction. Counts exclude additional trigger/cascade/CTE effects. A lost commit response can
|
|
77
|
+
leave an unknown outcome. See [transactions](reference.md#transactions-guarded-writes-and-errors).
|
|
78
|
+
|
|
79
|
+
## Make schema changes
|
|
80
|
+
|
|
81
|
+
Keep an existing migration runner. With Tenon's runner, create a new numbered SQL pair, fill both
|
|
82
|
+
files or omit an irreversible down file, and apply it. Never change applied SQL; checksums include
|
|
83
|
+
both directions. Each file commits separately. Use direct or session pooling for tooling advisory
|
|
84
|
+
locks. See [migrations](migrations.md).
|
|
85
|
+
|
|
86
|
+
Generate with the configured database instance, after migrations commit:
|
|
113
87
|
|
|
114
88
|
```ts
|
|
115
|
-
await db.
|
|
116
|
-
await tx.query(sql`UPDATE public.users SET active = false WHERE id = ${42}`, {
|
|
117
|
-
affectedRows: { exact: 1 },
|
|
118
|
-
});
|
|
119
|
-
});
|
|
89
|
+
await db.generateTypes({ out: 'src/generated-schema.d.ts', namespace: 'public' });
|
|
120
90
|
```
|
|
121
91
|
|
|
122
|
-
Use
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Result nullability is unknown and JSON structure is unchecked. A successful description does not
|
|
146
|
-
verify `sql<Row>`, bound values, execution permission, or effects.
|
|
147
|
-
|
|
148
|
-
Pair inspection with database assertions for complete relevant rows, meaningful query variants, and
|
|
149
|
-
rollback. A small query-building function can share the same statement with inspection and
|
|
150
|
-
execution; keep simple queries inline when that is sufficient. See
|
|
151
|
-
[query inspection](query-inspection.md).
|
|
152
|
-
|
|
153
|
-
## Schema changes
|
|
154
|
-
|
|
155
|
-
With Tenon's runner, development can use
|
|
156
|
-
`tenon migrate && tenon generate --out src/generated-schema.d.ts`. Commit migrations and exact
|
|
157
|
-
generated output together; exclude the generated file from automatic formatting. Production runs
|
|
158
|
-
migrations and deploys committed types. `generate --check` detects representation drift, not pending
|
|
159
|
-
migrations or every schema change.
|
|
160
|
-
|
|
161
|
-
All database CLI commands require `DATABASE_URL`. `tenon new <name>` creates empty up/down files
|
|
162
|
-
offline; fill them before migrating. Migration commands use `--directory migrations` and
|
|
163
|
-
`--namespace public` by default; generation also accepts `--namespace`. Pass a custom namespace to
|
|
164
|
-
both migration and generation. Omit the down file for an irreversible migration; never edit applied
|
|
165
|
-
history. Each file commits independently with its history entry. Tools use session advisory locks,
|
|
166
|
-
so use a direct or session-pooled connection. Nontransactional DDL needs a separate procedure. See
|
|
167
|
-
the [command reference](reference.md#command-reference).
|
|
92
|
+
Use the project's [generation script](human-guide.md#generate-types), review the diff, and commit
|
|
93
|
+
migrations with exact generated output. Keep handwritten aliases separate and generated files out
|
|
94
|
+
of formatting. In CI, migrate the test database and use `check: true` without first regenerating.
|
|
95
|
+
Production consumes committed declarations.
|
|
96
|
+
|
|
97
|
+
## Verify the changed workflow
|
|
98
|
+
|
|
99
|
+
Run the project's typecheck and database tests for actual rows, effects, permissions, and rollback.
|
|
100
|
+
Use `tenon check` for source contracts and `tenon describe` for finished SQL metadata when useful.
|
|
101
|
+
See [validation tools](reference.md#choosing-a-validation-tool).
|
|
102
|
+
|
|
103
|
+
The checker fails on definite mismatches and unchecked sites; unproven properties remain notes.
|
|
104
|
+
Fixed composition is supported within bounded variants. Object expansion needs an explicit finite
|
|
105
|
+
shape with required, defined selected fields; optional generated patches may be unchecked. Keep
|
|
106
|
+
intentional omission behavior and test it. Narrow to required fields only when application validation
|
|
107
|
+
establishes that contract; do not cast merely to silence a diagnostic. See
|
|
108
|
+
[responding to findings](query-checking.md#responding-to-findings).
|
|
109
|
+
|
|
110
|
+
Match the runtime role, search path, timezone, and codecs. Describe uses a separate session and
|
|
111
|
+
cannot establish result nullability, JSON members, cardinality, or execution permissions. Native
|
|
112
|
+
calls bypass Tenon's guards/observations and need explicit resource ownership; see
|
|
113
|
+
[connections and driver access](connections.md). For common failures, use
|
|
114
|
+
[troubleshooting](reference.md#troubleshooting).
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Connections and driver access
|
|
2
|
+
|
|
3
|
+
`createTenon` uses a pgclear connection pool. Tenon includes the driver; a separate installation is
|
|
4
|
+
needed only when your application imports native pgclear APIs directly.
|
|
5
|
+
|
|
6
|
+
## Configure a database instance
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import { createTenon } from '@kequtech/tenon-orm';
|
|
10
|
+
|
|
11
|
+
const connection = process.env.DATABASE_URL;
|
|
12
|
+
if (!connection) throw new Error('DATABASE_URL is required');
|
|
13
|
+
|
|
14
|
+
export const db = createTenon({
|
|
15
|
+
connection: {
|
|
16
|
+
connectionString: connection,
|
|
17
|
+
maxConnections: 10,
|
|
18
|
+
connectTimeoutMs: 5000,
|
|
19
|
+
acquireTimeoutMs: 5000,
|
|
20
|
+
idleTimeoutMs: 30_000,
|
|
21
|
+
startupParameters: { search_path: 'app,public', TimeZone: 'UTC' },
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
```
|
|
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.
|
|
29
|
+
|
|
30
|
+
The driver does not load environment variables, `.env`, pgpass, or service files. Tenon CLI commands
|
|
31
|
+
read `DATABASE_URL`; application code passes its configuration explicitly. An empty options object
|
|
32
|
+
uses localhost:5432, the current OS user and a database with that user's name.
|
|
33
|
+
|
|
34
|
+
Use `tls: {}` for TLS with certificate and hostname verification using system trust. Supply the
|
|
35
|
+
provider's CA or other required TLS settings through pgclear's `tls` options. Preserve the
|
|
36
|
+
connection requirements of your deployment. The complete option list is in the installed driver's
|
|
37
|
+
`docs/public-api.md`; unknown options reject.
|
|
38
|
+
|
|
39
|
+
Qualify table names in SQL or set runtime `search_path`. Tooling `namespace` options select the
|
|
40
|
+
schema to migrate or generate; they do not configure your application's queries. The example above
|
|
41
|
+
assumes an `app` schema already exists.
|
|
42
|
+
|
|
43
|
+
## Close resources
|
|
44
|
+
|
|
45
|
+
For a short-lived script, use `try/finally`:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
try {
|
|
49
|
+
// Await database work here.
|
|
50
|
+
} finally {
|
|
51
|
+
await db.close();
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
For a server, stop accepting work and finish requests before closing the database. A close call
|
|
56
|
+
immediately rejects new Tenon work and gives an owned pool a default 5,000 ms grace period. Set
|
|
57
|
+
`db.close({ timeoutMs: 1000 })` to change it; `0` requests immediate shutdown. Remaining driver work
|
|
58
|
+
and checkouts expire at the deadline. This bounds database resources, not arbitrary JavaScript
|
|
59
|
+
callbacks or proof that a remote rollback completed.
|
|
60
|
+
|
|
61
|
+
The first valid close call fixes the timeout. Concurrent and later calls share its promise and
|
|
62
|
+
outcome. Timeout must be an integer from 0 to 2,147,483,647 ms; invalid initial options reject
|
|
63
|
+
without closing the instance.
|
|
64
|
+
|
|
65
|
+
### Use an application-owned pool
|
|
66
|
+
|
|
67
|
+
When another part of the application already owns a pgclear pool:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { createPool } from '@kequtech/pgclear';
|
|
71
|
+
import { createTenon } from '@kequtech/tenon-orm';
|
|
72
|
+
|
|
73
|
+
const pool = createPool(connection);
|
|
74
|
+
const db = createTenon({ connection: pool });
|
|
75
|
+
// db.native === pool
|
|
76
|
+
|
|
77
|
+
await db.close(); // Disables this wrapper; pool remains open.
|
|
78
|
+
await pool.close(); // The application owns the pool's shutdown.
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Tenon borrows the supplied pool by identity. Closing the wrapper does not cancel that pool's
|
|
82
|
+
outstanding work. Tool APIs that accept `Connection` follow the same ownership rule.
|
|
83
|
+
|
|
84
|
+
## Choose output conversions
|
|
85
|
+
|
|
86
|
+
Outputs follow the pool's codec registry. For example, to read int8 as JavaScript bigint and
|
|
87
|
+
`timestamptz` as Date:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
import { builtin, createTenon, defaultCodecs } from '@kequtech/tenon-orm';
|
|
91
|
+
|
|
92
|
+
const codecs = defaultCodecs.with(builtin.int8BigInt).with(builtin.timestamptzDate);
|
|
93
|
+
const db = createTenon({ connection: { connectionString: connection, codecs } });
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
These choices apply across the database instance, including callback transactions. Tenon's query
|
|
97
|
+
options have no per-query codec override. Input encoders remain separate: a `param(value, codec)`
|
|
98
|
+
carries its own encoder, and passing a Date does not select a Date output decoder.
|
|
99
|
+
|
|
100
|
+
Use `db.generateTypes` with this instance to inherit its read mappings. Pass the same registry
|
|
101
|
+
explicitly to `describeQuery` and `checkQueries`; their CLI forms use default codecs. See
|
|
102
|
+
[schema types and values](schema-types.md#custom-codecs) for custom representations and Temporal.
|
|
103
|
+
|
|
104
|
+
## Observe completed queries
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const db = createTenon({
|
|
108
|
+
connection,
|
|
109
|
+
onQuery(event) {
|
|
110
|
+
console.log(event.label, event.durationMs, event.outcome, event.rowCount);
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Set `{ label: 'user-by-email' }` on a query to identify it. `onQuery` receives duration, outcome,
|
|
116
|
+
and available command/count/error-code metadata, with no SQL text or bound values. Validation
|
|
117
|
+
failures before execution are not observed. Native driver calls and tooling are outside this hook.
|
|
118
|
+
|
|
119
|
+
Observers are not awaited. A thrown error or rejected observer promise emits a warning and leaves
|
|
120
|
+
the query outcome unchanged. A successful query event inside a transaction does not mean the outer
|
|
121
|
+
transaction committed. Guarded-call duration includes its commit or rollback work.
|
|
122
|
+
|
|
123
|
+
## Use native driver features
|
|
124
|
+
|
|
125
|
+
`db.native` is the actual pgclear pool. Use it for prepared statements, streams, COPY, or held
|
|
126
|
+
sessions. For a finished SQL string:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
const result = await db.native.queryClassic('SELECT $1::integer AS value', [42]);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Native calls bypass Tenon's result modes, affected-row guards, SQL boundary checks, and `onQuery`.
|
|
133
|
+
They do not join a Tenon callback transaction. A callback's `tx` exposes only `query`.
|
|
134
|
+
|
|
135
|
+
Use the public driver session APIs and release held leases. For example:
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
await db.native.withSession(async (session) => {
|
|
139
|
+
const prepared = await session.prepare('SELECT $1::integer AS value');
|
|
140
|
+
try {
|
|
141
|
+
await prepared.query<{ value: number }>([42]);
|
|
142
|
+
} finally {
|
|
143
|
+
await prepared.close();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Native `query` accepts pgclear's own SQL tag. Tenon's `sql` and `unsafeSql` results belong in
|
|
149
|
+
`db.query`; use `queryClassic` for text to avoid needing a second tag. When both tags are used,
|
|
150
|
+
import the native one as `pgSql`.
|
|
151
|
+
|
|
152
|
+
### Listen for notifications
|
|
153
|
+
|
|
154
|
+
Import `listen` directly from pgclear for a dedicated connection:
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
import { listen } from '@kequtech/pgclear';
|
|
158
|
+
|
|
159
|
+
const listener = await listen(connection, {
|
|
160
|
+
channels: ['users_changed'],
|
|
161
|
+
onNotification: ({ payload }) => console.log(payload),
|
|
162
|
+
reconnect: true,
|
|
163
|
+
});
|
|
164
|
+
// At shutdown:
|
|
165
|
+
await listener.close();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
A listener has a separate lifetime and is not closed by `db.close()`. Notifications are not replayed
|
|
169
|
+
after a disconnection; the application owns its recovery policy.
|
|
170
|
+
|
|
171
|
+
## Update the driver
|
|
172
|
+
|
|
173
|
+
Tenon depends on `@kequtech/pgclear: ^0.3.0`, which permits compatible 0.3.x patch updates. With npm:
|
|
174
|
+
|
|
175
|
+
```sh
|
|
176
|
+
npm update @kequtech/pgclear
|
|
177
|
+
npm ls @kequtech/pgclear
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Review the resolved version and lockfile, regenerate affected declarations, and run application
|
|
181
|
+
checks before committing. A lockfile keeps its chosen version until updated. Pgclear 0.4 is outside
|
|
182
|
+
this range and requires an explicit Tenon compatibility update.
|
|
183
|
+
|
|
184
|
+
If the application imports pgclear directly, declare it as a dependency with a compatible version
|
|
185
|
+
so the package manager can share a copy. See the [changelog](../CHANGELOG.md) when updating Tenon.
|