Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20]
node-version: [18, 20]

steps:
- name: Checkout
Expand Down
5 changes: 4 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ on:
jobs:
verify:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
permissions:
contents: read

Expand All @@ -18,7 +21,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: ${{ matrix.node-version }}
cache: npm

- name: Verify tag matches package version
Expand Down
58 changes: 58 additions & 0 deletions POST_ROADMAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Post-Roadmap Notes

This file is intentionally retired from active planning.

Use [ROADMAP.md](/Users/joshmclain/code/ts-env-validator/ROADMAP.md) as the only roadmap and release-planning source of truth.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Use a repository-relative link instead of a local filesystem path to ROADMAP.md.

This absolute path will only work on your machine and will be broken for other contributors and on GitHub. Please switch to a repo-relative link like [ROADMAP.md](./ROADMAP.md) so it resolves correctly for everyone.

Suggested change
Use [ROADMAP.md](/Users/joshmclain/code/ts-env-validator/ROADMAP.md) as the only roadmap and release-planning source of truth.
Use [ROADMAP.md](./ROADMAP.md) as the only roadmap and release-planning source of truth.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The link to ROADMAP.md uses an absolute path from your local machine (/Users/joshmclain/code/ts-env-validator/ROADMAP.md). This will not work for other users or in the GitHub UI. Please use a relative path instead.

Suggested change
Use [ROADMAP.md](/Users/joshmclain/code/ts-env-validator/ROADMAP.md) as the only roadmap and release-planning source of truth.
Use [ROADMAP.md](./ROADMAP.md) as the only roadmap and release-planning source of truth.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Replace absolute local path with relative path.

The path /Users/joshmclain/code/ts-env-validator/ROADMAP.md is a local filesystem path that won't resolve for other users or in repository contexts.

Proposed fix
-Use [ROADMAP.md](/Users/joshmclain/code/ts-env-validator/ROADMAP.md) as the only roadmap and release-planning source of truth.
+Use [ROADMAP.md](./ROADMAP.md) as the only roadmap and release-planning source of truth.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Use [ROADMAP.md](/Users/joshmclain/code/ts-env-validator/ROADMAP.md) as the only roadmap and release-planning source of truth.
Use [ROADMAP.md](./ROADMAP.md) as the only roadmap and release-planning source of truth.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@POST_ROADMAP.md` at line 5, The link in POST_ROADMAP.md uses an absolute
local path that won't resolve for others; change the link text to a relative
repository path (e.g., [ROADMAP.md](./ROADMAP.md) or simply
[ROADMAP.md](ROADMAP.md)) so it points to the project's ROADMAP.md file in repo
contexts and CI; update the line currently containing
"/Users/joshmclain/code/ts-env-validator/ROADMAP.md" to use the relative path.


### Testing Utilities

Future helpers:

- mock env generator
- test schema validation
- snapshot env configs

## Ecosystem Plan

Split into modular packages:

- `ts-env-validator` (core)
- `@ts-env-validator/cli`
- `@ts-env-validator/next`
- `@ts-env-validator/vite`
- `@ts-env-validator/github-action`
- `@ts-env-validator/docs`

## High Impact Features (Priority)

If limited time, prioritize:

- `.env.example` generation
- CLI (`check`, `generate-example`)
- Next.js adapter
- secret-aware validation
- documentation generation

## Success Metrics

- npm downloads growth
- GitHub stars
- usage in real projects
- adoption in CI pipelines
- developer feedback

## Long-Term Vision

Position the library as:

> The standard way to define and validate configuration in TypeScript applications

## Guiding Principle

Do not just add validators.

Build:

- a system
- a workflow
- a developer habit
65 changes: 56 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Without validation, bad config leaks into runtime and fails late. `ts-env-valida
npm install ts-env-validator
```

Node.js `>=20` is supported.
Node.js `>=18` is supported.

## Quick start

Expand All @@ -52,7 +52,7 @@ export const env = createEnv({
ENABLE_CACHE: boolean().default(false),
MAX_RETRIES: integer().default(3),
LATENCY_THRESHOLD: float().default(0.75),
FEATURE_FLAGS: json().optional(),
FEATURE_FLAGS: json<{ enabled: boolean; limit: number }>().optional(),
LOG_LEVEL: enumOf(["debug", "info", "warn", "error"]).optional(),
});
```
Expand All @@ -79,7 +79,7 @@ env.ALLOWED_HOSTS;
// string[]

