@zerotal/orm 1.6.3 → 1.7.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 +91 -0
- package/api-surface.md +1665 -0
- package/package.json +4 -3
- package/src/commands/MigrateCommand.ts +14 -0
- package/src/commands/_runSeeders.ts +42 -2
- package/src/db/DB.ts +25 -0
- package/src/db/dialects/MysqlDialect.ts +4 -0
- package/src/db/dialects/PostgresDialect.ts +3 -0
- package/src/db/dialects/SqliteDialect.ts +4 -0
- package/src/db/dialects/types.ts +17 -0
- package/src/model/ModelQueryBuilder.ts +16 -6
- package/src/observability.ts +75 -1
- package/src/provider/DatabaseProvider.ts +15 -1
- package/src/schema/MigrationRunner.ts +90 -14
- package/src/schema/Schema.ts +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,97 @@ follows the Zerotal monorepo's unified versioning.
|
|
|
8
8
|
|
|
9
9
|
## [Unreleased]
|
|
10
10
|
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **A seeder that failed partway left its rows behind.** `Seeder.call()` has always wrapped
|
|
14
|
+
*composed* seeders in a transaction, so a `DatabaseSeeder` that delegates was atomic and one that
|
|
15
|
+
does its work inline — which is most of them — was not. A failure on the fourth table committed
|
|
16
|
+
the first three, so the obvious next move, running it again, died on a unique constraint, and the
|
|
17
|
+
only way out was `migrate:fresh`. Migrations became transactional in 1.7.0; this closes the
|
|
18
|
+
asymmetry.
|
|
19
|
+
|
|
20
|
+
`db:seed` now wraps the whole run. Nesting is safe — `DB.transaction` opens a `SAVEPOINT` when one
|
|
21
|
+
is already open, so an inner `call()` still rolls back independently. The wrapper is skipped when
|
|
22
|
+
no connection is bound, because a seeder is not obliged to touch the database and an app that has
|
|
23
|
+
not configured one should not fail to seed over a transaction it never needed.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **`DatabaseProvider` now runs in `worker`, so `zt queue:work` can boot.** It did not, and the
|
|
29
|
+
consequence was total rather than partial: `QueueProvider` _does_ run in `worker`, the
|
|
30
|
+
queue's own default driver is `sqlite`, and so the worker asked for a connection this
|
|
31
|
+
provider had not made and died on startup —
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
error: [Zerotal ORM] No database connection. Is DatabaseProvider registered?
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
— while it plainly was registered.
|
|
38
|
+
|
|
39
|
+
It was never only the queue. Nine providers run in `worker` — notifications, audit, media,
|
|
40
|
+
tenancy, scheduler among them — and a job exists to do work with models. `AuthProvider` and
|
|
41
|
+
`SessionProvider` are absent from `worker` correctly, having neither a request nor a
|
|
42
|
+
session; the ORM being absent was an oversight, dating to 1.0.2.
|
|
43
|
+
|
|
44
|
+
Found building the first cookbook app, whose first queued job could not run.
|
|
45
|
+
|
|
46
|
+
- **Relation keys now accept the JS spelling, like every other identifier.** The convention is
|
|
47
|
+
camelCase in the application and snake_case in the database, converted on the way through —
|
|
48
|
+
and relation keys were the one place it did not happen. `@hasMany(() => Issue, { foreignKey:
|
|
49
|
+
"projectId" })` type-checked and then emitted `no such column: issues.projectId`, with the
|
|
50
|
+
error naming a column rather than the relation that produced it.
|
|
51
|
+
|
|
52
|
+
`_relationSubquery()` builds its subquery with a plain `QueryBuilder`, and the `_column()`
|
|
53
|
+
hook that converts is an override on `ModelQueryBuilder`, so nothing was converting these.
|
|
54
|
+
The keys are now converted where they are qualified, which covers `withCount`, `withSum`,
|
|
55
|
+
`has`/`whereHas` and `withExists` for `hasMany`, `belongsTo`, `manyToMany` and the morph
|
|
56
|
+
relations. Both spellings resolve to the column, so apps already passing `project_id` are
|
|
57
|
+
unaffected.
|
|
58
|
+
|
|
59
|
+
Found building the first cookbook app, where a project list would not count its issues.
|
|
60
|
+
|
|
61
|
+
## [1.7.0] — 2026-08-16
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- **Migrations are now actually transactional.** The runner wrapped each `up()` in
|
|
66
|
+
`begin()` and the docblock promised all-or-nothing, but the wrapper governed nothing:
|
|
67
|
+
`Schema` resolved the _global_ connection, so the migration's DDL ran on a pooled
|
|
68
|
+
connection and committed independently of the transaction around it. On PostgreSQL a
|
|
69
|
+
migration that failed on its third statement left the first two behind, and the enclosing
|
|
70
|
+
`ROLLBACK` had nothing to undo.
|
|
71
|
+
|
|
72
|
+
Three changes close it. `Schema` resolves the enclosing transaction when there is one
|
|
73
|
+
(new `_getScopedDbConnection`), so DDL issued inside `DB.transaction()` joins it —
|
|
74
|
+
migrations included. The tracking-table insert moved _inside_ the transaction, because
|
|
75
|
+
recording after the commit leaves a window where the schema has moved and nothing says so,
|
|
76
|
+
and the next deploy re-runs the migration against a schema it already changed. And
|
|
77
|
+
rollback got the same treatment: a `down()` that fails part-way now undoes nothing rather
|
|
78
|
+
than leaving the schema and the tracking table disagreeing.
|
|
79
|
+
|
|
80
|
+
This was invisible to the test suite by construction — `new SQL(":memory:")` is a single
|
|
81
|
+
handle, so the "global" and transaction connections are the same object and DDL joined the
|
|
82
|
+
transaction by accident. The new tests use a fake that keeps them distinguishable.
|
|
83
|
+
|
|
84
|
+
### Added
|
|
85
|
+
|
|
86
|
+
- **`MigrationRunner.willRollBackOnFailure`** and `SqlDialect.supportsTransactionalDdl`.
|
|
87
|
+
MySQL and MariaDB implicitly commit on every DDL statement, so a transaction around a
|
|
88
|
+
migration there is a promise that cannot be kept — the runner no longer opens one, and
|
|
89
|
+
`bun zt migrate` warns before it starts rather than after something breaks. PostgreSQL
|
|
90
|
+
and SQLite report `true`.
|
|
91
|
+
|
|
92
|
+
- **Two DevTools tabs the ORM already had the data for.** `ModelChanged`,
|
|
93
|
+
`TransactionCommitted` and `TransactionRolledBack` were on the framework event bus and went
|
|
94
|
+
nowhere: a request that wrote four rows and one that wrote none looked identical in the
|
|
95
|
+
panel, and a transaction that rolled back showed only as queries that appeared to succeed.
|
|
96
|
+
|
|
97
|
+
The observability bridge now declares a **Models** channel (grouped per model) and a
|
|
98
|
+
**Transactions** channel (marking a rollback as a warning, with its reason). Both are
|
|
99
|
+
declared as data, so DevTools ships no ORM-specific code — and both are skipped entirely
|
|
100
|
+
when DevTools is not installed, as every other bridge here is.
|
|
101
|
+
|
|
11
102
|
## [1.6.0] — 2026-08-15
|
|
12
103
|
|
|
13
104
|
### Fixed
|