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

@zenv-sh/vite-plugin

v0.0.1

Published

Vite plugin for zEnv — build-time secret injection with zero-knowledge encryption

Readme

@zenv-sh/vite-plugin

Vite plugin for zEnv. Fetches encrypted secrets at build time, decrypts them in Node, and injects the values into import.meta.env — without ever exposing your credentials to the browser.

How it works

vite build / vite dev
  │
  └─ @zenv-sh/vite-plugin (Node, build host)
       │
       ├─ ZENV_TOKEN + ZENV_PROJECT_KEY → @zenv-sh/sdk → decrypt secrets
       │                                                   (stays in Node)
       └─ plaintext values → Vite define API → import.meta.env.ZENV_*
                                                   (lands in bundle)

ZENV_TOKEN and ZENV_PROJECT_KEY are never in the bundle. Only the decrypted values you declare in schema are injected.

Install

pnpm add -D @zenv-sh/vite-plugin

Quick start

// vite.config.ts
import { defineConfig } from "vite"
import { z } from "zod"
import { zenvPlugin } from "@zenv-sh/vite-plugin"

export default defineConfig({
  plugins: [
    zenvPlugin({
      token:      process.env.ZENV_TOKEN!,
      projectKey: process.env.ZENV_PROJECT_KEY!,
      projectId:  process.env.ZENV_PROJECT_ID!,
      environment: "production",   // or "development" | "staging"
      schema: z.object({
        STRIPE_PUBLISHABLE_KEY: z.string().startsWith("pk_"),
        API_BASE_URL:           z.string().url(),
      }),
    }),
  ],
})

In your app:

// Fully typed. Injected at build time. No network call from the browser.
const stripe = import.meta.env.ZENV_STRIPE_PUBLISHABLE_KEY
const api    = import.meta.env.ZENV_API_BASE_URL

Options

| Option | Type | Default | Description | |---------------|---------------------------------|--------------------------|-------------| | token | string | required | Service token (ZENV_TOKEN) | | projectKey | string | required | Project Vault Key (ZENV_PROJECT_KEY) — never sent to server | | projectId | string | required | Project ID | | schema | Standard Schema / plain object | required | Declares which secrets are safe to inject into the browser bundle | | environment | "development" \| "staging" \| "production" | "development" | Target environment | | prefix | string | "ZENV_" | Prefix for import.meta.env keys. "" = no prefix | | baseUrl | string | "https://api.zenv.sh" | Override for self-hosted deployments | | watch | number \| false | 30 | Dev polling interval in seconds. 0 or false = disabled |

Schema

The schema option is required. This is a deliberate security decision — without it, the plugin would have no way to distinguish client-safe secrets from server-only credentials like DATABASE_URL.

// Zod (recommended)
import { z } from "zod"

schema: z.object({
  STRIPE_PUBLISHABLE_KEY: z.string().startsWith("pk_"),
  API_BASE_URL:           z.string().url(),
})

// Valibot
import * as v from "valibot"

schema: v.object({
  STRIPE_PUBLISHABLE_KEY: v.string(),
  API_BASE_URL:           v.string(),
})

// ArkType
import { type } from "arktype"

schema: type({
  STRIPE_PUBLISHABLE_KEY: "string",
  API_BASE_URL:           "string",
})

// Plain object — no validation, keys only
schema: {
  STRIPE_PUBLISHABLE_KEY: {},
  API_BASE_URL:           {},
}

Note on transforms: Schema transforms (e.g. z.string().transform(Number)) are intentionally ignored at build time. import.meta.env values are always strings in the browser. Apply numeric transforms in your app code instead.

Custom prefix

// No prefix → import.meta.env.STRIPE_PUBLISHABLE_KEY
zenvPlugin({ ..., prefix: "" })

// Custom prefix → import.meta.env.APP_STRIPE_PUBLISHABLE_KEY
zenvPlugin({ ..., prefix: "APP_" })

When using a custom prefix, extend ImportMetaEnv in your project's env.d.ts:

/// <reference types="vite/client" />

interface ImportMetaEnv {
  readonly [key: `APP_${string}`]: string | undefined
}

Dev server — automatic secret sync

In vite dev, the plugin polls zEnv every watch seconds (default: 30). When a secret changes, the dev server restarts automatically — no manual action needed.

// Poll every 60 seconds
zenvPlugin({ ..., watch: 60 })

// Disable polling (secrets only load on server start)
zenvPlugin({ ..., watch: false })

You'll see a log line when secrets reload:

[zenv] Secrets changed — restarting dev server…

Build behaviour

In vite build, the plugin fails hard if secrets cannot be fetched. This prevents shipping a broken bundle with undefined secret values. The error message includes the underlying API error for debugging.

TypeScript

The plugin ships an ambient declaration that makes import.meta.env.ZENV_* valid TypeScript without errors:

// src/vite-env.d.ts — already included by Vite scaffolding
/// <reference types="vite/client" />
/// <reference types="@zenv-sh/vite-plugin" />

vs. @zenv-sh/sdk

| | @zenv-sh/sdk | @zenv-sh/vite-plugin | |---|---|---| | Runs in | Node / server / edge | Vite build host (Node) | | Secrets available | At runtime (fetched on demand) | At build time (bundled as literals) | | Browser safe | ❌ (throws if window detected) | ✅ (values only, not credentials) | | Schema | Optional | Required | | Use for | APIs, SSR, CLI, backend | React, Vue, Svelte, etc. |