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
27 changes: 27 additions & 0 deletions .agents/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Safe Runtime Config

This context defines the project language for validating Nuxt and Nitro runtime configuration.

## Language

**Runtime validation**:
Validation of the resolved server runtime config when Nitro starts.
_Avoid_: startup validation, server validation

**Build validation**:
Validation of configured runtime config during Nuxt or Nitro build hooks.
_Avoid_: compile-time validation, static validation

## Relationships

- **Runtime validation** happens after runtime environment values have been resolved.
- **Build validation** happens before the server starts.

## Example dialogue

> **Dev:** "Should this failure be handled by **Build validation**?"
> **Domain expert:** "No, it depends on the server's resolved environment, so it belongs to **Runtime validation**."

## Flagged ambiguities

- "runtime validation" means validation when Nitro starts, not validation inside the Vue app runtime.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ dist
*.tgz

# Nuxt
.nitro
.nuxt
.output
.nuxtrc
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,8 @@ Runtime validation uses [@cfworker/json-schema](https://github.com/cfworker/cfwo
- Missing required environment variables in production
- Invalid values that pass build-time checks but fail at runtime

Shelve runtime fetching is separate from validation ownership. If `shelve.fetchAtRuntime` is enabled, the Shelve runtime plugin runs before validation so fetched secrets are included in the validated config.

## ESLint Integration

The module includes an ESLint plugin that warns when using `useRuntimeConfig()` instead of `useSafeRuntimeConfig()`.
Expand Down
7 changes: 5 additions & 2 deletions build.config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { defineBuildConfig } from 'unbuild'

export default defineBuildConfig({
entries: [{ input: 'src/eslint/index', name: 'eslint' }],
entries: [
{ input: 'src/nitro', name: 'nitro' },
{ input: 'src/eslint/index', name: 'eslint' },
],
declaration: true,
clean: false,
rollup: { emitCJS: false },
externals: ['@typescript-eslint/utils', '@typescript-eslint/types', 'eslint'],
externals: ['@nuxt/kit', '@typescript-eslint/utils', '@typescript-eslint/types', 'eslint'],
})
8 changes: 6 additions & 2 deletions docs/content/1.guide/4.validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ icon: ph:seal-check

The module validates your runtime config to catch configuration errors early. By default, validation runs at build time only.

Validation is handled by the package's Nitro module. In Nuxt apps, the Nuxt module exposes the user-facing API, types, and imports, then passes validation options into Nitro. Generated validation files are internal implementation details and their paths are not part of the public API.

## Build-time Validation (Default)

With default settings, validation runs at:
With default settings, build validation runs through Nitro during:

- **Dev start** - when running `nuxt dev`
- **Build completion** - when running `nuxt build`
- **Build** - when running `nuxt build`

```ts [nuxt.config.ts]
export default defineNuxtConfig({
Expand Down Expand Up @@ -54,6 +56,8 @@ When enabled:
2. A Nitro plugin validates `useRuntimeConfig()` on cold start
3. Uses [@cfworker/json-schema](https://github.com/cfworker/cfworker/tree/main/packages/json-schema) (~8KB, edge-compatible)

Shelve runtime fetching remains a separate integration. When `shelve.fetchAtRuntime` is enabled, that runtime fetch plugin runs before validation so fetched secrets can be validated.

What it catches:

- Missing env vars in production (`DATABASE_URL` not set)
Expand Down
3 changes: 3 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,8 @@ export default antfu({
'playground/shelve.json',
'src/runtime/**/*.d.ts',
'src/runtime/**/*.js',
'test/fixtures/**/.nitro/**',
'test/fixtures/**/.nuxt/**',
'test/fixtures/**/.output/**',
],
})
46 changes: 40 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,38 @@
],
"sideEffects": false,
"exports": {
".": { "types": "./dist/types.d.mts", "import": "./dist/module.mjs" },
"./eslint": { "types": "./dist/eslint.d.mts", "import": "./dist/eslint.mjs" }
".": {
"types": "./dist/types.d.mts",
"import": "./dist/module.mjs"
},
"./nuxt": {
"types": "./dist/types.d.mts",
"import": "./dist/module.mjs"
},
"./nitro": {
"types": "./dist/nitro.d.mts",
"import": "./dist/nitro.mjs"
},
"./eslint": {
"types": "./dist/eslint.d.mts",
"import": "./dist/eslint.mjs"
}
},
"main": "./dist/module.mjs",
"typesVersions": {
"*": {
".": ["./dist/types.d.mts"],
"eslint": ["./dist/eslint.d.mts"]
".": [
"./dist/types.d.mts"
],
"nuxt": [
"./dist/types.d.mts"
],
"nitro": [
"./dist/nitro.d.mts"
],
"eslint": [
"./dist/eslint.d.mts"
]
}
},
"files": [
Expand All @@ -56,10 +80,19 @@
},
"peerDependencies": {
"@nuxt/kit": "^3.0.0 || ^4.0.0 || ^5.0.0-0",
"eslint": ">=9.0.0"
"eslint": ">=9.0.0",
"nitro": "^3.0.0-beta"
},
"peerDependenciesMeta": {
"eslint": { "optional": true }
"@nuxt/kit": {
"optional": true
},
"eslint": {
"optional": true
},
"nitro": {
"optional": true
}
},
"dependencies": {
"@cfworker/json-schema": "catalog:",
Expand Down Expand Up @@ -93,6 +126,7 @@
"eslint": "catalog:",
"eslint-plugin-format": "catalog:",
"lint-staged": "catalog:",
"nitro": "catalog:",
"nuxt": "catalog:",
"simple-git-hooks": "catalog:",
"typescript": "catalog:",
Expand Down
Loading
Loading