JSPM

@borderless/context

2.1.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 1
  • Score
    100M100P100Q15496F
  • License MIT

Tiny, type-safe, JavaScript-native `context` implementation

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

NPM version NPM downloads Build status Test coverage Bundle size

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