@kequtech/tenon-orm 0.4.2 → 0.4.4
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 +18 -0
- package/README.md +16 -5
- package/dist/check.js +4 -2
- package/dist/cli.js +106 -28
- package/dist/config/connection.d.ts +3 -0
- package/dist/config/connection.js +22 -0
- package/dist/config/init.d.ts +2 -0
- package/dist/config/init.js +31 -0
- package/dist/config/load.d.ts +7 -0
- package/dist/config/load.js +152 -0
- package/dist/config/validate.d.ts +3 -0
- package/dist/config/validate.js +28 -0
- package/dist/config.d.ts +30 -0
- package/dist/config.js +1 -0
- package/dist/connection.d.ts +8 -0
- package/dist/connection.js +1 -1
- package/dist/database.d.ts +3 -7
- package/dist/database.js +22 -19
- package/dist/describe.js +3 -1
- package/dist/generate.d.ts +0 -11
- package/dist/generate.js +1 -26
- package/dist/generation/output.js +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/inspection/cli.d.ts +3 -4
- package/dist/inspection/cli.js +3 -13
- package/dist/inspection/connection.js +23 -19
- package/dist/inspection/options.d.ts +5 -7
- package/dist/inspection/options.js +39 -22
- package/dist/inspection/types.d.ts +3 -2
- package/dist/migrations/history.d.ts +1 -1
- package/dist/migrations/history.js +3 -2
- package/dist/types.d.ts +1 -4
- package/dist/validation.d.ts +2 -0
- package/dist/validation.js +6 -0
- package/docs/adoption-guide.md +9 -3
- package/docs/agent-guide.md +13 -16
- package/docs/configuration.md +211 -0
- package/docs/connections.md +51 -27
- package/docs/human-guide.md +44 -25
- package/docs/migrations.md +13 -6
- package/docs/query-checking.md +14 -11
- package/docs/query-inspection.md +10 -7
- package/docs/reference.md +72 -49
- package/docs/schema-types.md +35 -27
- package/package.json +1 -1
package/docs/reference.md
CHANGED
|
@@ -10,12 +10,19 @@ Requires Node.js 24+, PostgreSQL 16+, and ESM. TypeScript consumers need TypeScr
|
|
|
10
10
|
development typings. Install `@kequtech/tenon-orm`; pgclear is included as a runtime dependency.
|
|
11
11
|
|
|
12
12
|
```text
|
|
13
|
-
createTenon(
|
|
13
|
+
createTenon(config: TenonConfig): Database
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
`connection`
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
Pass one database config directly. Its `connection` holds a PostgreSQL URI, pgclear pool-option
|
|
17
|
+
values, or an application-owned pool. Its optional `onQuery` callback observes runtime queries.
|
|
18
|
+
`ConnectionOptions` aliases pgclear `PoolOptions`; a supplied pool retains application ownership.
|
|
19
|
+
Unknown config keys reject. Connection validation and pool creation wait until the first query,
|
|
20
|
+
transaction, or native-pool access. Closing an unused instance needs no credentials.
|
|
21
|
+
|
|
22
|
+
`tenon init` generates a named `main` config with plain environment reads and no eager credential
|
|
23
|
+
check. Import it and call `createTenon(main)`. Runtime does not discover files. For multiple databases,
|
|
24
|
+
export one config per database and select the matching export in the CLI with `--database`.
|
|
25
|
+
See [shared configuration](configuration.md) for the complete setup.
|
|
19
26
|
|
|
20
27
|
The returned `Database` provides:
|
|
21
28
|
|
|
@@ -23,7 +30,6 @@ The returned `Database` provides:
|
|
|
23
30
|
| ------------------------ | ---------------------------------------------------------------------------------------- |
|
|
24
31
|
| `query(query, options?)` | Execute a Tenon `SqlQuery` and apply result/count options |
|
|
25
32
|
| `transaction(callback)` | Commit on callback success, roll back on an escaping error; return the callback's result |
|
|
26
|
-
| `generateTypes(options)` | Write or check schema declarations using this instance's pool/codecs |
|
|
27
33
|
| `native` | The underlying pgclear pool |
|
|
28
34
|
| `close({ timeoutMs? }?)` | Disable the wrapper and close an owned pool; default grace 5,000 ms |
|
|
29
35
|
|
|
@@ -206,16 +212,17 @@ Use codes and structured fields rather than matching message text. Retain unexpe
|
|
|
206
212
|
|
|
207
213
|
## Generated types and checking
|
|
208
214
|
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
```sh
|
|
216
|
+
tenon generate
|
|
217
|
+
tenon generate --check
|
|
212
218
|
```
|
|
213
219
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
220
|
+
Generation uses [shared configuration](configuration.md) for its connection/codecs, output path,
|
|
221
|
+
and namespace. Missing/stale output rejects under `--check` without writing. See
|
|
222
|
+
[schema types and values](schema-types.md) for file guarantees, generated shapes, and value mappings.
|
|
223
|
+
`generateDefinitions({ connection, namespace?, onWarning? }): Promise<string>` returns source text
|
|
224
|
+
without file handling for custom tooling. Database instances expose query execution and lifecycle
|
|
225
|
+
methods; generation is a CLI tool.
|
|
219
226
|
|
|
220
227
|
### Application aliases
|
|
221
228
|
|
|
@@ -234,15 +241,16 @@ Generated types account for defaults, identity/computed columns, domains, and nu
|
|
|
234
241
|
|
|
235
242
|
## Choosing a validation tool
|
|
236
243
|
|
|
237
|
-
Use the intended development/test schema and role. TypeScript itself
|
|
244
|
+
Use the intended development/test schema and role through shared configuration. TypeScript itself
|
|
245
|
+
needs no connection.
|
|
238
246
|
|
|
239
|
-
| Tool
|
|
240
|
-
|
|
|
241
|
-
| `tsc --noEmit`
|
|
242
|
-
| `tenon check [file.ts[:line]]`
|
|
243
|
-
| `tenon describe query.sql`
|
|
244
|
-
| `
|
|
245
|
-
| Database tests
|
|
247
|
+
| Tool | Question answered | Main limit |
|
|
248
|
+
| ------------------------------ | ------------------------------------------------------------------------------ | --------------------------------------------------------------- |
|
|
249
|
+
| `tsc --noEmit` | Does application code satisfy TypeScript contracts? | Does not inspect SQL/schema |
|
|
250
|
+
| `tenon check [file.ts[:line]]` | Does recoverable SQL contradict declared input/result types? | Unsupported sites fail; inconclusive properties remain unproven |
|
|
251
|
+
| `tenon describe query.sql` | What metadata does PostgreSQL report for finished SQL? | Does not compare annotations or bind values |
|
|
252
|
+
| `tenon generate --check` | Does committed output match generation now? | Does not check queries or replace migration status |
|
|
253
|
+
| Database tests | Do exercised queries produce correct rows, effects, permissions, and rollback? | Cover only tested inputs and paths |
|
|
246
254
|
|
|
247
255
|
After schema changes, migrate the test database, regenerate/review declarations, and run typechecks,
|
|
248
256
|
applicable source checks, and database tests. In CI, check generated output without replacing it.
|
|
@@ -255,7 +263,7 @@ take locks. Neither proves actual bound values or write effects.
|
|
|
255
263
|
The bundled TypeScript compiler loads only through that path or the check command.
|
|
256
264
|
|
|
257
265
|
Options are `{ connection, target?, project?, session?, timeouts?, codecs?, readTypes? }`.
|
|
258
|
-
Connection is a URI. The report contains `ok`, `project`, `queries`, `diagnostics`, and available
|
|
266
|
+
Connection is a URI or pool-option values; connection codecs are inherited unless `codecs` is explicit. The report contains `ok`, `project`, `queries`, `diagnostics`, and available
|
|
259
267
|
inspection `context`. Mismatches, unchecked sites, and no sites fail; unproven findings alone do not.
|
|
260
268
|
See [query checking](query-checking.md) for selection, supported composition, and custom decoders.
|
|
261
269
|
|
|
@@ -265,7 +273,7 @@ See [query checking](query-checking.md) for selection, supported composition, an
|
|
|
265
273
|
describeQuery({ connection, query, session?, timeouts?, codecs? }): Promise<QueryDescription>
|
|
266
274
|
```
|
|
267
275
|
|
|
268
|
-
Connection is a URI; query is finished SQL text or `{ text, parameterTypes? }`. A `SqlQuery` fits
|
|
276
|
+
Connection is a URI or pool-option values; query is finished SQL text or `{ text, parameterTypes? }`. A `SqlQuery` fits
|
|
269
277
|
because it has `text`; values and its row annotation are not checked. The report includes
|
|
270
278
|
`parameters`, `columns`, `rowType`, `issues`, and `context`. It uses a separate session with bounded
|
|
271
279
|
timeouts. See [query inspection](query-inspection.md) for fields and finding codes.
|
|
@@ -279,20 +287,23 @@ prepared statements, and dedicated notification listeners.
|
|
|
279
287
|
|
|
280
288
|
## Troubleshooting
|
|
281
289
|
|
|
282
|
-
| Symptom
|
|
283
|
-
|
|
|
284
|
-
|
|
|
285
|
-
|
|
|
286
|
-
|
|
|
287
|
-
| `
|
|
288
|
-
| `
|
|
289
|
-
|
|
|
290
|
-
|
|
|
291
|
-
|
|
|
292
|
-
|
|
|
293
|
-
|
|
|
294
|
-
|
|
|
295
|
-
|
|
|
290
|
+
| Symptom | Response |
|
|
291
|
+
| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
292
|
+
| Missing shared configuration | Run `tenon init` or select an existing config with `--config`. Generation, checking, and inspection require it. |
|
|
293
|
+
| Multiple databases or unknown database export | Select a named config with `--database name`. Use `--config path` when it belongs to another file. |
|
|
294
|
+
| Missing or empty config connection | Set the environment variable read by that database's config before using it. Imports alone need no credentials. |
|
|
295
|
+
| `DATABASE_URL is required` | Supply the variable for migration commands without config. Tenon does not load `.env` files. |
|
|
296
|
+
| `PGCLEAR_ENCODE` | Check inputs: objects/arrays need `json`/`param`; direct undefined, unsafe integers, nonfinite numbers, and invalid Dates reject. JSON still rejects bigint. |
|
|
297
|
+
| `CardinalityError` | Check the expected row count and predicate. To choose one of several rows, author deterministic `ORDER BY ... LIMIT 1`. |
|
|
298
|
+
| `TENON_AFFECTED_ROWS_MISMATCH` | The guarded operation rolled back. Inspect `actual` and the predicate; retain the intended bound. |
|
|
299
|
+
| `TENON_AFFECTED_ROWS_UNSUPPORTED` | The final command lacks a supported write count. Use guards on INSERT/UPDATE/DELETE/MERGE; result modes check returned rows. |
|
|
300
|
+
| Empty assignments | All selected fields were missing/undefined. Skip the update or branch to an expression-only statement. |
|
|
301
|
+
| Duplicate result columns | Alias columns uniquely, especially in joins. The driver rejects duplicate object keys. |
|
|
302
|
+
| SQLSTATE `42P18` / `42P08` during inspection | Add the intended parameter cast or Describe OID hints. Inspection binds no values. |
|
|
303
|
+
| Checker reports `unchecked` | Follow the diagnostic. Runtime fragments and optional patches can execute correctly yet remain unsupported by source recovery. |
|
|
304
|
+
| Missing/stale generated output | Check schema, namespace, registry, driver version, and path. Regenerate, review, and commit intended output. |
|
|
305
|
+
| Migration history conflict | Restore original applied files. Add new changes in a forward migration. Checksums include down SQL too. |
|
|
306
|
+
| Tooling lock wait | Check concurrent migration/DDL work, use direct or session pooling, and configure server timeouts. |
|
|
296
307
|
|
|
297
308
|
After a guarded query rejects, savepoint cleanup has finished. An unguarded server error normally
|
|
298
309
|
aborts a transaction. A timeout or lost connection does not itself establish whether a commit
|
|
@@ -300,15 +311,25 @@ happened; review the transaction contract before adding retries.
|
|
|
300
311
|
|
|
301
312
|
## Command reference
|
|
302
313
|
|
|
303
|
-
All
|
|
304
|
-
|
|
314
|
+
All commands accept `--config path`. Commands using a database config also accept `--database name`
|
|
315
|
+
to select its export. A file with one config is selected automatically; multiple configs require an
|
|
316
|
+
explicit selection. `generate`, `check`, and `describe` require
|
|
317
|
+
[shared configuration](configuration.md) and use its connection/codecs. Migration and baseline
|
|
318
|
+
commands use config when present and fall back to `DATABASE_URL` only when no config is found.
|
|
319
|
+
`init` creates config without importing application code; `new` uses the configured migration
|
|
320
|
+
directory without opening a database; `--help` loads no config. Config modules may read environment variables, but Tenon loads no `.env`
|
|
321
|
+
files automatically.
|
|
322
|
+
Configured paths resolve from the config file; CLI path flags resolve from the working directory.
|
|
323
|
+
Unsupported command flags reject.
|
|
305
324
|
|
|
306
325
|
```text
|
|
326
|
+
tenon init [--config path]
|
|
307
327
|
tenon new <name> [--directory migrations]
|
|
328
|
+
tenon generate [--out path] [--namespace schema] [--check]
|
|
308
329
|
tenon migrate [--directory migrations] [--namespace public]
|
|
309
330
|
tenon status [--directory migrations] [--namespace public]
|
|
310
331
|
tenon rollback [--directory migrations] [--namespace public] [--steps 1]
|
|
311
|
-
tenon baseline capture [--directory migrations] [--namespace public] [--schema name ...]
|
|
332
|
+
tenon baseline capture [--directory migrations] [--namespace public] [--schema name ...] [--connection native-uri]
|
|
312
333
|
tenon baseline adopt [--directory migrations] [--namespace public]
|
|
313
334
|
tenon check [file.ts[:line]] [--project tsconfig.json] [--json]
|
|
314
335
|
[--role name] [--search-path value] [--time-zone value]
|
|
@@ -317,15 +338,17 @@ tenon describe <query.sql> [--role name] [--search-path value] [--time-zone valu
|
|
|
317
338
|
[--connect-timeout-ms 3000] [--statement-timeout-ms 5000] [--lock-timeout-ms 1000]
|
|
318
339
|
```
|
|
319
340
|
|
|
320
|
-
| Command | Output and behavior
|
|
321
|
-
| ---------------------- |
|
|
322
|
-
| `
|
|
323
|
-
| `
|
|
324
|
-
| `
|
|
325
|
-
| `
|
|
326
|
-
| `baseline
|
|
327
|
-
| `
|
|
328
|
-
| `
|
|
341
|
+
| Command | Output and behavior |
|
|
342
|
+
| ---------------------- | --------------------------------------------------------------------------------------- |
|
|
343
|
+
| `init` | Creates a minimal TypeScript or JavaScript config, refusing to overwrite existing files |
|
|
344
|
+
| `new` | Paths of the new empty up/down pair; fill before applying |
|
|
345
|
+
| `migrate` / `rollback` | Completed IDs or “nothing to do”; each file commits independently |
|
|
346
|
+
| `status` | JSON `{ applied, pending }`; validates history |
|
|
347
|
+
| `baseline capture` | Writes a reviewed schema-only baseline using native `pg_dump` |
|
|
348
|
+
| `baseline adopt` | Records baseline history without executing its SQL |
|
|
349
|
+
| `generate` | Writes output atomically; `--check` rejects missing/stale output without writing |
|
|
350
|
+
| `check` | Diagnostics/counts, or a structured report with `--json` |
|
|
351
|
+
| `describe` | JSON metadata, including unresolved findings |
|
|
329
352
|
|
|
330
353
|
Successful commands exit 0. Invalid input or a failed operation exits 1. `check` also exits 1 for
|
|
331
354
|
mismatches, unchecked sites, or no sites; unproven findings alone do not fail. `describe` can exit 0
|
package/docs/schema-types.md
CHANGED
|
@@ -6,33 +6,35 @@ the selected columns, joins, and expressions.
|
|
|
6
6
|
|
|
7
7
|
## Generate and check a file
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Configure `generate.out` in [shared configuration](configuration.md), then run after migrations
|
|
10
|
+
have committed:
|
|
10
11
|
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
```sh
|
|
13
|
+
npx tenon generate
|
|
14
|
+
npx tenon generate --check
|
|
14
15
|
```
|
|
15
16
|
|
|
16
|
-
The first
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
The first command writes declarations. The second rejects missing or stale output without rewriting
|
|
18
|
+
it. Both use the configured connection and codecs. Relative configured paths resolve from the config
|
|
19
|
+
file; `--out` paths resolve from the working directory. The generation namespace comes from
|
|
20
|
+
`generate.namespace`, then `migrations.namespace`, then `public`; `--namespace` overrides it. It is
|
|
21
|
+
independent of runtime `search_path`.
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
it from automatic formatting.
|
|
23
|
+
Keep exact output unchanged and exclude it from automatic formatting. See
|
|
24
|
+
[getting started](human-guide.md#generate-types) for npm commands.
|
|
23
25
|
|
|
24
|
-
Generation
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
Generation runs outside callback transactions. It uses a repeatable-read catalog snapshot and
|
|
27
|
+
shares the migration advisory lock; use a direct or session-pooled connection. It needs no migration
|
|
28
|
+
files or Tenon history.
|
|
27
29
|
|
|
28
|
-
Tenon creates parent directories and replaces the entire output file atomically. With
|
|
29
|
-
it creates no directories, writes no files, and includes a bounded diff for stale output.
|
|
30
|
-
|
|
31
|
-
handler failures reject generation before output changes. Other filesystem errors propagate.
|
|
30
|
+
Tenon creates parent directories and replaces the entire output file atomically. With `--check`,
|
|
31
|
+
it creates no directories, writes no files, and includes a bounded diff for stale output. The CLI
|
|
32
|
+
prints warnings to stderr. Generation failures preserve the existing file; filesystem errors propagate.
|
|
32
33
|
|
|
33
34
|
For source text without file handling, use
|
|
34
35
|
`generateDefinitions({ connection, namespace?, onWarning? }): Promise<string>`. Pass the configured
|
|
35
|
-
pool or connection options to retain runtime read mappings.
|
|
36
|
+
pool or connection options to retain runtime read mappings. Its optional `onWarning` callback may
|
|
37
|
+
be synchronous or asynchronous and defaults to `console.warn`; callback failures reject generation.
|
|
36
38
|
|
|
37
39
|
## Read the generated types
|
|
38
40
|
|
|
@@ -161,17 +163,22 @@ both become null. For raw JSON text or Date/bigint outputs, configure codecs.
|
|
|
161
163
|
|
|
162
164
|
## Custom codecs
|
|
163
165
|
|
|
164
|
-
Configure the registry
|
|
166
|
+
Configure the registry in `tenon.config.ts` so runtime queries and generated declarations use the
|
|
167
|
+
same representations:
|
|
165
168
|
|
|
166
169
|
```ts
|
|
167
|
-
import { builtin,
|
|
170
|
+
import { builtin, defaultCodecs, type TenonConfig } from '@kequtech/tenon-orm';
|
|
168
171
|
|
|
169
172
|
const codecs = defaultCodecs.with(builtin.int8BigInt).with(builtin.timestamptzDate);
|
|
170
|
-
const
|
|
173
|
+
export const main = {
|
|
174
|
+
connection: { connectionString: process.env.DATABASE_URL, codecs },
|
|
175
|
+
generate: { out: 'src/generated-schema.d.ts' },
|
|
176
|
+
} satisfies TenonConfig;
|
|
171
177
|
```
|
|
172
178
|
|
|
173
|
-
Int8 now reads as bigint, and timestamptz reads as a millisecond-precision Date.
|
|
174
|
-
|
|
179
|
+
Int8 now reads as bigint, and timestamptz reads as a millisecond-precision Date. Run `tenon generate`
|
|
180
|
+
with this config to update declarations. Scalar overrides affect derived built-in arrays; explicit
|
|
181
|
+
array overrides win. Date and
|
|
175
182
|
Temporal output codecs reject infinity and values outside their supported ranges.
|
|
176
183
|
|
|
177
184
|
For Temporal, import `createTemporalCodecs` from Tenon and supply a native or compatible polyfill
|
|
@@ -196,10 +203,11 @@ checking consume the registry's metadata. An unidentified decoder warns and beco
|
|
|
196
203
|
a declared representation is trusted, not runtime validation. Under a custom registry, enum output
|
|
197
204
|
may remain string because a decoder can transform labels; enum write inputs retain catalog choices.
|
|
198
205
|
|
|
199
|
-
|
|
206
|
+
Place the registry in shared `connection.codecs` so runtime, CLI generation, inspection, and source
|
|
207
|
+
checking use it consistently. Programmatic inspection/checking inherit `connection.codecs` too. Relative type-import
|
|
200
208
|
expressions resolve from the generated file for saved declarations and from the tsconfig directory
|
|
201
|
-
for checking. Prefer a package or configured alias that resolves in both places.
|
|
202
|
-
execute referenced modules. See [custom-codec checking](query-checking.md#programmatic-use-and-custom-codecs).
|
|
209
|
+
for checking. Prefer a package or configured alias that resolves in both places. Resolving these
|
|
210
|
+
type expressions does not execute their referenced modules. See [custom-codec checking](query-checking.md#programmatic-use-and-custom-codecs).
|
|
203
211
|
|
|
204
212
|
Catalog queries use default result decoding on the same borrowed session, so application codecs do
|
|
205
213
|
not alter Tenon's metadata inputs. Output codec choices do not select input encoders.
|
|
@@ -256,7 +264,7 @@ application's authentication and authorization checks.
|
|
|
256
264
|
|
|
257
265
|
## What freshness checks establish
|
|
258
266
|
|
|
259
|
-
`check
|
|
267
|
+
`tenon generate --check` compares the exact generated file with output from this database and codec registry.
|
|
260
268
|
It does not check query annotations, pending migrations, functions, privileges, or every database
|
|
261
269
|
object. A driver or generator update can change output without schema drift. Review generated diffs
|
|
262
270
|
after upgrades. Generated text and schema-summary formatting are not version-stable schema
|