@zerotal/core 1.6.2 → 1.7.0
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 +99 -1
- package/api-surface.md +3609 -0
- package/package.json +2 -1
- package/src/application/Application.ts +121 -8
- package/src/command/builtin/DoctorCommand.ts +53 -6
- package/src/command/builtin/ServeCommand.ts +6 -1
- package/src/dev/DevOrchestrator.ts +29 -0
- package/src/doctor/HeaderProbe.ts +164 -0
- package/src/events/Emitter.ts +24 -0
- package/src/events/FrameworkEvents.ts +42 -0
- package/src/index.ts +2 -0
- package/src/middleware/BaseMiddleware.ts +12 -1
- package/src/middleware/SecureHeadersMiddleware.ts +54 -23
- package/src/provider/StorageProvider.ts +4 -1
- package/src/router/RouteHandler.ts +5 -0
- package/src/router/Router.ts +8 -2
- package/src/security/index.ts +6 -0
- package/src/security/redactGraph.ts +98 -0
- package/src/support/deepMerge.ts +42 -2
- package/src/support/version.ts +39 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,7 +6,105 @@ follows the Zerotal monorepo's unified versioning.
|
|
|
6
6
|
|
|
7
7
|
**Maturity: `stable`**
|
|
8
8
|
|
|
9
|
-
## [
|
|
9
|
+
## [1.7.0] — 2026-08-16
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **Security headers now cover static files.** Files under `public/` are handed to Bun as
|
|
14
|
+
pre-registered responses and served without entering JavaScript, so no middleware ran for
|
|
15
|
+
them — including `SecureHeadersMiddleware`, which the framework advertises as automatic.
|
|
16
|
+
Every asset went out with no `X-Content-Type-Options: nosniff`, which is precisely the
|
|
17
|
+
response class sniffing protection exists for. The header set is baked into the compiled
|
|
18
|
+
response at registration time, so Bun still serves the file natively; a header a mount
|
|
19
|
+
declares itself still wins.
|
|
20
|
+
|
|
21
|
+
- **`BaseMiddleware.with()` type-checks its options.** `Opts` has a default computed from the
|
|
22
|
+
middleware class, but a type parameter in an argument position is inferred from the
|
|
23
|
+
_argument_ and only falls back to its default when inference finds nothing — so
|
|
24
|
+
`with({ … })` inferred `Opts` from the object literal and type-checked the literal against
|
|
25
|
+
itself. Every callback parameter arrived implicitly `any`, and a misspelled option was
|
|
26
|
+
accepted in silence. `NoInfer` on the parameter makes the middleware's own option type the
|
|
27
|
+
one that governs. It caught a real defect on the first run: `StorageProvider` was passing
|
|
28
|
+
an `unknown` where a `StorageManager` was expected.
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **`DeepPartial<T>`, and `deepMerge` accepts it.** `deepMerge` does a deep merge and
|
|
33
|
+
declared `override: Partial<T>`, which only makes the top level optional — so
|
|
34
|
+
`{ drivers: { anthropic: { apiKey } } }`, the commonest thing anyone writes in a config
|
|
35
|
+
file, was a type error against any shape whose nested block has other keys.
|
|
36
|
+
`@zerotal/ai` had already hit this and defined a private copy; that copy is now deleted and
|
|
37
|
+
the type is exported from the kernel. `BaseMiddleware.with()` takes it too, since it
|
|
38
|
+
deep-merges as well.
|
|
39
|
+
|
|
40
|
+
- **`zt doctor --url` reports duplicated security headers.** A header the app sets and the
|
|
41
|
+
proxy also sets is invisible from inside the process. Conflicting values fail — browsers do
|
|
42
|
+
not agree which copy applies, so the control is enforced inconsistently — and identical
|
|
43
|
+
duplicates warn. `Permissions-Policy` and `Referrer-Policy` are deliberately not checked:
|
|
44
|
+
a comma is legitimate syntax there, and a probe that cried wolf on a correct header would
|
|
45
|
+
be switched off before it caught a real one.
|
|
46
|
+
|
|
47
|
+
- **`RequestFailed` carries the error's class name and stack.** It had the message and the
|
|
48
|
+
status, which is enough to say a request failed and not enough to say anything about how.
|
|
49
|
+
A subscriber rendering a failure — the devtools Exception tab is the first — has nothing to
|
|
50
|
+
show without them, and by the time the event is emitted the error object is the only place
|
|
51
|
+
they exist. Both are optional trailing parameters, so nothing that constructs or reads the
|
|
52
|
+
event needs to change.
|
|
53
|
+
|
|
54
|
+
- **`Application.providerReport`** — what each provider cost to boot and what it put in the
|
|
55
|
+
container, in boot order. `bootDurationMs` said the app took 240ms and nothing said which
|
|
56
|
+
provider spent it; the container listed a hundred bindings and nothing said who bound
|
|
57
|
+
them. Boot order is itself the third answer, since it decides who wins a contested
|
|
58
|
+
binding.
|
|
59
|
+
|
|
60
|
+
Provenance comes from diffing the container registry around each provider's hooks rather
|
|
61
|
+
than from the container recording a registrar — it keeps the cost at boot instead of on
|
|
62
|
+
every binding, and adds no mutable state to the container for a question only a debugging
|
|
63
|
+
tool asks. Async hooks are timed across their `await`, not up to it.
|
|
64
|
+
|
|
65
|
+
- **`FrameworkEvents.subscriptions()` and `Emitter.registrations()`** — which events have
|
|
66
|
+
subscribers, and what reacts to them. The bus is the framework's nervous system and had
|
|
67
|
+
been entirely invisible: `handlerCount()` returned one number for the whole thing.
|
|
68
|
+
|
|
69
|
+
- **`redactGraph` on the `@zerotal/core/security` subpath** — the object-graph redaction walk
|
|
70
|
+
that every recorder needs and that three packages had each written for themselves. Copy a
|
|
71
|
+
value, replace what a key name says is a secret, come back with something
|
|
72
|
+
`JSON.stringify` survives.
|
|
73
|
+
|
|
74
|
+
Shared because the hard parts are the same everywhere and are easy to get subtly wrong:
|
|
75
|
+
cycles (a model with a back-reference to its parent is ordinary, and `JSON.stringify`
|
|
76
|
+
throws on it), a depth bound (recording is on the request path), and values that read
|
|
77
|
+
better flat than walked (`Object.entries` on a `Date` or a `File` produces something worse
|
|
78
|
+
than useless).
|
|
79
|
+
|
|
80
|
+
It is not a policy. Callers bring their own markers and their own sensitivity predicate,
|
|
81
|
+
because those are not interchangeable — a debug panel's `‹redacted›` is a display choice,
|
|
82
|
+
while an adapter implementing a published wire protocol has its markers specified for it.
|
|
83
|
+
|
|
84
|
+
## [1.6.3] — 2026-08-15
|
|
85
|
+
|
|
86
|
+
### Added
|
|
87
|
+
|
|
88
|
+
- **`serve --dev` says when the framework on disk is no longer the framework running.** A
|
|
89
|
+
running dev server holds the code it imported at boot: `bun add zerotal@latest` in another
|
|
90
|
+
terminal rewrites `node_modules` and nothing else, and a save restarts only the _worker_,
|
|
91
|
+
which re-executes your app against that same in-memory framework. So an upgrade taken
|
|
92
|
+
mid-session appears to do nothing — the fix is installed, the symptom persists, and the
|
|
93
|
+
reasonable conclusion is that the fix does not work.
|
|
94
|
+
|
|
95
|
+
The supervisor now compares the version it booted with against the one installed, on each
|
|
96
|
+
restart, and says which is which:
|
|
97
|
+
|
|
98
|
+
```text
|
|
99
|
+
[zerotal:dev] ⚠ framework upgraded on disk: running v1.6.2, installed v1.6.3
|
|
100
|
+
[zerotal:dev] restart the dev server to pick it up (a save will not).
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Once per version, so a long session is not nagged, and silent where there is nothing to read
|
|
104
|
+
— a workspace checkout or a hoisted layout is not a finding.
|
|
105
|
+
|
|
106
|
+
- **The dev banner carries the version** — `Zerotal v1.6.3 › dev`. There was previously
|
|
107
|
+
nothing on screen naming the framework a running server was actually executing.
|
|
10
108
|
|
|
11
109
|
## [1.6.2] — 2026-08-15
|
|
12
110
|
|