env.FEATURE_FLAGS;
// unknown
// { enabled: boolean; limit: number } | undefined

env.LOG_LEVEL;
// "debug" | "info" | "warn" | "error" | undefined
Expand Down Expand Up @@ -194,15 +194,16 @@ DATABASE_URL: url()

For example, `"https://example.com"` becomes `"https://example.com/"`.

#### `json()`
#### `json<T>()`

Parses any valid JSON and returns `unknown`.
Parses any valid JSON and returns the caller-provided type.

```ts
FEATURE_FLAGS: json()
FEATURE_FLAGS: json<{ enabled: boolean; limit: number }>()
```

This validator accepts JSON objects, arrays, primitives, and `null`.
Typing is compile-time only; runtime validation is still plain `JSON.parse`.

#### `array(separator?)`

Expand All @@ -218,6 +219,52 @@ SCOPES: array(";")
- Empty items are rejected
- Separators are treated literally

### Custom validators

#### `createValidator({ expected, parse })`

Creates a custom validator that works with `createEnv`, type inference, and all existing modifiers.

```ts
import { createValidator } from "ts-env-validator";

const port = createValidator<number>({
expected: "port",
parse: (input) => {
const value = Number.parseInt(input, 10);

if (!Number.isInteger(value) || value < 1 || value > 65535) {
return {
success: false,
message: `expected port, received ${JSON.stringify(input)}`,
};
}

return {
success: true,
value,
};
},
});
```

Use it in schemas just like a built-in validator:

```ts
const env = createEnv({
PORT: port.default(3000),
});
```

Parser contract:

- `parse` receives the raw string value
- return `{ success: true, value }` on success
- return `{ success: false, message }` on failure
- keep parsers synchronous
- if `parse` throws an `Error`, its `message` is surfaced as the validation failure
- if `parse` throws a non-`Error` value, `ts-env-validator` falls back to `expected ${expected}, received ...`

### Modifiers

#### `.optional()`
Expand Down Expand Up @@ -308,7 +355,7 @@ export const env = createEnv({
JWT_SECRET: string(),
MAX_RETRIES: integer().default(3),
LATENCY_THRESHOLD: float().default(0.75),
SERVICE_METADATA: json().optional(),
SERVICE_METADATA: json<{ region: string; team: string }>().optional(),
});
```

Expand All @@ -320,7 +367,7 @@ import { array, createEnv, json, string } from "ts-env-validator";
export const env = createEnv({
NEXT_PUBLIC_ENABLED_LOCALES: array(),
NEXT_PUBLIC_API_URL: string(),
NEXT_PUBLIC_THEME_CONFIG: json().optional(),
NEXT_PUBLIC_THEME_CONFIG: json<{ accent: string; compact: boolean }>().optional(),
});
```

