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

@enclosurejs/stamp

v1.1.0

Published

Compile-time build metadata for Enclosure apps

Readme

@enclosurejs/stamp — Compile-time build metadata

[!IMPORTANT] This package collects git, version, and environment information at build time and injects it as a frozen object available at runtime. One import — all build context. Zero runtime dependencies. Works with any backend.

The Problem

Every application needs build metadata — git commit, branch, version, build mode — for about screens, error reports, telemetry, and diagnostics. Collecting this by hand in every repo means duplicated scripts, inconsistent formats, and fragile shell commands scattered across configs.

@enclosurejs/stamp solves this with a single collectStamp() call that reads package.json, queries git, and snapshots the environment into a typed, frozen object. A Vite plugin (stampPlugin) wraps it as virtual:stamp for zero-config frontend access. Non-Vite bundlers call collectStamp() directly in their plugin/script layer.

Architecture

┌──────────────────────────────────────────────────────────────┐
│                       Build Time                             │
│                                                              │
│  ┌──────────────┐   ┌───────────┐                            │
│  │ package.json │   │ git repo  │                            │
│  └──────┬───────┘   └─────┬─────┘                            │
│         │                 │                                  │
│         └────────┬────────┘                                  │
│                  ▼                                           │
│          collectStamp(options)                                │
│                  │                                           │
│         ┌────────┴─────────┐                                 │
│         ▼                  ▼                                 │
│   stampPlugin()       Direct use in                          │
│   (Vite only)       Node/CI scripts                          │
│         │                                                    │
│   virtual:stamp                                              │
└─────────┼────────────────────────────────────────────────────┘
          ▼
┌──────────────────────────────────────────────────────────────┐
│                       Runtime                                │
│                                                              │
│  import stamp from 'virtual:stamp'                           │
│  stamp.version       // "1.2.3"                              │
│  stamp.branch        // "main"                               │
│  stamp.revision      // "a1b2c3d4e5f6..."                    │
│  stamp.dirty         // false                                │
│  stamp.tag           // "v1.2.3-5-gabc1234"                  │
│  stamp.remark        // "Pilot v1.2.3 [main@a1b2c3d]        │
│                         production"                          │
│  stamp.extra.target  // "electron" (user-supplied)           │
└──────────────────────────────────────────────────────────────┘

Dependency rule: @enclosurejs/stamp imports nothing from @enclosurejs/core or any platform package — it is a standalone build-time utility that works everywhere Node.js runs.

Quick Start

1. Install

pnpm add @enclosurejs/stamp

2a. Vite setup (Tauri, Electron, Web, Capacitor)

// vite.config.ts
import { stampPlugin } from '@enclosurejs/stamp/vite';

export default defineConfig({
    plugins: [stampPlugin({ app: 'pilot', product: 'Enclosure Pilot' })],
});
// src/env.d.ts (or any .d.ts included in tsconfig)
/// <reference types="@enclosurejs/stamp/virtual-stamp" />
// app code
import stamp from 'virtual:stamp';

console.log(stamp.remark);
// "Enclosure Pilot v1.0.0 [main@a1b2c3d] production"
// or with uncommitted changes:
// "Enclosure Pilot v1.0.0 [main@a1b2c3d] development (dirty)"

if (stamp.dev) {
    console.log(`Branch: ${stamp.branch}, Commit: ${stamp.shortRevision}`);
    if (stamp.dirty) console.warn('Build from dirty working tree!');
}

2b. Direct use (Node scripts, CI, non-Vite bundlers)

import { collectStamp } from '@enclosurejs/stamp';

const stamp = collectStamp({ app: 'pilot', product: 'Pilot' });
// Use stamp.version, stamp.revision, etc. in your build pipeline

For webpack or esbuild, call collectStamp() in your plugin/define config and inject the result via DefinePlugin or define.

How It Works

Data Collection

collectStamp() gathers metadata from three sources:

  1. package.jsonname, version, description (read from options.packageRootoptions.rootcwd). Fields are validated as strings; non-string values are ignored.
  2. Gitbranch, revision (full SHA), shortRevision (7 chars), commitDate (ISO 8601), dirty (uncommitted changes), tag (git describe). Collected via git log, git status --porcelain, and git describe --tags --always with a fallback rev-parse for detached HEAD.
  3. Mode — resolved as options.modeNODE_ENV"development". Date provides builtAt and year.

All git commands use execFileSync (no shell interpolation) and are wrapped in try/catch — fields become "unknown" when git is unavailable (CI containers, shallow clones, non-repo directories). No crashes, no warnings.

Vite Integration

