Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
85f1c22
feat: add nitrov3 support
schplitt Jan 27, 2026
3fc91f7
chore: add ufo package to dependencies
schplitt Jan 27, 2026
d55f967
docs: add note for early Nitro v3 support in installation guide
schplitt Jan 27, 2026
e25ae9b
chore: initialize nitrov3 playground
schplitt Jan 27, 2026
e6e9445
revert: nitro 2 playground
schplitt Jan 27, 2026
9d005f0
chore: add nitrov3 output to package.json
schplitt Jan 27, 2026
0b1592a
docs: add note for early Nitro v3 support in README
schplitt Jan 27, 2026
6e7727d
chore: better nitro v3 playground
schplitt Jan 27, 2026
ef07be7
chore: expose nitro v3 at `nitro/v3`
schplitt Jan 27, 2026
6171d49
chore: update Nitro v3 references to use `evlog/nitro/v3`
schplitt Jan 27, 2026
d307849
Merge branch 'main' into feat/nitrov3-support
HugoRCD Jan 27, 2026
0994c10
up
HugoRCD Jan 27, 2026
90e2c45
feat(nitro-v3): align EvlogError with Nitro v3 HTTPError contract
schplitt Jan 31, 2026
f6e7b73
Merge remote-tracking branch 'origin/main' into feat/nitrov3-support
schplitt Jan 31, 2026
23c32cd
nitro/v3 feature parity
schplitt Jan 31, 2026
2bd17f6
fix: error handling/logging
schplitt Jan 31, 2026
860568f
Merge remote-tracking branch 'origin/main' into feat/nitrov3-support
schplitt Jan 31, 2026
4d7b5e4
nitrov3 respect exclude config
schplitt Jan 31, 2026
9dbe202
Merge remote-tracking branch 'origin/main' into feat/nitrov3-support
schplitt Feb 10, 2026
3fe2419
feat: nitro v3 feature parity
schplitt Feb 10, 2026
5e2e51d
chore: deps
schplitt Feb 10, 2026
da5ff3a
build: add nitro v3 plugin and error handler entries to tsdown config
schplitt Feb 10, 2026
3be516a
test: nitro v3 tests
schplitt Feb 10, 2026
83ecf18
chore: clean up nitro v3 playground
schplitt Feb 10, 2026
cd2f9fb
docs: update nitro v3 usage
schplitt Feb 10, 2026
8b1339f
docs: update README for Nitro v3 playground
schplitt Feb 10, 2026
2bb0b09
Merge remote-tracking branch 'origin/main' into feat/nitrov3-support
schplitt Feb 11, 2026
fa5f175
build: explicitly inline `ufo`
schplitt Feb 11, 2026
0534fc1
refactor: remove evlog path mappings from playground tsconfig
schplitt Feb 11, 2026
5a5de18
docs: update Nitro v3 plugin and errorHandler registration
schplitt Feb 11, 2026
a353be6
refactor: leave unknown errors to default error handler
schplitt Feb 11, 2026
933faee
Merge branch 'main' into feat/nitrov3-support
HugoRCD Feb 12, 2026
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: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ export default defineNitroConfig({
})
```

> **Note**: For early Nitro v3 support, use `evlog/nitro/v3` instead of `evlog/nitro`.
Same API, same wide events:

```typescript
Expand Down
2 changes: 2 additions & 0 deletions apps/docs/content/1.getting-started/1.introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ Structured errors provide actionable context:
::code-group
```typescript [Code]
// server/api/checkout.post.ts
import { createError } from 'evlog'

throw createError({
message: 'Payment failed',
status: 402,
Expand Down
31 changes: 30 additions & 1 deletion apps/docs/content/1.getting-started/2.installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -373,13 +373,42 @@ bun add evlog
```
::

Then, add evlog as a Nitro plugin (without Nuxt) using the `evlog/nitro` plugin:
### Nitro v2

Use the Nitro v2 plugin and error handler directly in your config:

::code-group
```typescript [nitro.config.ts]
export default defineNitroConfig({
plugins: ['evlog/nitro'],
errorHandler: ['evlog/nitro'],
})
```
::

### Nitro v3

