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

glove-env-zip

v1.0.0

Published

Archive stdlib adapter for glove-working-environment. Reads and writes zip, tar and tar.gz inside the agent's virtual filesystem as env:archives — list, describe, extract selectively, package a directory back up. Dependency-free: node:zlib and nothing els

Readme

glove-env-zip

Archive stdlib adapter for glove-working-environment. Bridges zip, tar and tar.gz into the agent's virtual filesystem as env:archives — list and describe without extracting, extract selectively, and package a directory back up as one file.

pnpm add glove-env-zip

The package is glove-env-zip; the module it registers is env:archives. The mismatch is deliberate. env:archives is the name agents write in their scripts, and it is recorded in snapshots and in the adapter-version file, so renaming it would break restore for anyone holding one — and it is the more accurate name besides, since this reads tar and tar.gz as well as zip. The npm name is only how you install it.

Dependency-free: node:zlib and the container formats themselves.

import { createWorkingEnvironment } from "glove-working-environment";
import { archives } from "glove-env-zip";

const env = await createWorkingEnvironment({ stdlib: [archives()] });

Why

Archives are how batches of files actually arrive — an export from another system, a bundle of scans, a customer's data dump. An agent handed /inbox/records.zip was simply stuck: nothing could open it, and no verb would. They are also the natural way to hand a multi-file deliverable back, as one file rather than an array the host has to write out itself.

What the model gets

| Function | Does | |---|---| | describe(path) | Entry count, file count, archive size, extracted size, a sample — without extracting | | list(path) | Every entry, still without extracting | | extract(path, dir, { include? }) | Writes matching entries into dir; returns the paths written | | create(dir, output, { glob?, format? }) | Packages a directory into one archive |

import { describe, extract } from 'env:archives';
import { readFile } from 'env:fs';
import { csv } from 'env:std';

/** Totals every CSV in a mounted archive. */
export default async function main() {
  const summary = await describe('/inbox/records.zip');
  if (summary.uncompressedBytes > 50_000_000) return { skipped: 'too large', summary };

  const written = await extract('/inbox/records.zip', '/inbox/records', { include: '**/*.csv' });
  let total = 0;
  for (const path of written) {
    for (const row of csv.parse(await readFile(path))) total += Number(row.amount ?? 0);
  }
  return { files: written.length, total };
}

describe() before extract() is the habit worth having: it reports the entry count and the extracted size without spending either.

Extraction is the part that has to be right

A zip reader that round-trips its own output but writes outside the destination when handed a hostile archive has failed at the only job that is hard. The tests that matter here are the refusals, not the round trips.

  • Escaping names. A name that climbs out of the destination, an absolute path, backslash separators — all refused, by name. The check is on the resolved path rather than the spelling, because climbing segments normalise away before any string comparison would catch them. A name that climbs but resolves back inside the destination is allowed: refusing it would reject archives real tools produce.
  • Runaway expansion. The declared uncompressed size is supplied by the file itself, so it is checked and the decompression is capped at the environment's maxVfsBytes. An archive that declares ten bytes and expands to five megabytes fails at the cap, not at the claim. .tar.gz gets the same treatment before the tar is even parsed — it has no declared size to check at all.
  • Entry counts are bounded, so an archive of a million empty entries cannot spend the whole budget on overhead.
  • Extracted bytes count against maxVfsBytes like any other write, because they go through the same guarded handle.
  • Nested archives are not extracted recursively. An extracted .zip is just a file; extracting it is a second, separately budgeted call.

What cannot be read honestly is refused rather than half-read: encrypted entries, ZIP64, unsupported compression methods, and tar entries that are symlinks, devices or hard links. A silently-wrong extraction is worse than a failed one.

Formats

| Format | Read | Write | Notes | |---|---|---|---| | .zip | ✅ | ✅ | store + deflate; ZIP64 and encryption refused | | .tar | ✅ | ✅ | regular files and directories; GNU/PAX long names | | .tar.gz / .tgz | ✅ | ✅ | the above, through gzip |

Format is detected from the bytes, not the extension — a zip named .tar is still read as a zip. Output format follows the extension of output unless format overrides it.

Archives are also claimed by the describe verb, so describe('/inbox/records.zip') from the tool surface routes here without writing a script.