Repository navigation
Releases: petarzarkov/dunx
Release list
v3.10.5
Listener hostname, per-route body and query parsers, declared route status
A 204 or 304 response is now always sent without a body, even when the
handler returns a value.
Features
- examples: use listener hostname and a route bodyParser in examples/full (
61b6f41) - http: support per-route body and query parsers (
1e887bc) - http: configure hostname for HTTP and gateway listeners (
79ef3bb) - http: expose declared status in route context (
8de97d4)
Fixes
- http: await an async queryParser and skip the clone on buffered bodies (
2149827) - http: keep cookies on a replayed custom-body request (
2b571dc) - http: preserve declared bodyless response statuses (
37df30e)
Documentation
- mcp: refresh controller guide corpus (
b86fe09)
v3.10.4
Bun 1.4.3, log filters, leak fixes, hot-path URL parsing
Features
- logger: log filters through
@arkv/logger0.14.0 (8632f21)
Fixes
- http: sync the notes template and regenerate the MCP corpus (
2b8e180) - http: default Input's type argument to RouteSchemas (
76559a5) - schedule: keep a fired one-shot listed, only release its handler (
a468c18) - bound three unbounded-growth leaks (
6c0f3b1) - redis: drop the subscriber leak workarounds 1.4.3 made unnecessary (
cfc6efb) - example: an email filter that skips version tags and reaches josé (
f3ec1d1)
Performance
- avoid per-request URL parsing, memoize the dashboard snapshot (
13ed886)
Refactors
- examples: count with private fields again (
55f9ff3)
Documentation
- where to bound a slow route, and the client under bullmq (
4a87be8) - name bun check as the type gate and the examples/minimal exception (
ffc1d76)
Other changes
v3.10.3
OpenAPI tag groups, brotli compression, split cache TTLs
OpenApiModule takes tagGroups, emitted as x-tagGroups under 3.1 or as a
parent on each tag under specVersion 3.2.0. Compression offers brotli now
that Bun 1.4.3 honours its level. TieredCacheStore takes separate local and
remote TTLs, and core and http drop allocations on their hot paths.
Breaking for 3.10.2 callers:
@Controller's route filter takes includes and excludes. The singular
include and exclude are gone.@dunx/infraexports TieredCacheOptions in place of TieredCacheInit, and
promoteTtl is now ttl. An object still passing promoteTtl compiles only
when untyped, and its value is ignored for the 30s default.
Features
- openapi: hierarchical tag groups, as x-tagGroups or 3.2 parent (
9656a03) - http: brotli in Compression, now that Bun 1.4.3 honours its level (
5f5ae7f)
Fixes
- openapi: validate tag groups and warn on tags 3.1 viewers hide (
ab773ff) - mcp: regenerate the corpus from LF sources (
a2595be) - http: sync corpus and verify filter priority (
deb0a18) - http: support plural controller filters (
0b7abfd) - mcp: regenerate corpus with LF sources (
042f46a) - cache: refresh generated corpus and docs (
8635dc9) - cache: separate local and remote TTLs (
9f98723)
Performance
- http: cut per-request allocations on the hot path (
46a63d7) - core: index token ownership, cut a pass from export sets, skip absent-SDK spans (
fbc2cb0) - http: skip premature idempotency sweeps (
a32b799)
Documentation
- notes: re-measure the queue shutdown leak on Bun 1.4.3 (
c9281d9)
Other changes
v3.10.2
Providers resolve their dependencies in the module that declares them
A globally exported provider with private dependencies could fail to boot
depending only on import order, because it was built in the scope of
whichever module asked for it first. The injector now builds each declared
provider in its owning module's scope, so its private dependencies stay
reachable and a consumer's binding for the same token is not picked up.
An app-level override of an exported provider keeps that owner too: the
importing modules now share the owner's substitute binding, so the override
resolves its dependencies in the declaring module and stays one instance.
Fixes
- core: keep the declaring scope for an overridden exported provider (
b17bcb6) - core: resolve providers in their declaring module scope (
87077ef)
Documentation
- mcp: refresh module scope guidance (
2205470) - address review on the plain-language pass (
dcecc4c) - plain-language pass over the guides and the remaining READMEs (
d9c0314) - plain-language pass on three package READMEs and the Nest migration guide (
50b15dd)
Other changes
- review: skip the push review on fork pull requests (
bb316f1)
v3.10.1
Connection passwords kept out of url boot errors, shared helpers, and faster CORS and security headers
The Redis and websocket relay url checks no longer write the url as given
into their boot errors, so a wrong scheme or an unparseable url cannot log
a password. The check and redactUrl live once in @dunx/core as assertUrl,
and AMQP, Redis and both relays use it.
@dunx/core also exports within(), the one timer race that the queue, AMQP,
database, dashboard and health code had each written for themselves. The
dashboard's probe bound is the health registry's, and a probe that times
out there now reads "no answer in N ms".
In @dunx/testing, TestClient, Http2Client and TestServer are classes, and
testClient(), http2Client() and createTestServer() return them. close
stays bound, so afterAll(server.close) works; request and json now need
their receiver. A hand-built object typed as Http2Client no longer
satisfies it. The CSRF check and ConsoleLogger's stdout batch are classes
too.
With securityHeaders on, the responses dunx builds carry the headers from
construction instead of a per-response walk, and CORS routes keep the
direct path. As a share of raw Bun.serve on a JSON route: securityHeaders
84.9% to 91.8%, cors 82.4% to 90.2%, both 73.7% to 83.5%.
Features
- example: the demo page as a guided walkthrough, with the 3.10 features (
d0c7f76)
Fixes
- testing: keep TestServer.close bound, so afterAll(server.close) works (
65983df) - mcp: keep structuredContent for a tool that answers with a class instance (
d909bc9) - core: keep connection passwords out of url boot errors (
9bd922a) - example: the walkthrough on a phone (
aefa037)
Performance
- http: prebuilt security headers, and CORS on the direct path (
ac31a56)
Refactors
v3.10.0
Tracing, security headers, CSRF, idempotency, versioning, ETags and cookies
OpenTelemetry spans come through an always-bound Tracer that is a no-op until
an app imports OtelModule from @dunx/core/otel: the HTTP server and client,
database queries, Redis commands, bullmq jobs and AMQP messages, with log
lines joined to the recording span and trace context carried into forked
workers.
The HTTP layer gains opt-in securityHeaders, token-free CSRF protection from
Fetch Metadata, Idempotency-Key support over a memory or Redis store, API
versioning by URI, header or media type with Deprecation and Sunset headers,
ETags with conditional GET, and signed cookies with key rotation. Each is
wired at boot, so an app that leaves it off pays nothing.
Upgrading covers what changes without opting in: the explorer and dashboard
pages send their own CSP, an unmatched-path flood logs one line a second, a
304 carries the Vary its 200 had, and req.cookies exists on an unmatched path.
Features
- http: signed cookies, and req.cookies on an unmatched path (
dfa2507) - http: ETags and conditional GET, opt-in, on serialized values (
99acb98) - http: API versioning by URI, header or media type, with deprecation (
b6c698d) - http: throttle request logging's warn for unmatched paths (
13f3727) - http: CSRF protection by Fetch Metadata, opt-in, applied at boot (
d7db586) - http: Idempotency-Key support, opt-in per route (
65e13aa) - http: security response headers, opt-in, applied at boot (
02ba74f) - core: OpenTelemetry spans behind an always-bound Tracer (
21f6a74)
Fixes
- http: address review on signed cookies (
74bfc95) - http: address review on ETags (
d833b9c) - http: address review on API versioning (
a19b6c9) - http: validate csrf.trustedOrigins at construction (
e2eb86e) - http: address review on CSRF protection (
593356e) - http: address review on idempotency keys (
2b1a4b7) - http: address review on security headers (
293c7a3) - infra: address review on the OpenTelemetry spans (
f0ec454) - infra/db: a failed Bun.SQL query no longer surfaces as unhandled (
627b254)
Documentation
- bun-apis: a Bun.SQL query's then runs twice per await (
17343c0) - address review comments on the docs review pass (
18c6107) - correct samples and stale claims found in a full docs review (
b0ffdf6)
Other changes
v3.9.6
The Scalar explorer wears the dunx mark, not a stale Swagger icon
The Scalar page defaulted its favicon to data:,, which is no image, so a
browser that once showed Swagger UI at the same URL kept Swagger's cached
icon. It now defaults to the dunx mark as an inline SVG, and favicon still
takes a URL of your own or false.
The dashboard's bull-board page reads the same icon without loading the
400 KB page bundle to find it.
Fixes
- http: generate the published favicon from internal/ui, typed string (
77bbf7f) - openapi: give the Scalar page the dunx mark instead of data:, (
c1df502) - release: leave code spans, fences, link destinations and URLs alone (
8913e1b) - release: keep decorators and scopes in the notes from mentioning accounts (
4ba1e90)
Refactors
- scripts: draw the OG card mark from internal/ui's geometry (
68906a5)
Other changes
- examples: read symbols no other suite warms in the cache test (
7d5e57c)
v3.9.5
Pick inherited controller handlers with include and exclude
@Controller(prefix, { include, exclude }) chooses which handlers a subclass
of a generic base controller serves, by method name. The filter is read off
the class, so OpenAPI, the dashboard and mcp, which never construct a
controller, agree with the server. A misspelt name is a compile error, and an
unknown key or a method that is not a route is a boot error.
@dunx/transform records an empty dependency list for a subclass whose own
constructor takes no parameters. Before, it inherited its base's record and
was handed arguments it never declared.
Features
- http: pick inherited handlers with
@Controllerinclude and exclude (7b06a39)
Fixes
- reject unknown
@Controllerfilter keys, and match the constructor by kind (07d63ee)
v3.9.4
Named data sources, and one opened per tenant at runtime
DbModule.forRoot(options, { name }) registers a second data source under
dbOptions(name), dbConnection(name), dbHandle(name) and dbMetrics(name),
the shape redisConnection(name) and httpClient(name) already use. The handle
token is generic, so a repository's annotation keeps drizzle's schema inference.
DbModule.forDataSources({ create }, As) binds a DataSources pool for keys
only known at runtime, such as a database per tenant. It opens on first use,
bounds how many are live with LRU eviction, closes idle ones on a sweep, retries
a failed open rather than caching it, and closes everything on shutdown.
use(key, work) leases a data source so eviction cannot close it mid-query, and
resolution takes the key as an argument, so two tenants in flight cannot read
each other's rows.
namedToken in @dunx/core replaces the per-name token memoisation that redis,
the http client and the db module had each written.
Features
- infra/db: named data sources, and a pool keyed at runtime (
4cc9dc2)
Fixes
- infra/db: make pool admission atomic, and the review findings (
a9933ca) - docs: the indexing defects Search Console reported (
0d1ab76)
Other changes
- queue: wait on the job, not on the handler's own recorder (
696c902)
v3.9.3
A published package build, two copies of core caught at boot, and stricter MCP arguments
@dunx/transform/build publishes the build a dunx package needs, so an outside
author records constructor dependencies the way this repo does rather than
shipping classes nobody can inject. The guide for that is at
https://dunx.win/guide/publishing-a-package.
AppFactory.create refuses to boot on a second copy of @dunx/core, which
until now injected a contract with none of its methods and failed later in
whichever package asked for it. The missing-transform message names the three
things that actually cause it instead of blaming the preload.
@dunx/mcp rejects an argument the tool never declared, reads an explicit null
as the absent filter it means, and answers with structuredContent.
Features
- transform: publish the package build, and a guide for publishing one (
9175c38)
Fixes
- transform,docs: resolve an exports entry by condition, not by key order (
13b22e0) - mcp: one error message shape, and absent means absent for undefined too (
1ab20e8) - core,transform: the three review findings (
b98731b) - transform: read every shape an exports field can take (
90736ed) - mcp: hand run a record with the null key gone, and size the sweep honestly (
37b9ab5) - mcp: an integer argument, and arguments that are not an object (
c54addf) - mcp: read an explicit null as the absent filter it means (
6087611) - mcp: regenerate the bundled corpus the guide edit invalidated (
7c15c02) - core,transform: name the installed package, and fail on two copies of core (
570e39d) - mcp: reject an argument the tool never declared, and answer with structuredContent (
5460a8d)