Skip to content

Commit 8d4569d

Browse files
committed
feat: add Shelve secrets integration
- Fetch secrets from Shelve at build/runtime - onInstall wizard for setup (Nuxt 4.1+) - Move @nuxt/kit to peerDeps for onInstall support - Use --pnpm flag in pkg.pr.new for catalog: protocol - Add wizard E2E tests
1 parent ae0512c commit 8d4569d

30 files changed

+13118
-1322
lines changed

.github/workflows/pkg.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ jobs:
2020
- run: pnpm install
2121
- run: pnpm dev:prepare
2222
- run: pnpm prepack
23-
- run: pnpm dlx pkg-pr-new publish
23+
- run: pnpm dlx pkg-pr-new publish --pnpm

README.md

Lines changed: 309 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,65 +3,350 @@
33
</br>
44
Nuxt Safe Runtime Config</h1>
55
<p align="center">
6-
Validate Nuxt runtime config at build or runtime using <b>Zod</b>, <b>Valibot</b>, <b>ArkType</b>, or any Standard Schema compatible library.
6+
Validate Nuxt runtime config at build time using <b>Zod</b>, <b>Valibot</b>, <b>ArkType</b>, or any Standard Schema compatible library.
77
</p>
88
<br/>
99

1010
<p align="center">
11-
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config"><img src="https://img.shields.io/npm/v/nuxt-safe-runtime-config.svg" alt="npm version" /></a>
12-
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config"><img src="https://img.shields.io/npm/dm/nuxt-safe-runtime-config.svg" alt="npm downloads" /></a>
13-
<a href="https://github.com/onmax/nuxt-safe-runtime-config/blob/main/LICENSE"><img src="https://img.shields.io/github/license/onmax/nuxt-safe-runtime-config.svg" alt="License" /></a>
14-
<a href="https://nuxt.com"><img src="https://img.shields.io/badge/Nuxt-3.0+-00DC82.svg" alt="Nuxt" /></a>
15-
</p>
11+
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config">
12+
<img src="https://img.shields.io/npm/v/nuxt-safe-runtime-config.svg" alt="npm version" />
13+
</a>
14+
<a href="https://www.npmjs.com/package/nuxt-safe-runtime-config">
15+
<img src="https://img.shields.io/npm/dm/nuxt-safe-runtime-config.svg" alt="npm downloads" />
16+
</a>
17+
<a href="https://github.com/onmax/nuxt-safe-runtime-config/blob/main/LICENSE">
18+
<img src="https://img.shields.io/github/license/onmax/nuxt-safe-runtime-config.svg" alt="License" />
19+
</a>
20+
<a href="https://nuxt.com">
21+
<img src="https://img.shields.io/badge/Nuxt-3.0+-00DC82.svg" alt="Nuxt" />
22+
</a>
1623

17-
<p align="center">
18-
<a href="https://nuxt-safe-runtime-config.vercel.app">Documentation</a>
24+
<p align="center">
25+
<a href="https://github.com/nuxt/nuxt/discussions/32301">
26+
🔗 Related Nuxt RFC: Enable Standard Schema Validation in Nuxt Config
27+
</a>
28+
</p>
1929
</p>
2030

2131
## Features
2232

