npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@orkestrel/test

v0.0.33

Published

The test helpers the Orkestrel fleet repeats — a call recorder, a real delay, JSON and async collectors, an owned scratch directory with a source-file walker, and a browser journey layer that drives real interfaces by role and accessible name. Part of the

Readme

@orkestrel/test

The test helpers the @orkestrel fleet kept rewriting, published once: families of what a test records, what it waits for, and what it owns, with a pair outside all of them and a browser journey layer beside them.

Add it as a devDependency and import the core from @orkestrel/test. A helper ships here only when enough packages had already written their own; the guide's Limits section states that rule, what it excluded, and the one door the journey layer came through instead. Nothing here runs in production code. Part of the @orkestrel line.

This package runtime-depends on @orkestrel/contract for the outcome type retryUntil reads internally and for the guards every environment narrows with. That type is not re-exported. No exported signature names an @orkestrel/* type. Both rules exist for one reason: a test helper hands its types straight into the consumer's assertions, and a second copy of a package inside its own repository makes the compiler read one type as two.

Install

npm install -D @orkestrel/test

Requirements

  • Node.js >= 22.18.0
  • ESM and CommonJS

Usage

@orkestrel/test is the host-independent core. @orkestrel/test/server is the Node face. @orkestrel/test/browser is the journey layer, which drives a real browser through the installed Vitest provider. Core touches neither node:* nor the DOM, so a browser test project imports it unchanged.

The browser face also supplies sendTouch, tapTouch, and releaseTouch, external file drags through sendFileDrag, a pointer axis on stageMedia, and the stylesheet instruments adoptSheet, SheetAdoption, and readLayerNames.

import {
	captureError,
	createRecorder,
	createTeardown,
	requireValue,
	waitForDelay,
} from '@orkestrel/test'
import { createScratch } from '@orkestrel/test/server'

// A cleanup list the test adds to as it goes, and a temporary directory seeded with the input.
const teardown = createTeardown()
const scratch = createScratch({ prefix: 'loader-', files: { 'input.txt': 'hello' } })
teardown.add(() => scratch.destroy()) // idempotent, and it removes only the directory it allocated

// A real callback, not a spy: hand `handler` to the code under test.
const recorder = createRecorder<[path: string]>()
loader.on('read', recorder.handler)

loader.watch(scratch.path)
await waitForDelay(10) // let a real host timer elapse

recorder.count // how many reads arrived
recorder.calls // the arguments of each, oldest first
recorder.clear() // truncates in place, so a `calls` reference captured earlier empties too

// A throw becomes a value; absence becomes a throw. The test still does the asserting.
captureError(() => loader.read('missing.txt')) // the thrown value, or undefined
requireValue(scratch.read('input.txt')) // 'hello' — narrows `string | undefined` without `!`

scratch.remove('input.txt') // one contained entry, subtree and all; a missing target is a no-op
await teardown.destroy() // newest registration first, and every handler runs even after one throws

The list registers no hook of its own. The consumer writes afterEach(() => teardown.destroy()) once, in its own setup, because registering it here would take a runtime dependency on the test runner. A handler that throws does not stop the run: one failure is rethrown by identity, so the test can assert on the value it threw, and several arrive as an AggregateError in run order.

Core also carries the wait family (waitForCondition, retryUntil, waitForEvent, waitForAbort, and the leaves they share), the recorder map and the abort-signal instrument, the unchecked boundary, the header flattener, and the JSON Lines decoder — the guide's Surface section carries every export. Among them are collect (drains an async iterable), collectStream (drains a ReadableStream), roundTripJSON (copies any value JSONSafe accepts, including an interface-typed one, and throws rather than turning a non-finite number into null), resolveRoot (the directory above the calling module, from import.meta), and createHostileValues (a frozen array of fresh values that each make a naive read throw or violate a naive structural assumption). Beside them sits the statechart contract the fleet repeats: StateTransition and StateScenario for one row of a transition table, executeScenario and executeScenarios to walk it, and STATECHART_ATTRIBUTES and STATECHART_STATUSES for the harness a browser workspace renders that table in.

The server face adds readInventory, which reads a checkout into a map of root-relative path to file text that a parity suite can assert against, and createLoopback, which binds a server the test built to an ephemeral loopback port. Behind them sit the pure leaves the server face also exports on its own: readInventory refuses a path outside the root with resolveContained and prunes with isExcluded, while createScratch refuses one with requireContained, the throwing form, and reads and compares its allocation with readIdentity and matchesIdentity before destroy() removes anything. createLink and removeTree do the writing under it, and both decide on the errno readErrorCode reads off an unknown throw.

import { resolveRoot } from '@orkestrel/test'
import { readInventory } from '@orkestrel/test/server'

// A suite in tests/ is one directory below the workspace root, which is what `resolveRoot` returns.
const root = resolveRoot(import.meta)

// Nothing is walked by default, so the targets are a required argument rather than an option.
const sources = readInventory(root, ['src/core', 'src/server'], { extensions: ['.ts'] })

Object.keys(sources)
// ['src/core/Production.ts', 'src/core/Teardown.ts', 'src/core/constants.ts', 'src/core/factories.ts', 'src/core/helpers.ts', 'src/core/index.ts',
//  'src/core/types.ts', 'src/core/validators.ts', 'src/server/Loopback.ts', 'src/server/constants.ts',
//  'src/server/factories.ts', 'src/server/helpers.ts', 'src/server/index.ts',
//  'src/server/types.ts']

// The keys are paths; the values are the file contents.
sources['src/core/index.ts']
// "export * from './types.js'\nexport * from './constants.js'\nexport * from './validators.js'\n
//  export * from './helpers.js'\nexport * from './factories.js'\n"

// A target is a file or a directory. A named file is read whatever the extension filter says.
Object.keys(readInventory(root, ['package.json', 'src/core'], { extensions: ['.ts'] }))
// ['package.json', 'src/core/Production.ts', 'src/core/Teardown.ts', 'src/core/constants.ts', 'src/core/factories.ts', 'src/core/helpers.ts',
//  'src/core/index.ts', 'src/core/types.ts', 'src/core/validators.ts']

// An `exclude` entry matches whole key segments. A file key drops that file.
Object.keys(
	readInventory(root, ['src/core'], { extensions: ['.ts'], exclude: ['src/core/index.ts'] }),
)
// ['src/core/Production.ts', 'src/core/Teardown.ts', 'src/core/constants.ts', 'src/core/factories.ts', 'src/core/helpers.ts', 'src/core/types.ts',
//  'src/core/validators.ts']

// A directory key prunes its whole subtree.
Object.keys(
	readInventory(root, ['src'], { extensions: ['.ts'], exclude: ['src/server', 'src/browser'] }),
)
// ['src/core/Production.ts', 'src/core/Teardown.ts', 'src/core/constants.ts', 'src/core/factories.ts', 'src/core/helpers.ts', 'src/core/index.ts',
//  'src/core/types.ts', 'src/core/validators.ts']

Keys are root-relative and /-separated whatever the host separator is. The suite gates that proof on a runtime reading of node:path rather than on a platform name, so it runs only where the host separator differs from /. Keys are inserted in sorted order, and a plain object reads that order back for every key that is not integer-like.

createScratch and readInventory promise different things, and each boundary is worth stating up front. createScratch allocates its own directory at POSIX mode 0700 — under the host temporary directory, or under a parent you name — and refuses a path that lexically resolves outside it. The suite gates that assertion on supportsMode, the shipped probe that reads whether permission bits round-trip on this host, so it runs only where they do and claims nothing about a host that emulates them. The mode keeps another uid out, and neither a sibling test worker nor the code under test is another uid. It does not walk segments for symbolic links. A link inside its own allocation was created by the test process, by the code the test drives, or by this package's own link — handing that code scratch.path is the ordinary use of this helper — and a contained path reaches outside the allocation through one. The guide's traversal section states what each member does with a link it meets. Naming a parent inside a package tree costs one more thing: while the allocation exists, everything that walks that tree sees it. destroy() is unaffected by where the allocation sits, because it matches the allocation's identity rather than its path. One field of that identity is the host's to supply: where a host reports no real creation time, libuv reports ctime in its place, the first write moves it, and destroy() then removes nothing and returns as if it had. readInventory walks a checkout you supply, usually one the test did not create, so it throws on a symlinked root or named target, throws on a named target whose real path leaves the root through a link in the middle, and skips a symlink met while walking. Neither is a sandbox against untrusted filesystem content: those refusals stop accidental access outside the root, not a process that can create hard links where the test process already writes.

The browser face is the journey layer: an accessible-name resolver with exact failure voices, region and disclosure targeting, input and traversal verbs, perception readers, a WCAG contrast instrument that composites translucent layers, and the capture portfolio. Every acting verb resolves its own target from an accessible name and, where the platform supplies one, a role — none takes an element, a component instance, or a selector — and the whole environment imports vitest/browser, DOM globals, this package's own core, and the @orkestrel/contract guards it narrows with, with vitest declared as a peer dependency. Beside the journey verbs are the readers a markup conformance check needs: readClasses for the classes the markup carries, differenced against readCascade for the classes the cascade declares, and extractStyles for the inline style attributes and <style> elements that go round the cascade entirely. The guide's Browser section carries every export and every voice.

createLoopback binds a server the test built. The caller constructs it and keeps every route, header, and status on it; this package supplies the bind and the release and nothing else.

import { createServer } from 'node:http'
import { createLoopback } from '@orkestrel/test/server'

const server = createServer((_request, response) => {
	response.end('ok')
})

const loopback = await createLoopback(server)

loopback.port // the number the host assigned; this package neither picks a port nor reserves one
loopback.url // `http://127.0.0.1:${loopback.port}` — IPv4 loopback, no trailing slash

const response = await fetch(loopback.url) // a real request, answered by the caller's own server
await response.text() // 'ok'

await loopback.destroy() // drops every live connection, then closes; idempotent

It listens on port 0 at 127.0.0.1, so the host assigns the number and the address is IPv4 loopback rather than ::1, which a host resolving localhost can hand back instead. The scheme in url is spelled http unconditionally, so a TLS server's origin is port plus a scheme the caller writes. destroy() drops live connections on a server that carries closeAllConnections — both node:http's and node:https's do — so a keep-alive client cannot hold the port past the test that opened it. A plain node:net server has no such method, so destroy() waits for its open sockets to end.

Guide

For the full surface — every export, the behavioral contract each one holds to, and the measured rule deciding what ships and what stays in the package that owns it — see guides/test.md.

Package

Published as typed entry points per the exports field in package.json: @orkestrel/test for the host-independent core, @orkestrel/test/browser for the journey layer, and @orkestrel/test/server for the Node helpers. Core and server ship ESM and CommonJS; the browser face ships ESM only, because vitest/browser is an ES-only module.

License

MIT © Orkestrel — see LICENSE.