Nitro v3 auto-loads plugins from `./plugins`. Create a plugin file and an error handler file next to `nitro.config.ts`:

::code-group
```typescript [nitro.config.ts]
import { defineConfig } from 'nitro'

export default defineConfig({
errorHandler: './errorHandler',
})
```
```typescript [errorHandler.ts]
export { default } from 'evlog/nitro/v3/errorHandler'
```
```typescript [plugins/evlog.ts]
export { default } from 'evlog/nitro/v3'
```
::

::callout{icon="i-lucide-info" color="info"}
**Note:** The `createError` function is available from `evlog` for all Nitro versions. Only the plugin and error handler paths differ between v2 and v3.
::

## Cloudflare Workers

Expand Down
8 changes: 8 additions & 0 deletions apps/nitro-v3-playground/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
node_modules
dist
.data
.nitro
.cache
.output
.env
.env.local
14 changes: 14 additions & 0 deletions apps/nitro-v3-playground/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Nitro v3 evlog playground

## Getting started

```bash
bun install
bun run dev
```

## Deploying

```bash
bun run build
```
171 changes: 171 additions & 0 deletions apps/nitro-v3-playground/bun.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions apps/nitro-v3-playground/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'evlog/nitro/v3/errorHandler'
6 changes: 6 additions & 0 deletions apps/nitro-v3-playground/nitro.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { defineConfig } from 'nitro'

export default defineConfig({
serverDir: './',
errorHandler: './errorHandler',
})
16 changes: 16 additions & 0 deletions apps/nitro-v3-playground/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "evlog-nitro-v3-playground",
"type": "module",
"scripts": {
"build": "nitro build",
"dev": "nitro dev",
"preview": "npx srvx --prod .output/"
},
"dependencies": {
"evlog": "workspace:*"
},
"devDependencies": {
"nitro": "latest",
"rolldown": "latest"
}
}
1 change: 1 addition & 0 deletions apps/nitro-v3-playground/plugins/evlog.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from 'evlog/nitro/v3'
29 changes: 29 additions & 0 deletions apps/nitro-v3-playground/routes/api/test/create.post.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { defineHandler, readBody } from 'nitro/h3'
import { useLogger } from 'evlog'

export default defineHandler(async (event) => {
const log = useLogger(event)
const body = await readBody(event) as { name: string; type: string }

log.set({ user: { id: 456 } })
log.set({ item: { name: body.name, type: body.type } })

// Simulate validation
await new Promise(resolve => setTimeout(resolve, 100))
log.set({ validation: { passed: true, checks: ['name_length', 'type_valid'] } })

// Simulate database insert
await new Promise(resolve => setTimeout(resolve, 200))
const itemId = `item_${Math.random().toString(36).substring(2, 8)}`
log.set({ database: { operation: 'insert', duration: '200ms' } })

// Simulate search indexing
await new Promise(resolve => setTimeout(resolve, 80))
log.set({ search: { indexed: true, engine: 'elasticsearch' } })

return {
success: true,
message: 'Item created',
id: itemId,
}
})
28 changes: 28 additions & 0 deletions apps/nitro-v3-playground/routes/api/test/delete.delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog'

export default defineHandler(async (event) => {
const log = useLogger(event)
const itemId = 'item_123'

log.set({ user: { id: 789 } })
log.set({ action: 'delete_item', itemId })

// Simulate permission check
await new Promise(resolve => setTimeout(resolve, 100))
log.set({ permissions: { checked: true, hasDelete: true } })

// Simulate database delete
await new Promise(resolve => setTimeout(resolve, 150))
log.set({ database: { operation: 'delete', affected: 1 } })

// Simulate cache invalidation
await new Promise(resolve => setTimeout(resolve, 50))
log.set({ cache: { invalidated: true, keys: [`item:${itemId}`] } })

return {
success: true,
message: 'Item deleted',
deletedId: itemId,
}
})
24 changes: 24 additions & 0 deletions apps/nitro-v3-playground/routes/api/test/success.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog'