23-
- **Build-time validation** with Zod, Valibot, ArkType, or any [Standard Schema](https://standardschema.dev/) library
24-
- **Runtime validation** (opt-in) validates config when the server starts
25-
- **Auto-generated types** - `useSafeRuntimeConfig()` is fully typed
26-
- **ESLint plugin** warns when using `useRuntimeConfig()` instead of the type-safe composable
33+
- 🔒 **Build-time validation** with Zod, Valibot, ArkType, or any [Standard Schema](https://standardschema.dev/) library
34+
- 🚀 **Runtime validation** (opt-in) validates config when the server starts
35+
-**Auto-generated types**`useSafeRuntimeConfig()` is fully typed without manual generics
36+
- 🛠 **ESLint plugin** warns when using `useRuntimeConfig()` instead of the type-safe composable
37+
-**Zero runtime overhead** by default — validation happens at build time only
38+
- 🔐 **Shelve integration** — fetch secrets from [Shelve](https://shelve.cloud) and merge into runtime config
2739

28-
## Quick Start
40+
## Quick Setup
41+
42+
Install the module:
2943

3044
```bash
3145
npx nuxi module add nuxt-safe-runtime-config
3246
```
3347

34-
```ts [nuxt.config.ts]
48+
## Usage
49+
50+
### 1. Define your schema
51+
52+
Use Zod, Valibot, ArkType, or any Standard Schema compatible library:
53+
54+
<details>
55+
<summary>With Valibot</summary>
56+
57+
```typescript
3558
import { number, object, optional, string } from 'valibot'
3659

37-
const schema = object({
38-
public: object({ apiBase: string() }),
60+
const runtimeConfigSchema = object({
61+
public: object({
62+
apiBase: string(),
63+
appName: optional(string()),
64+
}),
3965
databaseUrl: string(),
66+
secretKey: string(),
4067
port: optional(number()),
4168
})
69+
```
70+
71+
</details>
72+
73+
<details>
74+
<summary>With Zod</summary>
75+
76+
```typescript
77+
import { z } from 'zod'
78+
79+
const runtimeConfigSchema = z.object({
80+
public: z.object({
81+
apiBase: z.string(),
82+
appName: z.string().optional(),
83+
}),
84+
databaseUrl: z.string(),
85+
secretKey: z.string(),
86+
port: z.number().optional(),
87+
})
88+
```
89+
90+
</details>
91+
92+
<details>
93+
<summary>With ArkType</summary>
94+
95+
```typescript
96+
import { type } from 'arktype'
97+
98+
const runtimeConfigSchema = type({
99+
'public': {
100+
'apiBase': 'string',
101+
'appName?': 'string'
102+
},
103+
'databaseUrl': 'string',
104+
'secretKey': 'string',
105+
'port?': 'number'
106+
})
107+
```
42108

109+
</details>
110+
111+
### 2. Configure in nuxt.config.ts
112+
113+
```typescript
43114
export default defineNuxtConfig({
44115
modules: ['nuxt-safe-runtime-config'],
116+
45117
runtimeConfig: {
46-
databaseUrl: process.env.DATABASE_URL || '',
118+
databaseUrl: process.env.DATABASE_URL || 'postgresql://localhost:5432/mydb',
119+
secretKey: process.env.SECRET_KEY || 'default-secret-key',
47120
port: Number.parseInt(process.env.PORT || '3000'),
48-
public: { apiBase: 'https://api.example.com' },
121+
public: {
122+
apiBase: process.env.PUBLIC_API_BASE || 'https://api.example.com',
123+
appName: 'My Nuxt App',
124+
},
125+
},
126+
127+
safeRuntimeConfig: {
128+
$schema: runtimeConfigSchema,
49129
},
50-
safeRuntimeConfig: { $schema: schema },
51130
})
52131
```
53132

133+
### 3. Use the type-safe composable
134+
135+
Access your validated config with full type safety — types are auto-generated from your schema:
136+
54137
```vue
55138
<script setup lang="ts">
56139
const config = useSafeRuntimeConfig()
57-
// config.public.apiBase - string (typed)
140+
// config.public.apiBase is typed as string
141+
// config.secretKey is typed as string
58142
</script>
59143
```
60144

61-
## Documentation
145+
## Configuration Options
62146

63-
Full documentation at **[nuxt-safe-runtime-config.vercel.app](https://nuxt-safe-runtime-config.vercel.app)**
147+
| Option | Type | Default | Description |
148+
| ------------------- | ------------------------------- | ----------------- | ------------------------------------------ |
149+
| `$schema` | `StandardSchemaV1` || Your validation schema (required) |
150+
| `validateAtBuild` | `boolean` | `true` | Validate during dev/build |
151+
| `validateAtRuntime` | `boolean` | `false` | Validate when server starts |
152+
| `onBuildError` | `'throw' \| 'warn' \| 'ignore'` | `'throw'` | How to handle build validation errors |
153+
| `onRuntimeError` | `'throw' \| 'warn' \| 'ignore'` | `'throw'` | How to handle runtime validation errors |
154+
| `logSuccess` | `boolean` | `true` | Log successful validation |
155+
| `logFallback` | `boolean` | `true` | Log when using JSON Schema fallback |
156+
| `jsonSchemaTarget` | `string` | `'draft-2020-12'` | JSON Schema version for runtime validation |
157+
| `shelve` | `boolean \| ShelveOptions` | `undefined` | Shelve secrets integration (see below) |
64158

65-
## License
159+
## Shelve Integration
160+
161+
[Shelve](https://shelve.cloud) is a secrets management service. This module fetches secrets from Shelve at build time and merges them into your runtime config before validation.
162+
163+
### Zero-Config Setup
164+
165+
If you have a `shelve.json` file in your project root, the integration enables automatically:
166+
167+
```ts
168+
export default defineNuxtConfig({
169+
safeRuntimeConfig: {
170+
$schema: runtimeConfigSchema,
171+
shelve: true, // Auto-detects project, team, and environment
172+
},
173+
})
174+
```
175+
176+
The module resolves configuration from multiple sources (highest priority first):
177+
178+
| Config | Sources |
179+
| ----------- | ---------------------------------------------------------------------- |
180+
| project | `nuxt.config``SHELVE_PROJECT``shelve.json``package.json` name |
181+
| slug | `nuxt.config``SHELVE_TEAM_SLUG``shelve.json` |
182+
| environment | `nuxt.config``SHELVE_ENV``shelve.json` → dev mode auto |
183+
| token | `SHELVE_TOKEN``~/.shelve` file |
184+
185+
### Explicit Configuration
186+
187+
You can override any auto-detected value:
188+
189+
```ts
190+
export default defineNuxtConfig({
191+
safeRuntimeConfig: {
192+
$schema: runtimeConfigSchema,
193+
shelve: {
194+
project: 'my-app',
195+
slug: 'my-team',
196+
environment: 'production',
197+
url: 'https://app.shelve.cloud', // Self-hosted Shelve
198+
fetchAtBuild: true, // Default: fetch at build time
199+
fetchAtRuntime: false, // Opt-in: fetch on server cold start
200+
},
201+
},
202+
})
203+
```
204+
205+
### Variable Transformation
206+
207+
Shelve variables transform from `SCREAMING_SNAKE_CASE` to `camelCase` with smart grouping:
208+
209+
```
210+
DATABASE_URL → databaseUrl
211+
GITHUB_CLIENT_ID → github.clientId (grouped)
212+
GITHUB_CLIENT_SECRET → github.clientSecret (grouped)
213+
PUBLIC_API_URL → public.apiUrl
214+
```
215+
216+
Variables with repeated prefixes (2+ keys) nest automatically. `PUBLIC_*` and `NUXT_PUBLIC_*` map to `runtimeConfig.public`.
217+
218+
### Runtime Fetch (Opt-in)
219+
220+
For dynamic environments or secret rotation, enable runtime fetching:
221+
222+
```ts
223+
export default defineNuxtConfig({
224+
safeRuntimeConfig: {
225+
shelve: {
226+
fetchAtBuild: true, // Bake secrets into build
227+
fetchAtRuntime: true, // Also refresh on cold start
228+
},
229+
},
230+
})
231+
```
232+
233+
The runtime plugin runs before validation, so freshly fetched secrets are validated against your schema.
234+
235+
## Runtime Validation
236+
237+
By default, validation only runs at build time. Enable runtime validation to catch environment variable issues when the server starts:
238+
239+
```ts
240+
export default defineNuxtConfig({
241+
safeRuntimeConfig: {
242+
$schema: runtimeConfigSchema,
243+
validateAtRuntime: true,
244+
},
245+
})
246+
```
247+
248+
Runtime validation uses [@cfworker/json-schema](https://github.com/cfworker/cfworker/tree/main/packages/json-schema) to validate the config after environment variables are merged. This lightweight validator (~8KB) works on all runtimes including edge (Cloudflare Workers, Vercel Edge, Netlify Edge). It catches issues like:
249+
250+
- Environment variables with wrong types (e.g., `NUXT_PORT=abc` when expecting a number)
251+
- Missing required environment variables in production
252+
- Invalid values that pass build-time checks but fail at runtime
253+
254+
## ESLint Integration
255+
256+
The module includes an ESLint plugin that warns when using `useRuntimeConfig()` instead of `useSafeRuntimeConfig()`.
257+
258+
### With @nuxt/eslint (Automatic)
259+
260+
If you use [@nuxt/eslint](https://eslint.nuxt.com), the rule is auto-registered. No configuration needed.
261+
262+
### Manual Setup
263+
264+
Add to your `eslint.config.mjs`:
265+
266+
```javascript
267+
import { configs } from 'nuxt-safe-runtime-config/eslint'
268+
269+
export default [
270+
configs.recommended,
271+
// ... your other configs
272+
]
273+
```
274+
275+
Or configure manually:
276+
277+
```javascript
278+
import plugin from 'nuxt-safe-runtime-config/eslint'
279+
280+
export default [
281+
{
282+
plugins: { 'safe-runtime-config': plugin },
283+
rules: { 'safe-runtime-config/prefer-safe-runtime-config': 'warn' },
284+
},
285+
]
286+
```
287+
288+
The rule includes auto-fix support — run `eslint --fix` to automatically replace `useRuntimeConfig()` calls.
289+
290+
## Type Safety
291+
292+
Types are auto-generated at build time from your schema's JSON Schema representation. The `useSafeRuntimeConfig()` composable returns a fully typed object — no manual generics needed:
293+
294+
```ts
295+
const config = useSafeRuntimeConfig()
296+
// config is fully typed based on your schema
297+
```
298+
299+
Generated types are stored in `.nuxt/types/safe-runtime-config.d.ts` and automatically included in your project.
300+
301+
## Error Messages
302+
303+
When validation fails, you see detailed error messages:
304+
305+
```
306+
[safe-runtime-config] Validation failed!
307+
1. databaseUrl: Invalid type: Expected string but received undefined
308+
2. public.apiBase: Invalid type: Expected string but received undefined
309+
3. port: Invalid type: Expected number but received string
310+
```
311+
312+
The module stops the build process until all validation errors are resolved.
313+
314+
## Why This Module?
315+
316+
Nuxt's built-in schema validation is designed for module authors and broader configuration. This module focuses specifically on **runtime config validation** using Standard Schema, allowing you to:
317+
318+
- Use your preferred validation library (Valibot, Zod, ArkType)
319+
- Catch configuration errors at build time
320+
- Optionally validate at runtime for environment variable issues
321+
- Get full type safety in your components
322+
323+
## Contribution
324+
325+
<details>
326+
<summary>Local development</summary>
327+
328+
```bash
329+
# Install dependencies
330+
pnpm install
331+
332+
# Generate type stubs
333+
pnpm run dev:prepare
334+
335+
# Develop with the playground
336+
pnpm run dev
337+
338+
# Build the playground
339+
pnpm run dev:build
340+
341+
# Run ESLint
342+
pnpm run lint
343+
344+
# Run Vitest
345+
pnpm run test
346+
pnpm run test:watch
347+
348+
# Release new version
349+
pnpm run release
350+
```
66351

67-
MIT
352+
</details>

docs/.config/docs.yaml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
name: "nuxt-safe-runtime-config"
2-
shortDescription: "Type-safe runtime config for Nuxt"
3-
description: "Validate Nuxt runtime config at build or runtime using Zod, Valibot, ArkType, or any Standard Schema compatible library."
4-
github: "onmax/nuxt-safe-runtime-config"
5-
url: "https://nuxt-safe-runtime-config.vercel.app"
6-
themeColor: "green"
1+
name: nuxt-safe-runtime-config
2+
shortDescription: Type-safe runtime config for Nuxt
3+
description: 'Validate Nuxt runtime config at build or runtime using Zod, Valibot, ArkType, or any Standard Schema compatible library.'
4+
github: onmax/nuxt-safe-runtime-config
5+
url: 'https://nuxt-safe-runtime-config.vercel.app'
6+
themeColor: green
77
landing:
88
contributors: true

0 commit comments

Comments
 (0)