Expand All @@ -334,7 +381,7 @@ npm test
npm run build
```

Maintainers: pushing a version tag like `v0.2.0` runs the publish workflow, releasing `ts-env-validator` to npmjs and `@<repository-owner>/ts-env-validator` to GitHub Packages. The published npm tarball includes both `README.md` and `LICENSE`.
Maintainers: pushing a version tag like `v0.3.0` runs the publish workflow, releasing `ts-env-validator` to npmjs and `@<repository-owner>/ts-env-validator` to GitHub Packages. The published npm tarball includes both `README.md` and `LICENSE`.

## License

Expand Down
145 changes: 60 additions & 85 deletions ROADMAP.md
Original file line number Diff line number Diff line change
@@ -1,121 +1,96 @@
# ts-env-validator — Roadmap

## 🎯 Vision
`ROADMAP.md` is the single source of truth for planned releases.

A lightweight, TypeScript-first environment variable validator that:
- Validates at runtime
- Infers types at compile time
- Provides excellent developer experience
- Works across Node, Next.js, and serverless environments
## Vision

---
Build a lightweight, TypeScript-first environment validator that:

## 🚀 Milestones
- validates at runtime
- infers types at compile time
- stays small and framework-agnostic
- gives helpful startup failures instead of late runtime surprises

### v0.1.0 — MVP (Initial Release)
## Shipped

#### Core Features
### v0.1.0 — Core MVP

- `createEnv(schema, options?)`
- Validators:
- `string()`
- `number()`
- `boolean()`
- `enumOf([...])`
- `url()`

#### Modifiers

- `.optional()`
- `.default(value)`
- `.describe(text)`

#### Behavior

- Reads from `process.env`
- Supports custom env object
- Coerces types (string → number/boolean/etc.)
- Collects ALL errors before throwing
- Strong TypeScript inference

#### DX Features

- Clean error formatting
- Helpful error messages
- Zero config usage

---
- validators: `string()`, `number()`, `boolean()`, `enumOf([...])`, `url()`
- modifiers: `.optional()`, `.default(value)`, `.describe(text)`
- aggregated error reporting
- custom env source support

### v0.2.0 — Extended Types

Completed:

- `integer()`
- `float()`
- `json()`
- `json<T>()`
- `array(separator?)`

---
## Next

### v0.3.0 — Extensibility (Next)
### v0.3.0 — Extensibility Foundation

- Custom validator API
- `.transform(fn)`
- `.refine(fn)`
Goal: let users create first-class custom validators without expanding the validation model yet.

---
- public `createValidator({ expected, parse })` API
- public validator and parser result types
- custom validators work with `createEnv`
- custom validators support `.optional()`, `.default()`, and `.describe()`
- thrown parser errors are normalized into validation failures

### v0.4.0 — Ecosystem Enhancements
Explicitly deferred:

- dotenv helper
- `.env.example` validator CLI
- Next.js helpers (server/client env separation)
- `.transform(fn)`
- `.refine(fn)`
- constraint-style modifiers like `.min()` and `.max()`

---
## Provisional

### v1.0.0 — Stable Release
### v0.4.0 — Validation Constraints

- Finalized API
- Performance optimizations
- Full documentation
- Community feedback incorporated
Potential focus:

---
- string and number constraints
- `.transform(fn)`
- `.refine(fn, message?)`
- more composable validator pipelines

### v0.5.0 — Tooling

## 🧠 Design Principles
Potential focus:

- Minimal API surface
- Strong typing by default
- Fail fast at runtime
- Clear, readable errors
- Framework agnostic
- No unnecessary dependencies
- dotenv helper
- `.env.example` generation or validation
- schema-driven docs or CLI support

---
### v0.6.0 — Framework DX

## ❌ Explicit Non-Goals (v1)
Potential focus:

- Nested schemas
- Async validation
- Framework-specific wrappers
- Over-engineered abstractions
- Next.js helpers for server and client separation
- prefix enforcement for client-safe env keys
- framework-specific docs, not core package coupling

---
### v1.0.0 — Stable Release

## 📦 Release Strategy
- stable public API
- polished docs
- performance review
- community feedback incorporated

| Version | Focus |
|--------|------|
| 0.1.0 | Core functionality |
| 0.2.0 | More types |
| 0.3.0 | Extensibility |
| 1.0.0 | Stability |
## Design Principles

---
- minimal API surface
- strong typing by default
- readable errors
- sync, startup-time validation
- composable internals without overengineering

## 📈 Success Metrics
## Non-Goals

- Easy adoption in new projects
- Clean TypeScript inference
- Positive developer feedback
- GitHub stars / npm downloads
- nested schemas
- async validation
- framework-specific wrappers in the core package
- broad transform pipelines before the base validator contract is stable
Loading
Loading