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

simpleenvstruct

v1.1.0

Published

Read a flat string map (mainly environment variables) into a typed struct from a declarative spec. One file, zero dependencies.

Readme

simpleenvstruct

Read values out of a flat string map into a typed struct, following a declarative spec.

The intended use — the one it is named and designed for — is environment variables: renaming SCREAMING_SNAKE_CASE keys into the property names your code actually wants, and getting a precise TypeScript type for the result. Nothing in the implementation is specific to them, though. The source is just a Record<string, string | undefined>, so process.env, import.meta.env, a parsed .env, or any flat string map works the same.

One file, zero dependencies, no globals.

import { parseEnv, type EnvVarSpec } from "simpleenvstruct";

const spec = [
  { key: "VITE_FIREBASE_API_KEY", as: "apiKey" },
  { key: "VITE_FIREBASE_PROJECT_ID", as: "projectId" },
  { key: "VITE_FIRESTORE_EMULATOR_HOST", as: "emulatorHost" },
] as const satisfies EnvVarSpec[];

const config = parseEnv(spec, import.meta.env);
//    ^? { apiKey?: string; projectId?: string; emulatorHost?: string }

initializeApp({ apiKey: config.apiKey, projectId: config.projectId });

as is optional. Omit it and the key is used as the property name as-is:

const config = parseEnv([{ key: "PORT" }, { key: "HOME" }] as const, process.env);
//    ^? { PORT?: string; HOME?: string }

as const satisfies EnvVarSpec[] is what makes the names survive into the type. Without as const the spec widens to string and you get {}.

Install

npm install simpleenvstruct   # or: pnpm add simpleenvstruct

API

parseEnv(spec, env)

| Param | Type | Meaning | | ------ | ------------------------------------- | -------------------------------------------------------------- | | spec | readonly EnvVarSpec[] | { key, as? } entries — as is the output property name | | env | Record<string, string \| undefined> | The source: process.env, import.meta.env, or a plain object |

Returns an object keyed by as, falling back to key where as is omitted.

The source is always an explicit argument — the library never reaches for process.env or import.meta on its own, so the same build runs in Node, in the browser, and in tests where you hand it a fixture object.

Behavior

  • A value that is unset or an empty string is omitted from the result entirelyparseEnv never throws. Every property is therefore optional in the returned type.
  • Keys that are not in the spec are ignored, so unrelated secrets sitting in process.env can't leak into the struct by accident.
  • Values are returned as-is: no trimming, no coercion to number/boolean, no parsing.

EnvVarSpec / EnvStruct<T> / EnvSource

type EnvVarSpec = { key: string; as?: string };
type EnvSource = Record<string, string | undefined>;
type EnvStruct<T extends readonly EnvVarSpec[]>; // the struct type parseEnv returns

Use EnvStruct<typeof spec> when you want to name the config type in a signature.

Non-goals

Required-key enforcement, default values, .env file loading, and type coercion are all deliberately absent — they are what makes a library like this grow. Validate what you need at the call site:

const config = parseEnv(spec, process.env);
if (!config.apiKey) throw new Error("VITE_FIREBASE_API_KEY is not set");

If you want required / default / fallback declarations handled inside the spec, use envstruct instead — same spec shape, one level up.

Development

Built with Bun. tsc is used only to emit dist and the .d.ts.

bun install
bun test          # unit tests
bun run typecheck # includes the type-level tests in test/
bun run build     # emit dist/

License

MIT


simpleenvstruct(日本語)

宣言的な spec に従って、フラットな文字列マップから値を読み出し、型のついた構造体にして返す。

主に想定している用途は環境変数で、名前も設計もそこに合わせてある。 VITE_FIREBASE_API_KEY のようなキー名を、コード側で使いたいプロパティ名(apiKey)に 付け替え、その結果に正確な TypeScript の型を与える。 ただし実装に環境変数固有の処理はない。読み出し元は Record<string, string | undefined> に 過ぎないので、process.env でも import.meta.env でも、パース済みの .env でも、 任意のフラットな文字列マップでも同じように動く。

1ファイル・依存ゼロ・グローバル参照なし。

const spec = [
  { key: "VITE_FIREBASE_API_KEY", as: "apiKey" },
  { key: "VITE_FIRESTORE_EMULATOR_HOST", as: "emulatorHost" },
] as const satisfies EnvVarSpec[];

const config = parseEnv(spec, import.meta.env);
// 型: { apiKey?: string; emulatorHost?: string }

as は省略可能で、省いた場合は key がそのままプロパティ名になる。

const config = parseEnv([{ key: "PORT" }, { key: "HOME" }] as const, process.env);
// 型: { PORT?: string; HOME?: string }

as const satisfies EnvVarSpec[] を付けることで名前がリテラル型として型に残る。 as const がないと spec が string に広がり、結果の型は {} になる。

振る舞い

  • 未設定・空文字列の値はキーごと省く。throw はしない。 そのため返り値の全プロパティが optional。
  • spec にないキーは無視するので、process.env の無関係な秘密情報が構造体に紛れ込まない。
  • 値は加工しない(trim もしないし、number/boolean への変換もしない)。
  • 読み出し元は必ず引数で渡す。ライブラリ側からグローバルを触らないため、Node・ブラウザ・ テスト(フィクスチャを渡す)で同じコードが動く。

やらないこと

必須チェック・デフォルト値・.env の読み込み・型変換は意図的に持たない。必要なら呼び出し側で書く。

const config = parseEnv(spec, process.env);
if (!config.apiKey) throw new Error("VITE_FIREBASE_API_KEY is not set");

必須・デフォルト・フォールバックを spec の中で宣言したい場合は envstruct を使う(spec の形は同じで、一段上)。