add workers-shared package and source-maps for dotcom-worker sentry reports (#4052)

Share the new sentry stuff that plays nice with releases/sourcemaps with
dotcom-worker using a new `worker-shared` package.
This commit is contained in:
alex 2024-07-02 10:10:20 +01:00 committed by GitHub
parent 7ec30e56da
commit 031547749f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 150 additions and 54 deletions

View file

@ -24,11 +24,11 @@
"@tldraw/tlschema": "workspace:*",
"@tldraw/tlsync": "workspace:*",
"@tldraw/utils": "workspace:*",
"@tldraw/worker-shared": "workspace:*",
"itty-router": "^4.0.13",
"nanoid": "4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"toucan-js": "^3.4.0"
"react-dom": "^18.2.0"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240620.0",

View file

@ -1,6 +1,6 @@
import { createSentry } from '@tldraw/worker-shared'
import { DurableObject } from 'cloudflare:workers'
import { Router } from 'itty-router'
import { createSentry } from './sentry'
import { Environment } from './types'
export class BemoDO extends DurableObject<Environment> {

View file

@ -1,21 +0,0 @@
import { Toucan } from 'toucan-js'
import { Environment } from './types'
interface Context {
waitUntil: ExecutionContext['waitUntil']
request?: Request
}
export function createSentry(ctx: Context, env: Environment, request?: Request) {
return new Toucan({
dsn: env.SENTRY_DSN,
release: `${env.WORKER_NAME}.${env.CF_VERSION_METADATA.id}`,
environment: env.WORKER_NAME,
context: ctx,
request,
requestDataOptions: {
allowedHeaders: ['user-agent'],
allowedSearchParams: /(.*)/,
},
})
}

View file

@ -1,9 +1,9 @@
/// <reference no-default-lib="true"/>
/// <reference types="@cloudflare/workers-types" />
import { createSentry } from '@tldraw/worker-shared'
import { WorkerEntrypoint } from 'cloudflare:workers'
import { Router, createCors } from 'itty-router'
import { createSentry } from './sentry'
import { Environment } from './types'
export { BemoDO } from './BemoDO'

View file

@ -10,6 +10,9 @@
{
"path": "../../packages/dotcom-shared"
},
{
"path": "../../packages/worker-shared"
},
{
"path": "../../packages/store"
},

View file

@ -40,7 +40,7 @@ bucket_name = 'uploads-preview'
# in production, we write to the main bucket
[[env.production.r2_buckets]]
binding = "ROOMS"
binding = "UPLOADS"
bucket_name = "uploads"
#################### Analytics engine ####################

View file

@ -27,11 +27,11 @@
"@tldraw/tlsync": "workspace:*",
"@tldraw/utils": "workspace:*",
"@tldraw/validate": "workspace:*",
"@tldraw/worker-shared": "workspace:*",
"itty-router": "^4.0.13",
"nanoid": "4.0.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"toucan-js": "^3.4.0"
"react-dom": "^18.2.0"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20240620.0",

View file

@ -17,8 +17,8 @@ import {
type PersistedRoomSnapshotForSupabase,
} from '@tldraw/tlsync'
import { assert, assertExists, exhaustiveSwitchError } from '@tldraw/utils'
import { createSentry } from '@tldraw/worker-shared'
import { IRequest, Router } from 'itty-router'
import { Toucan } from 'toucan-js'
import { AlarmScheduler } from './AlarmScheduler'
import { PERSIST_INTERVAL_MS } from './config'
import { getR2KeyForRoom } from './r2'
@ -215,14 +215,7 @@ export class TLDrawDurableObject {
// Handle a request to the Durable Object.
async fetch(req: IRequest) {
const sentry = new Toucan({
dsn: this.sentryDSN,
request: req,
requestDataOptions: {
allowedHeaders: ['user-agent'],
allowedSearchParams: /(.*)/,
},
})
const sentry = createSentry(this.state, this.env, req)
try {
return await this.router.handle(req)

View file

@ -25,6 +25,8 @@ export interface Environment {
SLUG_TO_READONLY_SLUG: KVNamespace
READONLY_SLUG_TO_SLUG: KVNamespace
CF_VERSION_METADATA: WorkerVersionMetadata
// env vars
SUPABASE_URL: string | undefined
SUPABASE_KEY: string | undefined

View file

@ -7,8 +7,8 @@ import {
ROOM_PREFIX,
} from '@tldraw/dotcom-shared'
import { T } from '@tldraw/validate'
import { createSentry } from '@tldraw/worker-shared'
import { Router, createCors, json } from 'itty-router'
import { Toucan } from 'toucan-js'
import { createRoom } from './routes/createRoom'
import { createRoomSnapshot } from './routes/createRoomSnapshot'
import { forwardRoomRequest } from './routes/forwardRoomRequest'
@ -55,15 +55,7 @@ const router = Router()
const Worker = {
fetch(request: Request, env: Environment, context: ExecutionContext) {
const sentry = new Toucan({
dsn: env.SENTRY_DSN,
context, // Includes 'waitUntil', which is essential for Sentry logs to be delivered. Modules workers do not include 'request' in context -- you'll need to set it separately.
request, // request is not included in 'context', so we set it here.
requestDataOptions: {
allowedHeaders: ['user-agent'],
allowedSearchParams: /(.*)/,
},
})
const sentry = createSentry(context, env, request)
return router
.handle(request, env, context)

View file

@ -10,6 +10,9 @@
{
"path": "../../packages/dotcom-shared"
},
{
"path": "../../packages/worker-shared"
},
{
"path": "../../packages/store"
},

View file

@ -44,9 +44,7 @@ name = "TLDR_DOC"
class_name = "TLDrawDurableObject"
[durable_objects]
bindings = [
{ name = "TLDR_DOC", class_name = "TLDrawDurableObject" },
]
bindings = [{ name = "TLDR_DOC", class_name = "TLDrawDurableObject" }]
[[env.preview.durable_objects.bindings]]
name = "TLDR_DOC"
@ -186,3 +184,19 @@ id = "2fb5fc7f7ca54a5a9dfae1b07a30a778"
[[env.production.kv_namespaces]]
binding = "READONLY_SLUG_TO_SLUG"
id = "96be6637b281412ab35b2544539d78e8"
#################### Version metadata ####################
[version_metadata]
binding = "CF_VERSION_METADATA"
[env.dev.version_metadata]
binding = "CF_VERSION_METADATA"
[env.preview.version_metadata]
binding = "CF_VERSION_METADATA"
[env.staging.version_metadata]
binding = "CF_VERSION_METADATA"
[env.production.version_metadata]
binding = "CF_VERSION_METADATA"

View file

@ -0,0 +1,26 @@
{
"name": "@tldraw/worker-shared",
"version": "2.0.0",
"private": true,
"/* NOTE */": "These `main` and `types` fields are rewritten by the build script. They are not the actual values we publish",
"main": "./src/index.ts",
"types": "./.tsbuild/index.d.ts",
"/* GOTCHA */": "files will include ./dist and index.d.ts by default, add any others you want to include in here",
"files": [],
"dependencies": {
"@cloudflare/workers-types": "^4.20240620.0",
"@tldraw/utils": "workspace:*",
"lazyrepo": "0.0.0-alpha.27",
"toucan-js": "^3.4.0",
"typescript": "^5.3.3"
},
"scripts": {
"test-ci": "lazy inherit",
"test": "yarn run -T jest",
"lint": "yarn run -T tsx ../../scripts/lint.ts"
},
"jest": {
"preset": "config/jest/node",
"testEnvironment": "jsdom"
}
}

View file

@ -0,0 +1,13 @@
import { getOwnProperty } from '@tldraw/utils'
export function requiredEnv<T extends object>(
env: T,
keys: { [K in keyof T]: true }
): { [K in keyof T]-?: NonNullable<T[K]> } {
for (const key of Object.keys(keys)) {
if (getOwnProperty(env, key) === undefined) {
throw new Error(`Missing required env var: ${key}`)
}
}
return env as any
}

View file

@ -0,0 +1,3 @@
it('works', () => {
// we need a test for jest to pass.
})

View file

@ -0,0 +1,4 @@
/// <reference no-default-lib="true"/>
/// <reference types="@cloudflare/workers-types" />
export { createSentry } from './sentry'

View file

@ -0,0 +1,34 @@
import { WorkerVersionMetadata } from '@cloudflare/workers-types'
import { Toucan } from 'toucan-js'
import { requiredEnv } from './env'
interface Context {
waitUntil: ExecutionContext['waitUntil']
request?: Request
}
interface SentryEnvironment {
readonly SENTRY_DSN: string | undefined
readonly WORKER_NAME: string | undefined
readonly CF_VERSION_METADATA: WorkerVersionMetadata
}
export function createSentry(ctx: Context, env: SentryEnvironment, request?: Request) {
const { SENTRY_DSN, WORKER_NAME, CF_VERSION_METADATA } = requiredEnv(env, {
SENTRY_DSN: true,
WORKER_NAME: true,
CF_VERSION_METADATA: true,
})
return new Toucan({
dsn: SENTRY_DSN,
release: `${WORKER_NAME}.${CF_VERSION_METADATA.id}`,
environment: WORKER_NAME,
context: ctx,
request,
requestDataOptions: {
allowedHeaders: ['user-agent'],
allowedSearchParams: /(.*)/,
},
})
}

View file

@ -0,0 +1,14 @@
{
"extends": "../../config/tsconfig.base.json",
"include": ["src"],
"exclude": ["node_modules", ".tsbuild*"],
"compilerOptions": {
"outDir": "./.tsbuild",
"rootDir": "src"
},
"references": [
{
"path": "../utils"
}
]
}

View file

@ -196,6 +196,10 @@ async function deployTlsyncWorker({ dryRun }: { dryRun: boolean }) {
APP_ORIGIN: env.APP_ORIGIN,
WORKER_NAME: workerId,
},
sentry: {
project: 'tldraw-sync',
authToken: env.SENTRY_AUTH_TOKEN,
},
})
}

View file

@ -6000,13 +6000,13 @@ __metadata:
"@tldraw/tlschema": "workspace:*"
"@tldraw/tlsync": "workspace:*"
"@tldraw/utils": "workspace:*"
"@tldraw/worker-shared": "workspace:*"
esbuild: "npm:^0.21.5"
itty-router: "npm:^4.0.13"
lazyrepo: "npm:0.0.0-alpha.27"
nanoid: "npm:4.0.2"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
toucan-js: "npm:^3.4.0"
typescript: "npm:^5.3.3"
wrangler: "npm:3.62.0"
languageName: unknown
@ -6089,13 +6089,13 @@ __metadata:
"@tldraw/tlsync": "workspace:*"
"@tldraw/utils": "workspace:*"
"@tldraw/validate": "workspace:*"
"@tldraw/worker-shared": "workspace:*"
esbuild: "npm:^0.21.5"
itty-router: "npm:^4.0.13"
lazyrepo: "npm:0.0.0-alpha.27"
nanoid: "npm:4.0.2"
react: "npm:^18.2.0"
react-dom: "npm:^18.2.0"
toucan-js: "npm:^3.4.0"
typescript: "npm:^5.3.3"
wrangler: "npm:3.62.0"
languageName: unknown
@ -6349,6 +6349,18 @@ __metadata:
languageName: unknown
linkType: soft
"@tldraw/worker-shared@workspace:*, @tldraw/worker-shared@workspace:packages/worker-shared":
version: 0.0.0-use.local
resolution: "@tldraw/worker-shared@workspace:packages/worker-shared"
dependencies:
"@cloudflare/workers-types": "npm:^4.20240620.0"
"@tldraw/utils": "workspace:*"
lazyrepo: "npm:0.0.0-alpha.27"
toucan-js: "npm:^3.4.0"
typescript: "npm:^5.3.3"
languageName: unknown
linkType: soft
"@tootallnate/once@npm:1":
version: 1.1.2
resolution: "@tootallnate/once@npm:1.1.2"