export default defineHandler(async (event) => {
const log = useLogger(event)

log.set({ user: { id: 123, plan: 'pro' } })
log.set({ action: 'fetch_profile' })

// Simulate database query
await new Promise(resolve => setTimeout(resolve, 150))
const profile = { name: 'John Doe', email: 'john@example.com', lastLogin: new Date().toISOString() }
log.set({ profile })

// Simulate cache check
await new Promise(resolve => setTimeout(resolve, 50))
log.set({ cache: { hit: true, ttl: 3600 } })

return {
success: true,
message: 'Profile fetched successfully',
data: profile,
}
})
26 changes: 26 additions & 0 deletions apps/nitro-v3-playground/routes/api/test/update.put.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { defineHandler, readBody } from 'nitro/h3'
import { useLogger, createError } from 'evlog'

export default defineHandler(async (event) => {
const log = useLogger(event)
const body = await readBody(event) as { name: string; version: string }

log.set({ user: { id: 999 } })
log.set({ action: 'update_item', item: { name: body.name, version: body.version } })

// Simulate validation check
await new Promise(resolve => setTimeout(resolve, 100))
log.set({ validation: { checked: true } })

// Simulate permission check that always fails
await new Promise(resolve => setTimeout(resolve, 150))
log.set({ permissions: { checked: true, hasUpdate: false, requiredRole: 'admin' } })

throw createError({
message: 'Update failed',
status: 403,
why: 'Insufficient permissions to update this resource',
fix: 'Request admin privileges or contact your team lead',
link: 'https://docs.example.com/permissions',
})
})
72 changes: 72 additions & 0 deletions apps/nitro-v3-playground/routes/api/test/wide-event.get.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { defineHandler } from 'nitro/h3'
import { useLogger } from 'evlog'

export default defineHandler(async (event) => {
const log = useLogger(event)

// User context
await new Promise(resolve => setTimeout(resolve, 80))
log.set({
user: {
id: 'user_789',
email: 'demo@example.com',
plan: 'enterprise',
accountAge: '2 years',
role: 'admin',
},
session: {
device: 'desktop',
browser: 'Chrome 120',
country: 'US',
},
})

// Cart information
await new Promise(resolve => setTimeout(resolve, 80))
log.set({
cart: {
items: 5,
total: 24999,
currency: 'USD',
discount: {
code: 'WINTER25',
savings: 8333,
},
},
})

// Payment processing
await new Promise(resolve => setTimeout(resolve, 80))
log.set({
payment: {
method: 'card',
cardBrand: 'visa',
cardLast4: '4242',
},
fraud: {
score: 12,
riskLevel: 'low',
passed: true,
},
})

// Performance metrics
await new Promise(resolve => setTimeout(resolve, 80))
log.set({
performance: {
dbQueries: 8,
cacheHits: 12,
cacheMisses: 2,
},
flags: {
newCheckoutFlow: true,
experimentId: 'exp_checkout_v2',
},
})

return {
success: true,
message: 'Wide event demo - check your terminal!',
orderId: 'ord_abc123xyz',
}
})
19 changes: 19 additions & 0 deletions apps/nitro-v3-playground/routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { useLogger } from 'evlog'
import { defineHandler } from 'nitro/h3'

export default defineHandler((event) => {
const log = useLogger(event)
log.set({
playground: 'nitro/v3',
})
return {
message: 'evlog Nitro v3 Playground',
endpoints: [
'GET /api/test/success',
'POST /api/test/create',
'PUT /api/test/update (always fails)',
'DELETE /api/test/delete',
'GET /api/test/wide-event',
],
}
})
9 changes: 9 additions & 0 deletions apps/nitro-v3-playground/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": ["nitro/tsconfig"],
"compilerOptions": {
"moduleResolution": "bundler",
"paths": {
"~/*": ["./*"],
}
Copy link
Owner

Choose a reason for hiding this comment

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

Do we really need to customize tsconfig? Ideally, I would like the user to have as little to configure as possible

Copy link
Author

Choose a reason for hiding this comment

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

Bun workspaces does not resolve the types correctly.
It doesn't seem to correctly install the evlog package from the workspace on my machine

Copy link
Author

Choose a reason for hiding this comment

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

fixed

}
}
Loading