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

@g14o/env-core

v0.2.0

Published

Framework-agnostic, typesafe environment variables via Standard Schema (Zod, Valibot, ArkType).

Readme

@g14o/env-core

Framework-agnostic, typesafe environment variables validated with any Standard Schema library (Zod, Valibot, ArkType, and others).

Zero runtime dependencies — install only the validator you use.

Install

pnpm add @g14o/env-core zod

Peer dependencies

zod, valibot, and arktype are optional peers — install the validator you use. Ranges follow Standard Schema v1 support on each major line (arktype ^2.0.0, valibot ^1.0.0, zod ^4.0.0). typescript (>=5) is optional but recommended for typed env definitions.

Usage

import { createEnv } from "@g14o/env-core";
import * as z from "zod";

export const env = createEnv({
  clientPrefix: "PUBLIC_",
  server: {
    DATABASE_URL: z.url(),
    OPEN_AI_API_KEY: z.string().min(1),
  },
  client: {
    PUBLIC_API_URL: z.url(),
  },
  runtimeEnv: process.env,
  emptyStringAsUndefined: true,
});

On the server, all variables are validated and readable. On the client, only client keys are validated; accessing a server key throws with the exact variable name.

Import the same env object in server and client code — no separate client export is required.

How client protection works

createEnv returns a Proxy around the validated environment object (on both server and client):

  • Server: the proxy is transparent — all validated server and client keys are readable.
  • Client: only client schemas are validated at import time (missing server vars in process.env do not throw).
  • Client access: any property that is not a declared client key throws with the exact name, e.g. DATABASE_URL or typos like NOT_DECLARED.
  • Interop: __esModule and $$typeof are ignored so bundlers and React do not trigger false positives.

Valibot

import { createEnv } from "@g14o/env-core";
import * as v from "valibot";

export const env = createEnv({
  clientPrefix: "PUBLIC_",
  server: {
    DATABASE_URL: v.pipe(v.string(), v.url()),
  },
  client: {
    PUBLIC_API_URL: v.pipe(v.string(), v.url()),
  },
  runtimeEnv: process.env,
});

ArkType

import { createEnv } from "@g14o/env-core";
import { type } from "arktype";

export const env = createEnv({
  server: {
    DATABASE_URL: type("string.url"),
  },
  clientPrefix: "PUBLIC_",
  client: {
    PUBLIC_API_URL: type("string.url"),
  },
  runtimeEnv: process.env,
});

Strict runtime mapping (bundler-friendly)

When your framework only inlines env vars you reference explicitly, use runtimeEnvStrict:

import { createEnv } from "@g14o/env-core";
import * as z from "zod";

export const env = createEnv({
  server: { DATABASE_URL: z.url() },
  clientPrefix: "NEXT_PUBLIC_",
  client: { NEXT_PUBLIC_API_URL: z.url() },
  runtimeEnvStrict: {
    DATABASE_URL: process.env.DATABASE_URL,
    NEXT_PUBLIC_API_URL: process.env.NEXT_PUBLIC_API_URL,
  },
});

Overriding default error handler

import { createEnv } from "@g14o/env-core";

export const env = createEnv({
  // ...
  // Called when schema validation fails.
  onValidationError: (issues) => {
    console.error("Invalid environment variables:", issues);
    throw new Error("Invalid environment variables");
  },
  // Called when a server variable is accessed on the client.
  onInvalidAccess: (variable) => {
    console.error("Invalid access to server variable:", variable);
    throw new Error("Invalid access to server variable");
  },
});

Tell when we're in a server context

import { createEnv } from "@g14o/env-core";

export const env = createEnv({
  // ...
  // Tell when we're in a server context
  isServer: typeof window === "undefined", // or straight up `true` or `false`
});

Skip validation

skipValidation bypasses both schema validation and the client-access proxy. Only use it when exposing the picked keys acceptable for the current runtime.

import { createEnv } from "@g14o/env-core";

export const env = createEnv({
  // ...
  // Tell the library to skip validation and return picked runtime values only
  skipValidation: true,
});

Options

| Option | Description | |--------|-------------| | server | Server-only variables (validated on server; throw on client access) | | client | Client-safe variables (validated on server and client) | | clientPrefix | Optional prefix required on all client keys (type + runtime) | | runtimeEnv | Record to read values from (e.g. process.env) | | runtimeEnvStrict | Explicit per-key mapping; mutually exclusive with runtimeEnv | | emptyStringAsUndefined | Treat "" as undefined before validation | | isServer | Override server detection (default: typeof window === "undefined") | | onValidationError | Hook when schema validation fails; may throw custom error, otherwise default InvalidEnvironmentVariablesError is thrown | | onInvalidAccess | Hook when a server key is accessed on the client; may throw custom error, otherwise default server-access error is thrown | | skipValidation | Bypasses both schema validation and the client-access proxy. Only use it when exposing the picked keys acceptable for the current runtime (default: false) |

Security

Server values are never validated or exposed on the client. Importing a single env.ts that defines server schemas still ships those names to the client bundle. For sensitive names, split into env/server.ts and env/client.ts.

License

MIT