Use toucan.js on Deno
parent
9b9b6d2744
commit
cbcd8fb3da
|
@ -26,7 +26,7 @@ import { Hono } from 'https://deno.land/x/hono/mod.ts'
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
|
|
||||||
app.use('*', sentry())
|
app.use('*', sentry({ dsn: 'https://xxxxxx@xxx.ingest.sentry.io/xxxxxx' }))
|
||||||
app.get('/', (c) => c.text('foo'))
|
app.get('/', (c) => c.text('foo'))
|
||||||
|
|
||||||
serve(app.fetch)
|
serve(app.fetch)
|
||||||
|
|
|
@ -26,7 +26,7 @@ import { Hono } from 'https://deno.land/x/hono/mod.ts'
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
|
|
||||||
app.use('*', sentry())
|
app.use('*', sentry({ dsn: 'https://xxxxxx@xxx.ingest.sentry.io/xxxxxx' }))
|
||||||
app.get('/', (c) => c.text('foo'))
|
app.get('/', (c) => c.text('foo'))
|
||||||
|
|
||||||
serve(app.fetch)
|
serve(app.fetch)
|
||||||
|
|
|
@ -1,17 +1,62 @@
|
||||||
import type { Handler } from 'https://raw.githubusercontent.com/honojs/hono/v2.0.6/deno_dist/mod.ts'
|
import type { Context, 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 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) => {
|
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,
|
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 {
|
try {
|
||||||
await next()
|
await next()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
Sentry.captureException(error)
|
sentry.captureException(error)
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getSentry = (c: Context) => {
|
||||||
|
return c.get('sentry')
|
||||||
|
}
|
||||||
|
|
|
@ -4,11 +4,22 @@ import { assertEquals, Hono } from './deps.ts'
|
||||||
// Test just only minimal patterns.
|
// Test just only minimal patterns.
|
||||||
// Because others are tested well in Cloudflare Workers environment already.
|
// 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 () => {
|
Deno.test('Sentry Middleware', async () => {
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
app.use('/sentry/*', sentry())
|
app.use('/sentry/*', sentry())
|
||||||
app.get('/sentry/foo', (c) => c.text('foo'))
|
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)
|
assertEquals(res.status, 200)
|
||||||
})
|
})
|
||||||
|
|
19
package.json
19
package.json
|
@ -1,11 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "@honojs/hello",
|
"name": "@honojs/sentry",
|
||||||
"version": "0.0.6",
|
"version": "0.0.1",
|
||||||
"description": "An example of third-party middleware for Hono",
|
"description": "Sentry Middleware for Hono",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist/index.js",
|
||||||
|
"dist/index.d.ts"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest",
|
"test": "jest",
|
||||||
|
@ -13,21 +14,19 @@
|
||||||
"test:all": "yarn test && yarn test:deno",
|
"test:all": "yarn test && yarn test:deno",
|
||||||
"denoify": "rimraf deno_dist && denoify",
|
"denoify": "rimraf deno_dist && denoify",
|
||||||
"build": "rimraf dist && tsc",
|
"build": "rimraf dist && tsc",
|
||||||
"prerelease": "yarn denoify && yarn build && yarn test:all",
|
"prerelease": "yarn build && yarn denoify && yarn test:all",
|
||||||
"release": "yarn publish"
|
"release": "yarn publish"
|
||||||
},
|
},
|
||||||
"denoify": {
|
"denoify": {
|
||||||
"port": {
|
"replacer": "dist/replacer.js"
|
||||||
"hono": "honojs/hono"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"private": false,
|
"private": false,
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"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)",
|
"author": "Yusuke Wada <yusuke@kamawada.com> (https://github.com/yusukebe)",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"registry": "https://registry.npmjs.org",
|
"registry": "https://registry.npmjs.org",
|
||||||
|
|
29
src/index.ts
29
src/index.ts
|
@ -1,6 +1,21 @@
|
||||||
import type { Handler } from 'hono'
|
import type { Context, Handler } from 'hono'
|
||||||
import Toucan from 'toucan-js'
|
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 = {
|
export type Options = {
|
||||||
dsn?: string
|
dsn?: string
|
||||||
allowedCookies?: string[] | RegExp
|
allowedCookies?: string[] | RegExp
|
||||||
|
@ -16,12 +31,18 @@ export type Options = {
|
||||||
|
|
||||||
export const sentry = (options?: Options, callback?: (sentry: Toucan) => void): Handler => {
|
export const sentry = (options?: Options, callback?: (sentry: Toucan) => void): Handler => {
|
||||||
return async (c, next) => {
|
return async (c, next) => {
|
||||||
|
let hasExecutionContext = true
|
||||||
|
try {
|
||||||
|
c.executionCtx
|
||||||
|
} catch {
|
||||||
|
hasExecutionContext = false
|
||||||
|
}
|
||||||
const sentry = new Toucan({
|
const sentry = new Toucan({
|
||||||
dsn: c.env.SENTRY_DSN || c.env.NEXT_PUBLIC_SENTRY_DSN,
|
dsn: c.env.SENTRY_DSN || c.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||||
allowedHeaders: ['user-agent'],
|
allowedHeaders: ['user-agent'],
|
||||||
allowedSearchParams: /(.*)/,
|
allowedSearchParams: /(.*)/,
|
||||||
request: c.req,
|
request: c.req,
|
||||||
context: c.executionCtx,
|
context: hasExecutionContext ? c.executionCtx : new MockContext(),
|
||||||
...options,
|
...options,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -35,3 +56,7 @@ export const sentry = (options?: Options, callback?: (sentry: Toucan) => void):
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getSentry = (c: Context) => {
|
||||||
|
return c.get('sentry')
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
})
|
Loading…
Reference in New Issue