@kequtech/tenon-orm 0.2.1 → 0.2.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.
@@ -1,151 +1,135 @@
1
- # Adopting Tenon from another ORM
1
+ # Adopting Tenon
2
2
 
3
- This guide covers replacing an existing ORM and, separately, transferring migration ownership. For a
4
- query tutorial use the [human guide](human-guide.md); for API details use the
5
- [reference](reference.md). Preserve the existing schema, connection configuration, data, and
6
- application behavior while replacing the old ORM. Tenon does not need to own migrations to execute
7
- queries or generate types; keep the existing migration runner if that is the intended scope.
3
+ Replace application queries independently of migration ownership. Tenon can query an existing
4
+ schema and generate types while the current migration runner stays in place. For a Tenon version
5
+ upgrade, read the [Changelog](../CHANGELOG.md).
8
6
 
9
- ## Replace application queries
7
+ ## Preserve connections and behavior
10
8
 
11
- 1. Locate the old ORM's connection setup, schema, migrations, and call sites. Identify behavior
12
- supplied by the ORM: validation, IDs, timestamps, hooks, defaults, relation loading,
13
- serialization, and transaction boundaries.
14
- 2. If row and mutation input types will help your application, generate them from the existing database:
9
+ Keep the existing database URI, credentials, TLS requirements, schema, and data. Tenon can add
10
+ driver settings without parsing the URI:
15
11
 
16
- ```sh
17
- npx tenon generate --out src/generated-schema.d.ts
18
- ```
19
-
20
- Use `--namespace name` for a non-public schema. Generation reads the actual database and requires
21
- no Tenon migration history.
12
+ ```ts
13
+ import { createTenon } from '@kequtech/tenon-orm';
22
14
 
23
- 3. Replace queries with parameterized SQL in ordinary functions with named object inputs where
24
- useful. Preserve each caller's projection, null handling, ordering, and transaction behavior.
25
- Generated types describe Postgres.js values: check bigint/numeric strings, dates, JSON, and arrays when
26
- the old ORM transformed them.
27
- 4. Inspect completed queries and test their behavior using the
28
- [inspection workflow](#inspect-queries-during-adoption) below. Declare result types explicitly
29
- and run tests against a dedicated test database. Verify
30
- writes and rollback, relationships, validation, and any hooks you moved into application code or
31
- SQL. A schema-preserving first release makes application rollback easier.
32
- 5. Remove the old runtime dependency and unused generated code after its callers are gone. Keep the
33
- migration tooling until the migration transition below is complete, if that transition is part of
34
- the work.
15
+ const db = createTenon({
16
+ connection: { url: existingUri, options: { connect_timeout: 5, idle_timeout: 10 } },
17
+ });
18
+ ```
35
19
 
