This release introduces ctx()
, a lightweight utility with big ambitions. Access the active request context from anywhere in your code, no need to pass it through every method.
In addition, smarter @cache
and @span
decorators, enhanced afterHooks
and some much-needed love for Windows developers.
Added
- feat:
ctx()
utility, available viaimport {ctx} from '@trifrost/core'
, which returns the activeTriFrostContext
for the current request. No more manual parameter threading, you can now accessctx()
anywhere in your request lifecycle:
import {ctx} from '@trifrost/core';
export async function doSomething() {
ctx().logger.info('Hello from anywhere!');
}
Improved
- feat: Added support to run scripts on Windows machines by @dennishavermans
- feat: Internal context tracking now uses AsyncLocalStorage under the hood, enabling request-scoped data access across the entire call stack (see Added).
- feat:
afterHooks
(added through.addAfter
, which get run after the request finishes) now receive thecontext
instance, allowing hooks to inspect context state or perform additional actions using the context without being tied to request lifetime. Important to note that the AsyncLocalStorage context is not available in after hooks.
app.get('/hello', ctx => {
ctx.addAfter(context => {
context.logger.info(`Response sent with status ${context.statusCode}`);
});
return 'Hello World';
});
- feat:
@cache
and@span
decorators no longer require ctx to be passed and will tap into the active context coming from AsyncLocalStorage (using the newctx()
util, see Added)
import {cache, span} from '@trifrost/core';
class Example {
@cache('user:123') // ctx not required
async fetchUser() {
return await db.getUser(123);
}
@span('expensive-op') // ctx not required
async doWork() { /* ... */ }
}
import {cache, span, ctx} from '@trifrost/core';
import {type Context} from '~/types';
class UserService {
@cache(() => `user:${(ctx() as Context<{id:string}>).state.id}`) // ctx() here for dynamic key
@span('fetch-user')
async getUser() {
const context = ctx() as Context<{id:string}>; // full access to request context
context.logger.info(`Fetching user ${context.state.id}`);
return await db.getUser(context.state.id);
}
}
- feat:
cacheFn
andspanFn
helpers no longer require ctx to be passed and will tap into the active context coming from AsyncLocalStorage (using the newctx()
util, see Added)
import {cacheFn, spanFn} from '@trifrost/core';
const getData = cacheFn('data-key')(async () => fetchData());
const process = spanFn('processing')(async () => doProcessing());
- misc: Migrated to TypeScript 5.9 and updated types to align with lib.d.ts changes
- deps: Upgrade @cloudflare/workers-types to 4.20250813.0
- deps: Upgrade @types/node to 22.17.2
- deps: Upgrade bun-types to 1.2.20
- deps: Upgrade eslint to 9.33.0
- deps: Upgrade eslint-plugin-prettier to 5.5.4
- deps: Upgrade typescript to 5.9.2
- deps: Upgrade typescript-eslint to 8.39.1
Announcements
- First community contribution by @dennishavermans 🎉
This one’s all about making TriFrost feel lighter in the hands and sharper at the edges. The less you fight with boilerplate, the more you can focus on building things that matter.
We’ve got more lined up, so keep your eyes open and go ship something cool.
As always. Stay frosty ❄️