Use toucan.js on Deno

pull/31/head
Yusuke Wada 2022-08-12 12:41:03 +09:00
parent 9b9b6d2744
commit cbcd8fb3da
7 changed files with 111 additions and 22 deletions

View File

@ -26,7 +26,7 @@ import { Hono } from 'https://deno.land/x/hono/mod.ts'
const app = new Hono()
app.use('*', sentry())
app.use('*', sentry({ dsn: 'https://xxxxxx@xxx.ingest.sentry.io/xxxxxx' }))
app.get('/', (c) => c.text('foo'))
serve(app.fetch)

View File

@ -26,7 +26,7 @@ import { Hono } from 'https://deno.land/x/hono/mod.ts'
const app = new Hono()
app.use('*', sentry())
app.use('*', sentry({ dsn: 'https://xxxxxx@xxx.ingest.sentry.io/xxxxxx' }))
app.get('/', (c) => c.text('foo'))
serve(app.fetch)

View File

@ -1,17 +1,62 @@
import type { Handler } from 'https://raw.githubusercontent.com/honojs/hono/v2.0.6/deno_dist/mod.ts'
import * as Sentry from 'https://deno.land/x/sentry_deno/main.ts'
import type { Context, Handler } from 'https://raw.githubusercontent.com/honojs/hono/v2.0.6/deno_dist/mod.ts'
import Toucan from "https://cdn.skypack.dev/toucan-js@2.6.1"
export const sentry = (): Handler => {
declare module 'https://raw.githubusercontent.com/honojs/hono/v2.0.6/deno_dist/mod.ts' {
interface ContextVariableMap {
sentry: Toucan
}
}
class MockContext implements ExecutionContext {
passThroughOnException(): void {
throw new Error('Method not implemented.')
}
async waitUntil(promise: Promise<any>): Promise<void> {
await promise
}
}
export type Options = {
dsn?: string
allowedCookies?: string[] | RegExp
allowedHeaders?: string[] | RegExp
allowedSearchParams?: string[] | RegExp
attachStacktrace?: boolean
debug?: boolean
environment?: string
maxBreadcrumbs?: number
pkg?: Record<string, any>
release?: string
}
export const sentry = (options?: Options, callback?: (sentry: Toucan) => void): Handler => {
return async (c, next) => {
Sentry.init({
let hasExecutionContext = true
try {
c.executionCtx
} catch {
hasExecutionContext = false
}
const sentry = new Toucan({
dsn: c.env.SENTRY_DSN || c.env.NEXT_PUBLIC_SENTRY_DSN,
tracesSampleRate: 1.0,
allowedHeaders: ['user-agent'],
allowedSearchParams: /(.*)/,
request: c.req,
context: hasExecutionContext ? c.executionCtx : new MockContext(),
...options,
})
if (callback) callback(sentry)
try {
await next()
} catch (error) {
Sentry.captureException(error)
sentry.captureException(error)
throw error
}
}
}
export const getSentry = (c: Context) => {
return c.get('sentry')
}

View File

@ -4,11 +4,22 @@ import { assertEquals, Hono } from './deps.ts'
// Test just only minimal patterns.
// Because others are tested well in Cloudflare Workers environment already.
// Mock
class Context implements ExecutionContext {
passThroughOnException(): void {
throw new Error('Method not implemented.')
}
async waitUntil(promise: Promise<any>): Promise<void> {
await promise
}
}
Deno.test('Sentry Middleware', async () => {
const app = new Hono()
app.use('/sentry/*', sentry())
app.get('/sentry/foo', (c) => c.text('foo'))
const res = await app.request('http://localhost/sentry/foo')
const req = new Request('http://localhost/sentry/foo')
const res = await app.fetch(req, {}, new Context())
assertEquals(res.status, 200)
})

View File

@ -1,11 +1,12 @@
{
"name": "@honojs/hello",
"version": "0.0.6",
"description": "An example of third-party middleware for Hono",
"name": "@honojs/sentry",
"version": "0.0.1",
"description": "Sentry Middleware for Hono",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist"
"dist/index.js",
"dist/index.d.ts"
],
"scripts": {
"test": "jest",
@ -13,21 +14,19 @@
"test:all": "yarn test && yarn test:deno",
"denoify": "rimraf deno_dist && denoify",
"build": "rimraf dist && tsc",
"prerelease": "yarn denoify && yarn build && yarn test:all",
"prerelease": "yarn build && yarn denoify && yarn test:all",
"release": "yarn publish"
},
"denoify": {
"port": {
"hono": "honojs/hono"
}
"replacer": "dist/replacer.js"
},
"license": "MIT",
"private": false,
"repository": {
"type": "git",
"url": "https://github.com/honojs/middleware-template.git"
"url": "https://github.com/honojs/sentry.git"
},
"homepage": "https://github.com/honojs/middleware-template",
"homepage": "https://github.com/honojs/sentry",
"author": "Yusuke Wada <yusuke@kamawada.com> (https://github.com/yusukebe)",
"publishConfig": {
"registry": "https://registry.npmjs.org",

View File

@ -1,6 +1,21 @@
import type { Handler } from 'hono'
import type { Context, Handler } from 'hono'
import Toucan from 'toucan-js'
declare module 'hono' {
interface ContextVariableMap {
sentry: Toucan
}
}
class MockContext implements ExecutionContext {
passThroughOnException(): void {
throw new Error('Method not implemented.')
}
async waitUntil(promise: Promise<any>): Promise<void> {
await promise
}
}
export type Options = {
dsn?: string
allowedCookies?: string[] | RegExp
@ -16,12 +31,18 @@ export type Options = {
export const sentry = (options?: Options, callback?: (sentry: Toucan) => void): Handler => {
return async (c, next) => {
let hasExecutionContext = true
try {
c.executionCtx
} catch {
hasExecutionContext = false
}
const sentry = new Toucan({
dsn: c.env.SENTRY_DSN || c.env.NEXT_PUBLIC_SENTRY_DSN,
allowedHeaders: ['user-agent'],
allowedSearchParams: /(.*)/,
request: c.req,
context: c.executionCtx,
context: hasExecutionContext ? c.executionCtx : new MockContext(),
...options,
})
@ -35,3 +56,7 @@ export const sentry = (options?: Options, callback?: (sentry: Toucan) => void):
}
}
}
export const getSentry = (c: Context) => {
return c.get('sentry')
}

9
src/replacer.ts 100644
View File

@ -0,0 +1,9 @@
// @denoify-ignore
import { makeThisModuleAnExecutableReplacer } from 'denoify'
makeThisModuleAnExecutableReplacer(async ({ parsedImportExportStatement, version }) => {
if (parsedImportExportStatement.parsedArgument.nodeModuleName === 'toucan-js') {
return `import Toucan from "https://cdn.skypack.dev/toucan-js@${version}"`
}
return undefined
})