36
- For driver features such as LISTEN or COPY, use [`db.native`](reference.md#native-driver-access).
37
- Preserve the application's notification recovery and resource ownership. Native pool queries do
38
- not join Tenon transactions; use `tx.query` for operations that must commit together.
20
+ Translate old driver configuration into Postgres.js options rather than passing it through.
21
+ For example, pg's `connectionTimeoutMillis: 5000` becomes `connect_timeout: 5`, and
22
+ `idleTimeoutMillis: 10000` becomes `idle_timeout: 10`; both native options use seconds. Preserve
23
+ runtime `search_path` through URI startup parameters or `options.connection.search_path`.
24
+ URI startup parameters take precedence when both are supplied. CLI `--namespace` does not change
25
+ runtime queries. See [connection options](reference.md#installation-and-connection).
39
26
 
40
- ## Replace local write helpers
27
+ Before conversion, identify behavior supplied by the old ORM: IDs/defaults, timestamps,
28
+ validation, hooks, serialization, relationships, and transaction boundaries. Preserve it explicitly
29
+ in application code or SQL where required.
41
30
 
42
- Use `sql.insertValues(rows)` and `sql.assignments(patch)` in place of local object-to-SQL utilities:
31
+ ## Replace queries
43
32
 
44
- ```ts
45
- import { sql } from '@kequtech/tenon-orm';
33
+ 1. Optionally generate persistence types from the existing schema:
46
34
 
47
- const insert = sql`INSERT INTO profiles ${sql.insertValues(rows)} RETURNING id`;
48
- const update = sql`UPDATE profiles SET ${sql.assignments(patch)} WHERE id = ${id}`;
49
- ```
35
+ ```sh
36
+ npx tenon generate --namespace public --out src/generated-schema.d.ts
37
+ ```
50
38
 
51
- Compare omission, defaults, nulls, and empty-input behavior with the old helpers. Keep expressions
52
- in SQL and remove the local utilities after their callers move. Generated `Rows.Insert` and
53
- `Rows.Update` types can replace handwritten table inputs; retain application-specific refinements
54
- and validation. Run the existing write and rollback workflows. See the [mutation cookbook](mutations.md).
39
+ This needs no Tenon migration history. Use the application's namespace and commit exact output.
55
40
 
56
- ## Inspect queries during adoption
41
+ 2. Replace queries with parameterized SQL in ordinary functions accepting `QueryClient` where
42
+ callers may supply `db` or `tx`. Retain projections, ordering, null handling, and authorization.
43
+ 3. Review value conversions against [Tenon's mappings](reference.md#generated-types-and-checking):
44
+ bigint/numeric strings, dates, arrays, and native JSON may differ from the previous driver.
45
+ Generated declarations do not replace domain validation.
46
+ 4. Inspect meaningful completed query variants and run database assertions for actual rows,
47
+ permissions, write effects, and rollback. See [query inspection](query-inspection.md).
48
+ 5. Remove unused runtime dependencies and generated code after their callers are gone. Dependencies
49
+ retained by other application code or installed transitively may still be needed.
57
50
 
58
- Use `describeQuery` from `@kequtech/tenon-orm` on the completed query object, or
59
- `npx tenon describe query.sql` on a SQL file. Inspection is available with the normal Tenon
60
- installation and uses no TypeScript compiler API. It works with helpers and runtime-built
61
- fragments because it receives the final SQL. See [query inspection](query-inspection.md).
51
+ Check cardinality when replacing a first-row accessor. `oneOrNone` rejects multiple rows and `one`
52
+ also rejects zero rows. Use deterministic `ORDER BY` plus `LIMIT 1` when choosing one of several
53
+ matches is intended; modes do not add a limit.
62
54
 
63
- Reuse the application's connection URI and session configuration; compare the reported role,
64
- search path, and timezone with execution. Inspection uses a separate connection and Postgres.js
65
- read mappings. Custom parsers and transaction-local state remain application responsibilities.
55
+ Use `sql.insertValues` and `sql.assignments` to replace object-to-SQL utilities, checking omission,
56
+ default, null, and empty-input semantics. Generated `Rows.Insert`/`Rows.Update` types can replace
57
+ handwritten persistence inputs. See the [mutation cookbook](mutations.md).
66
58
 
67
- Pair descriptions with durable database tests of actual rows and effects. For each converted
68
- query, cover the meaningful filter/ownership branches, null relationships, JSON structure, empty
69
- results, ordering/cursors, and transaction failure/replay behavior as applicable. Describe's success
70
- does not verify a TypeScript annotation or prove that execution is permitted or succeeds. Preserve
71
- existing authorization policy and transaction boundaries while making queries available to tests.
59
+ Use `tx.query` for work that must commit together. Native pool calls do not join Tenon transactions.
60
+ For driver features, preserve resource ownership and recovery policy under the
61
+ [native access contract](reference.md#native-driver-access).
72
62
 
73
- Keep these tests in routine validation. A described query instance does not cover untested source
74
- queries or detect stale annotations elsewhere.
63
+ ## Transfer migration ownership
75
64
 
76
- ## Move migration ownership to Tenon
65
+ Choose one final checkpoint for the old runner. Bring existing databases to it and disable that
66
+ runner before handover; the runners do not share locks. Capture from a trusted reference database,
67
+ preferably a disposable replay of the legacy migrations.
77
68
 
78
- Choose one final schema checkpoint for the old runner and bring existing target databases to it.
79
- Disable its migration/schema synchronization before handing ownership to Tenon. The tools do not
80
- share locks.
69
+ ### Capture a baseline
81
70
 
82
- Capture one baseline from the reference database into an empty migration directory:
71
+ For an application owning `app` in a shared database:
83
72
 
84
73
  ```sh
85
- npx tenon baseline capture --directory migrations --namespace public
74
+ npx tenon baseline capture --directory migrations --namespace app --schema app
86
75
  ```
87
76
 
88
- This uses native `pg_dump --schema-only --no-owner --no-privileges` and writes
89
- `0001_baseline.up.sql` without a down file. It requires a compatible `pg_dump` on PATH; prefer the
90
- server's major. Default scope is the whole database schema. Repeat `--schema name` to select schemas
91
- explicitly, accounting for dependencies, extensions, and required roles. Tenon's selected history
92
- table is excluded; old ORM metadata is preserved for deliberate cleanup later.
93
-
94
- The baseline recreates application schema, not a complete deployment environment. It omits
95
- ownership, grants/revokes, and default privileges. PostgreSQL creates replayed objects under the
96
- migration role. Role definitions and memberships also remain outside the dump. Functions,
97
- policies, or other object definitions can still refer to roles; provision any referenced roles
98
- before replay. See the [PostgreSQL pg_dump reference](https://www.postgresql.org/docs/16/app-pgdump.html).
99
-
100
- For `SECURITY DEFINER` functions, the owner determines execution authority, and newly created
101
- functions ordinarily permit `PUBLIC` execution. A captured function body alone does not preserve
102
- its original security boundary. Keep fixed application policy, such as revoking `PUBLIC` execution,
103
- in reviewed SQL in the baseline before its first adoption or in a subsequent application migration.
104
- Deployment provisioning assigns the intended owners, schema access, and grants to runtime roles.
105
- Keeping all security setup in provisioning is also supported, provided it runs before application access.
106
-
107
- On fresh environments, provision required roles, apply migrations, then apply the deployment's
108
- ownership/grant policy before starting the application. Verify both allowed and denied operations,
109
- including execution of sensitive functions and access to their underlying tables. For a shared
110
- database, apply only the selected application's policy. On existing environments, confirm the same
111
- provisioning procedure is repeatable. After adoption, use subsequent migrations rather than editing
112
- checksummed history.
113
-
114
- Capture changes no source data, migration history, or sequence positions. Review and commit the SQL.
115
- Keep the capture header: it identifies native dump SQL that bypasses the authored-migration scanner.
116
- It is a convention, not proof of provenance; do not use it on handwritten migrations. Normal
117
- transactional execution and checksums still apply.
118
-
119
- On each existing database already at that checkpoint:
120
-
121
- ```sh
122
- npx tenon baseline adopt
123
- ```
77
+ The directory must contain no SQL files. Capture uses native `pg_dump --schema-only --no-owner
78
+ --no-privileges` and writes `0001_baseline.up.sql` with no down file. Install compatible PostgreSQL
79
+ client tools on PATH; prefer the server's major version.
124
80
 
125
- Adoption records the baseline checksum without executing the SQL or comparing schemas. It requires
126
- the selected namespace to exist. Repeating matching adoption is harmless, including after later
127
- migrations; conflicting history fails.
81
+ | Option | Scope |
82
+ | --------------------- | ------------------------------------------------------------------------------------ |
83
+ | `--namespace app` | Tenon history table to exclude from capture; history/runner scope for later commands |
84
+ | `--schema app` | Objects selected by native `pg_dump`; repeat to select additional schemas |
85
+ | Runtime `search_path` | Configured independently on the application connection |
128
86
 
129
- On a fresh database, run migrations instead:
87
+ Omitting `--schema` captures the whole database schema, even with a custom namespace. Capture does
88
+ not modify source data/history or preserve sequence positions. It excludes Tenon's selected history
89
+ table but can retain the old runner's metadata tables. Review and commit the SQL. Preserve the
90
+ capture header, which opts this reviewed dump into native baseline parsing; do not add it to
91
+ handwritten migrations.
130
92
 
131
- ```sh
132
- npx tenon migrate
133
- ```
93
+ A scoped dump can refer to objects it does not include. Provision external schemas, functions,
94
+ extensions, and referenced roles before they are needed. A fresh replay must execute representative
95
+ operations, not just reproduce generated relation types. Tenon requires no particular extension.
134
96
 
135
- That executes the baseline and subsequent migrations. Use the same committed baseline for every
136
- environment. Do not run adoption unconditionally on fresh databases. Existing databases adopt once
137
- and then use ordinary `migrate`.
97
+ ### Check and adopt an existing database
138
98
 
139
- Use the development [`db:migrate` script](human-guide.md#create-a-table) to apply subsequent
140
- migrations and update table types together. Production continues to run only `npx tenon migrate`.
141
- Verify both an existing database's upgrade and a fresh replay. Baselines contain schema only; they
142
- do not back up rows or sequence positions. The missing down file prevents normal rollback through
143
- the baseline. Keep the application's backup and recovery procedure.
99
+ Verify the final legacy checkpoint before adoption. If using schema comparisons, freeze separate
100
+ reference definitions rather than comparing against types regenerated after later migrations.
101
+ `generateDefinitions` covers relation metadata, not functions or privileges; include reviewed
102
+ catalog checks for other objects the application needs. Tenon does not perform these comparisons.
144
103
 
145
- Programmatic equivalents:
104
+ ```sh
105
+ npx tenon baseline adopt --directory migrations --namespace app
106
+ npx tenon migrate --directory migrations --namespace app
107
+ ```
146
108
 
147
- - `captureBaseline({ connection, directory?, namespace?, schemas?, onWarning? })` requires a
148
- nonempty native libpq connection string and returns `{ id, upPath }`. Omit `schemas` for the whole
149
- database schema; an explicitly empty list is rejected.
150
- - `adoptBaseline({ connection, directory?, namespace? })` accepts the usual Tenon connection options
151
- and returns newly recorded migration IDs.
109
+ Adoption records the baseline checksum without executing its SQL. The namespace must already
110
+ exist. Repeating matching adoption is harmless, including after later migrations; history conflicts
111
+ fail. Once Tenon owns history, add forward migrations rather than editing the baseline.
112
+
113
+ ### Replay a fresh database
114
+
115
+ Run `tenon migrate --directory migrations --namespace app`, without adoption. It executes the same
116
+ committed baseline and subsequent migrations. Apply the deployment's required roles, ownership,
117
+ and grants before application access.
118
+
119
+ The portable baseline omits ownership, grants/revokes, and default privileges. Objects are recreated
120
+ under the migration role. For `SECURITY DEFINER` functions, that owner controls execution authority,
121
+ and new functions ordinarily permit `PUBLIC` execution. Keep fixed policy such as revoking public
122
+ execution in reviewed baseline SQL before first adoption or in a forward migration. Deployment
123
+ provisioning supplies named roles, owners, and grants. Verify permitted and denied operations on
124
+ both adopted and fresh environments. See the
125
+ [pg_dump contract](https://www.postgresql.org/docs/16/app-pgdump.html).
126
+
127
+ For development, generate types after migrating; production runs migrations and consumes committed
128
+ types. Test existing-row preservation, repeatable adoption, subsequent migrations, and fresh replay.
129
+ A schema-only baseline and down migration are not backups. The missing down file prevents rollback
130
+ across the baseline; use the application's backup/recovery procedure for that boundary.
131
+
132
+ Programmatic capture is `captureBaseline({ connection, directory?, namespace?, schemas?, onWarning? })`
133
+ and returns `{ id, upPath }`. It requires a native libpq connection string; an explicitly empty
134
+ `schemas` list fails. `adoptBaseline({ connection, directory?, namespace? })` accepts Tenon's runtime
135
+ connection forms and returns newly recorded IDs.