stampPlugin() hooks into Vite's configResolved phase:

  1. Passes config.mode as options.mode and config.root as options.root (user-supplied options take priority)
  2. Calls collectStamp() — no process.env mutation
  3. Serializes the result as a JSON object
  4. Serves it via resolveId/load as the virtual module virtual:stamp

The plugin runs at enforce: 'pre' so stamp data is available to all downstream plugins.

Extra Fields

User-supplied extra fields are namespaced under stamp.extra:

const stamp = collectStamp({
    app: 'pilot',
    extra: { target: 'electron', channel: 'beta' },
});
stamp.extra.target; // "electron"
stamp.extra.channel; // "beta"
stamp.version; // always the real version — core fields are separate

Freeze Guarantee

The returned Stamp is deeply frozen via Object.freeze — both the stamp object and stamp.extra are immutable at runtime, not just at the type level.

API

Core (@enclosurejs/stamp)

| Export | Kind | Purpose | | -------------- | -------- | --------------------------------------------------------------- | | collectStamp | function | Snapshot git, package.json, and environment into a frozen Stamp | | Stamp | type | Frozen, deeply-immutable build metadata | | StampOptions | type | Configuration for collectStamp |

Vite plugin (@enclosurejs/stamp/vite)

| Export | Kind | Purpose | | ------------- | -------- | --------------------------------------------------- | | stampPlugin | function | Vite plugin exposing virtual:stamp virtual module |

StampOptions

| Field | Type | Required | Description | | ------------- | ------------------------- | -------- | ----------------------------------------------------------------------------------------- | | app | string | Yes | Short application name (e.g. "pilot") | | product | string | No | Human-readable name. Falls back to package.json name, then app | | root | string | No | Directory for git lookup. Falls back to cwd or Vite root | | packageRoot | string | No | Directory for package.json lookup. Falls back to root. Use when version lives elsewhere | | mode | string | No | Build mode. Falls back to NODE_ENV, then "development" | | extra | Record<string, unknown> | No | User fields available at stamp.extra |

Stamp

| Field | Type | Source | | --------------- | ------------------------- | ---------------------------------------------------------------------- | | app | string | Options app | | product | string | Options product → package.json nameapp | | name | string | package.json nameapp | | version | string | package.json version"0.0.0" | | description | string | package.json description"" | | mode | string | Options modeNODE_ENV"development" | | dev | boolean | mode !== "production" | | prod | boolean | mode === "production" | | branch | string | Git branch or "unknown" | | revision | string | Git SHA (40 hex chars) or "unknown" | | shortRevision | string | First 7 chars of revision or "unknown" | | commitDate | string | ISO 8601 commit date or "unknown" | | dirty | boolean | true when working tree has uncommitted changes | | tag | string | git describe --tags --always (e.g. "v1.2.3-5-gabc") or "unknown" | | builtAt | string | ISO 8601 build timestamp | | year | number | Build year (for copyright lines) | | remark | string | "Product vX.Y.Z [branch@hash] mode" + " (dirty)" if dirty | | extra | Record<string, unknown> | User-supplied fields from options |

Configuration

The only required field is app. Everything else is auto-detected:

// Minimal
collectStamp({ app: 'myapp' });

// Full control
collectStamp({
    app: 'studio',
    product: 'Creative Studio',
    root: '/path/to/project',
    mode: 'production',
    extra: { target: 'electron', locale: 'en' },
});

// Monorepo — git from root, version from workspace package
collectStamp({
    app: 'studio',
    root: '/path/to/monorepo',
    packageRoot: '/path/to/monorepo/packages/studio',
});

// Vite — minimal
stampPlugin({ app: 'myapp' });

// Vite — full control
stampPlugin({
    app: 'studio',
    product: 'Creative Studio',
    root: '/path/to/project',
    extra: { target: 'tauri', channel: 'beta' },
});

Backend Compatibility Matrix

@enclosurejs/stamp is a compile-time utility — it runs in Node.js during your build, not at runtime. It has no dependency on @enclosurejs/core, BackendAdapter, or any platform package:

| Backend | How to use | Plugin needed | | -------------------- | ----------------------------------- | ------------- | | Tauri (Vite) | stampPlugin() in vite.config.ts | Built-in | | Electron (Vite) | stampPlugin() in vite.config.ts | Built-in | | Capacitor (Vite) | stampPlugin() in vite.config.ts | Built-in | | Web (Vite) | stampPlugin() in vite.config.ts | Built-in | | Electron (webpack) | collectStamp() + DefinePlugin | Manual | | Electron (esbuild) | collectStamp() + define option | Manual | | Node.js (no bundler) | collectStamp() directly | None | | CI scripts | collectStamp() directly | None |

