Skip to content

Commit

Permalink
feat: add shorthand option for display request props as metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Dec 13, 2024
1 parent 05d0612 commit 227391b
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 14 deletions.
20 changes: 8 additions & 12 deletions examples/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,6 @@ createServer(async (req, res) => {
} catch (error) {
const statusCode = error.status ?? 500
const status = HTTP_STATUSES.find((httpStatus) => httpStatus.code === statusCode)
const headers = Object.keys(req.headers).map((key) => {
const value = req.headers[key]
return {
key,
value: key === 'cookie' ? { ...cookie.parse(value as string) } : value,
}
})

const youch = new Youch()

if (error instanceof E_ROUTE_NOT_FOUND) {
Expand All @@ -59,11 +51,15 @@ createServer(async (req, res) => {
})
}

youch.metadata.group('Request', {
headers,
const html = await youch.toHTML(error, {
title: status?.pharse,
cspNonce: 'fooooo',
request: {
url: req.url,
method: req.method,
headers: req.headers,
},
})

const html = await youch.toHTML(error, { title: status?.pharse, cspNonce: 'fooooo' })
res.writeHead(statusCode, { 'content-type': 'text/html' })
res.write(html)
res.end()
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"@types/pg": "^8.11.10",
"axios": "^1.7.9",
"c8": "^10.1.3",
"cookie": "^1.0.2",
"copyfiles": "^2.4.1",
"eslint": "^9.16.0",
"flydrive": "^1.1.0",
Expand Down Expand Up @@ -130,6 +129,7 @@
"dependencies": {
"@poppinss/dumper": "^0.6.1",
"@speed-highlight/core": "^1.2.7",
"cookie": "^1.0.2",
"youch-core": "^0.3.0"
}
}
2 changes: 1 addition & 1 deletion src/templates/error_metadata/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export class ErrorMetadata extends BaseComponent<ErrorMetadataProps> {
${
Array.isArray(rows)
? this.#renderRows(rows, cspNonce)
: this.#formatRowValue(rows.value, rows.dump, cspNonce)
: `<span>${this.#formatRowValue(rows.value, rows.dump, cspNonce)}</span>`
}
</div>`
}
Expand Down
10 changes: 10 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,16 @@ export type YouchHTMLOptions = {
* CSP nonce to define on inline style and script tags
*/
cspNonce?: string

/**
* Specify the HTTP request properties to be printed as
* metadata under the "Request" group
*/
request?: {
url?: string
method?: string
headers?: Record<string, string | string[] | undefined>
}
}

/**
Expand Down
42 changes: 42 additions & 0 deletions src/youch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* file that was distributed with this source code.
*/

import cookie from 'cookie'
import { ErrorParser } from 'youch-core'
import type { Parser, SourceLoader, Transformer, YouchParserOptions } from 'youch-core/types'

Expand Down Expand Up @@ -51,6 +52,45 @@ export class Youch {
return errorParser
}

/**
* Defines the request properties as a metadata group
*/
#defineRequestMetadataGroup(request: YouchHTMLOptions['request']) {
if (!request || Object.keys(request).length === 0) {
return
}

this.metadata.group('Request', {
...(request.url
? {
url: {
key: 'URL',
value: request.url,
},
}
: {}),
...(request.method
? {
method: {
key: 'Method',
value: request.method,
},
}
: {}),
...(request.headers
? {
headers: Object.keys(request.headers).map((key) => {
const value = request.headers![key]
return {
key,
value: key === 'cookie' ? { ...cookie.parse(value as string) } : value,
}
}),
}
: {}),
})
}

/**
* Define custom implementation for loading the source code
* of a stack frame.
Expand Down Expand Up @@ -94,6 +134,8 @@ export class Youch {
async toHTML(error: unknown, options?: YouchHTMLOptions) {
options = { ...options }

this.#defineRequestMetadataGroup(options.request)

const parsedError = await this.#createErrorParser({ offset: options.offset }).parse(error)
return this.templates.toHTML({
title: options.title ?? 'An error has occurred',
Expand Down
37 changes: 37 additions & 0 deletions tests/youch.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* file that was distributed with this source code.
*/

import { JSDOM } from 'jsdom'
import { test } from '@japa/runner'
import { Youch } from '../src/youch.js'
import { stripAnsi } from '../src/helpers.js'
Expand Down Expand Up @@ -51,4 +52,40 @@ test.group('Youch', () => {
const output = await new Youch().toHTML(new Error('Something went wrong'))
assert.include(output, 'at Object.executor (tests/youch.spec.ts:51:45)')
})

test('display request URL, method and headers as metadata', async ({ assert }) => {
const html = await new Youch().toHTML(new Error('Something went wrong'), {
request: {
url: '/',
method: 'GET',
headers: {
host: 'localhost:3000',
},
},
})

const { window } = new JSDOM(html)
const sections: { title: string; contents: string }[] = []
window.document.querySelectorAll('.card-subtitle').forEach((node) => {
sections.push({
title: node.textContent!,
contents: node.nextElementSibling!.textContent!.replace(/[\s]+/g, ''),
})
})

assert.deepEqual(sections, [
{
contents: '/',
title: 'url',
},
{
contents: 'GET',
title: 'method',
},
{
contents: `hostlocalhost:3000`,
title: 'headers',
},
])
})
})

0 comments on commit 227391b

Please sign in to comment.