Context
Tiny, type-safe, JavaScript-native `context` implementation
Install / Use
/learn @borderless/ContextREADME
Context
Tiny, type-safe, JavaScript-native
contextimplementation.
Why? Working on a project across browsers, workers and node.js requires different implementations on the same thing, e.g. fetch vs require('http'). Go's context package provides a nice abstraction to bring all the interfaces together. By implementing a JavaScript first variation, we can achieve the same benefits.
Installation
npm install @borderless/context --save
Usage
Context values are unidirectional.
import { background, withValue } from "@borderless/context";
// Extend the default `background` context with a value.
const ctx = withValue(background, "test", "test");
ctx.value("test"); //=> "test"
background.value("test"); // Invalid.
Abort
Use withAbort to support cancellation of execution in your application.
import { withAbort } from "@borderless/context";
const [ctx, abort] = withAbort(parentCtx);
onUserCancelsTask(() => abort(new Error("User canceled task")));
Timeout
Use withTimeout when you want to abort after a specific duration:
import { withTimeout } from "@borderless/context";
const [ctx, abort] = withTimeout(parentCtx, 5000); // You can still `abort` manually.
Using Abort
The useAbort method will return a Promise which rejects when aborted.
import { useAbort } from "@borderless/context";
// Race between the abort signal and making an ajax request.
Promise.race([useAbort(ctx), ajax("http://example.com")]);
Example
Abort Controller
Use context with other abort signals, such as fetch.
import { useAbort, Context } from "@borderless/context";
function request(ctx: Context<{}>, url: string) {
const controller = new AbortController();
withAbort(ctx).catch(e => controller.abort());
return fetch(url, { signal: controller.signal });
}
Application Tracing
Distributed application tracing is a natural example for context:
import { Context, withValue } from "@borderless/context";
// Use a unique symbol for tracing.
const spanKey = Symbol("span");
// Start a new span, and automatically use "parent" span.
export function startSpan<T extends { [spanKey]?: Span }>(
ctx: Context<T>,
name: string
): [Span, Context<T & { [spanKey]: Span }>] {
const span = tracer.startSpan(name, {
childOf: ctx.value(spanKey)
});
return [span, withValue(ctx, spanKey, span)];
}
// server.js
export async function app(req, next) {
const [span, ctx] = startSpan(req.ctx, "app");
req.ctx = ctx;
try {
return await next();
} finally {
span.finish();
}
}
// middleware.js
export async function middleware(req, next) {
const [span, ctx] = startSpan(req.ctx, "middleware");
req.ctx = ctx;
try {
return await next();
} finally {
span.finish();
}
}
Libraries
JavaScript and TypeScript libraries can accept a typed context argument.
import { Context, withValue } from "@borderless/context";
export function withSentry<T>(ctx: Context<T>) {
return withValue(ctx, sentryKey, someSentryImplementation);
}
export function captureException(
ctx: Context<{ [sentryKey]: SomeSentryImplementation }>,
error: Error
) {
return ctx.value(sentryKey).captureException(error);
}
License
MIT
Related Skills
node-connect
346.8kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
107.6kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
Writing Hookify Rules
107.6kThis skill should be used when the user asks to "create a hookify rule", "write a hook rule", "configure hookify", "add a hookify rule", or needs guidance on hookify rule syntax and patterns.
review-duplication
100.1kUse this skill during code reviews to proactively investigate the codebase for duplicated functionality, reinvented wheels, or failure to reuse existing project best practices and shared utilities.