Types Exported

Types other packages depend on:

| Type | Used by | | -------------- | ------------------------------------------------- | | Stamp | Any code that reads build metadata | | StampOptions | Build configs, CI scripts, bundler plugin configs |

Entrypoint separation prevents Vite type pollution:

| Import path | Contains | Requires Vite types | | ----------------------- | -------------- | ------------------- | | @enclosurejs/stamp | collectStamp | No | | @enclosurejs/stamp/vite | stampPlugin | Yes (peer dep) |

Safety

Graceful Degradation

  • All git commands (log, rev-parse, status --porcelain, describe --tags) use execFileSync (no shell) wrapped in try/catch — no crashes outside a repo or in shallow clones
  • Missing or corrupted package.json → sensible defaults ("0.0.0", "", app as name)
  • Non-string fields in package.json (name: 42, version: [1,0]) are ignored — treated as missing
  • Missing NODE_ENV and no mode option → defaults to "development"

Immutability

  • The returned Stamp is Object.freeze'd — mutation throws in strict mode, silently fails otherwise
  • stamp.extra is also frozen — deeply immutable at runtime, not just at the type level

Environment Safety

  • stampPlugin does not mutate process.env — mode is passed as a parameter to collectStamp
  • extra fields are namespaced under stamp.extra — cannot collide with core fields

Dependency Safety

  • Zero runtime dependencies — only Node.js builtins (child_process, fs, path) at build time
  • Vite is an optional peer dependency — only needed by @enclosurejs/stamp/vite
  • Main entrypoint (@enclosurejs/stamp) does not import Vite — safe for pure Node.js consumers

Benchmarks

Not applicable. @enclosurejs/stamp is a build-time utility — it runs once during compilation via collectStamp() or stampPlugin(). There is no runtime hot path to benchmark. Execution time is dominated by a single execFileSync('git', ...) call (typically < 50ms) and a single readFileSync for package.json.

Bundle Size

| Output | File | Size | | ------------ | -------------- | -------- | | Runtime (JS) | collect.js | 3.08 KB | | | vite.js | 3.69 KB | | | index.js | 3.07 KB | | Types (DTS) | collect.d.ts | 2.99 KB | | | vite.d.ts | 0.63 KB | | | index.d.ts | 0.06 KB | | Total JS | | 9.84 KB | | Total | | 13.52 KB |

index.js is a barrel that re-exports from collect.js — consumers import either @enclosurejs/stamp or @enclosurejs/stamp/collect. vite.js is only loaded by Vite builds. Zero runtime dependencies — only Node.js builtins at build time.

Quality

| Metric | Value | | --------------------- | ------------------------------------------------------------------ | | Unit tests | 51 (all pass) | | Test files | 3 (collect.test.ts, collect-git.test.ts, vite.test.ts) | | Source files | 4 (index.ts, collect.ts, vite.ts, virtual-stamp.d.ts) | | External dependencies | 0 (devDependencies only: tsup, vite) | | Peer dependencies | vite >=5.0.0 (optional) | | Coverage thresholds | statements >= 90%, branches >= 85%, functions >= 95%, lines >= 90% |

Quality Layers

Layer 1: STATIC ANALYSIS (every commit)
  tsc --noEmit        strict mode, zero errors
  eslint              ESLint 9 flat config, zero warnings
  prettier --check    formatting

Layer 2: UNIT TESTS (every commit)
  51 tests            collectStamp + stampPlugin, edge cases, safety
  v8 coverage         statements >= 90%, branches >= 85%, functions >= 95%, lines >= 90%

Layer 3: BENCHMARKS
  N/A                 build-time utility, no runtime path to benchmark

Layer 4: PACKAGE HEALTH
  0 external deps     pure TypeScript + Node.js builtins
  tsup build          ESM + DTS output, separate entrypoints

File Structure

packages/stamp/
├── src/
│   ├── index.ts             Barrel: collectStamp, Stamp, StampOptions
│   ├── collect.ts           Core collector (git + package.json + env)
│   ├── vite.ts              Vite plugin (virtual:stamp)
│   ├── virtual-stamp.d.ts   Type declaration for virtual module
│   └── __tests__/
│       ├── collect.test.ts      36 tests — fields, fallbacks, modes, extra, freeze, validation, dirty, tag, packageRoot
│       ├── collect-git.test.ts  2 tests — detached HEAD, sparse git log (mocked execFileSync)
│       └── vite.test.ts         13 tests — plugin identity, resolve, load, mode, root, env safety
├── package.json
├── tsconfig.json
├── tsup.config.ts
└── README.md

License

MIT