Package Exports
- @borderless/context
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (@borderless/context) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
Context
Tiny, type-safe, JavaScript-native
context
implementation.
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