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

@particle-academy/prism-workspace

v0.1.0

Published

A guarded agent workspace for TypeScript.

Readme

Prism Workspace for TypeScript

TypeScript implementation of Prism's guarded agent workspace. It provides a Promise-based local workspace, stable owner addressing, optional authorization, streamed directory listings, stable failure codes, lexical path guarding, and realpath-based symlink containment.

Zero runtime dependencies. Node 20+.

This package is private while coordinated parity work is in progress.

Verify it on YOUR disk

The package ships the same byte-preserving 134-case adversarial corpus as PHP — and, since it ships, you can fire it at your own configuration:

import { Workspace } from '@particle-academy/prism-workspace';
import { CorpusRunner } from '@particle-academy/prism-workspace/corpus';

const report = await new CorpusRunner().against(new Workspace(base, owner));

if (!report.passed()) throw new Error(report.summary());

Our CI proves the boundary holds on our disk. Yours might use a different root, a network share, or a case-insensitive volume, and a security property is only true of the configuration it was measured on.

It is safe to run against a live workspace: every attempt is expected to be refused, so a passing run writes nothing at all.

Two checks, not one. Every attempt must be refused with the code the corpus names, and the directories around the workspace are then swept for a per-run marker. The second catches what the first cannot — a guard that refuses everything correctly, paired with a root assembled wrongly, passes every unit test in this package and still writes into the wrong place.

A truncated sweep is not a pass. The sweep stops at a file ceiling so it cannot become the slowest thing in your suite; when it does, report.passed() is false and sweepComplete says why. "No strays found" and "no strays exist" are different claims, and only one of them is a security result. The PHP reference does not yet make this distinction.

Paths can be bytes

Workspace methods take string | Uint8Array. Bytes are not a convenience: several corpus cases are invalid UTF-8 on purpose, and such a path cannot be expressed as a JavaScript string at all — it becomes U+FFFD on the way in. A string-only API could never carry the attack the guard exists to refuse.

Reading and writing

read() decodes UTF-8; readBytes() does not. readStream() and writeStream() move content too large to hold in memory. Every one of them goes through the same guard before a handle is opened — a streaming accessor that skipped it would be a hole straight through the boundary, and it would look like an optimisation.