better-ship 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{application-D5tNECiC.js → application-C_xN_1pa.js} +2 -2
- package/dist/{application-D5tNECiC.js.map → application-C_xN_1pa.js.map} +1 -1
- package/dist/application.js +1 -1
- package/dist/better-auth/apple-client-secret.js +1 -1
- package/dist/better-auth/auth-client.js +1 -1
- package/dist/better-auth/rate-limit.js +1 -1
- package/dist/better-auth.js +1 -1
- package/dist/billing/client.js +1 -1
- package/dist/cloudflare/auth-secondary-storage.js +1 -1
- package/dist/cloudflare/durable-object-client.js +1 -1
- package/dist/cloudflare/durable-object-errors.js +1 -1
- package/dist/cloudflare/kv-client.js +1 -1
- package/dist/cloudflare/kv-errors.js +1 -1
- package/dist/cloudflare/queue.js +2 -2
- package/dist/cloudflare/r2-client.js +1 -1
- package/dist/cloudflare/r2-errors.js +1 -1
- package/dist/cloudflare/scheduled.js +1 -1
- package/dist/cloudflare.js +3 -3
- package/dist/{core-BIas38R0.js → core-BiN6S_cm.js} +32 -1
- package/dist/{core-BIas38R0.js.map → core-BiN6S_cm.js.map} +1 -1
- package/dist/core.js +1 -1
- package/dist/http.js +2 -2
- package/dist/index-DUFS0SJ_.d.ts.map +1 -1
- package/dist/{outbox-repository-options-D0R8z1yS.js → outbox-repository-options-Ww0kAgbq.js} +2 -2
- package/dist/{outbox-repository-options-D0R8z1yS.js.map → outbox-repository-options-Ww0kAgbq.js.map} +1 -1
- package/dist/postgres.js +3 -3
- package/dist/posthog/server.js +1 -1
- package/dist/resend.js +2 -2
- package/dist/sentry/client.d.ts +1 -1
- package/dist/sentry/client.d.ts.map +1 -1
- package/dist/sentry/client.js +4 -15
- package/dist/sentry/client.js.map +1 -1
- package/dist/sentry/server.d.ts +1 -1
- package/dist/sentry/server.d.ts.map +1 -1
- package/dist/sentry/server.js +4 -17
- package/dist/sentry/server.js.map +1 -1
- package/dist/sentry/tanstack.d.ts +11 -0
- package/dist/sentry/tanstack.d.ts.map +1 -0
- package/dist/sentry/tanstack.js +20 -0
- package/dist/sentry/tanstack.js.map +1 -0
- package/dist/sentry/vite.d.ts.map +1 -1
- package/dist/stripe/payment-service.error.js +1 -1
- package/dist/stripe/payment-webhook.js +1 -1
- package/dist/tanstack.js +1 -1
- package/dist/{webhook-verification-CEUARY9X.js → webhook-verification-lWso44-q.js} +2 -2
- package/dist/{webhook-verification-CEUARY9X.js.map → webhook-verification-lWso44-q.js.map} +1 -1
- package/package.json +33 -32
- package/src/core/transport-classification.ts +38 -0
- package/src/infrastructure/sentry/client.ts +5 -9
- package/src/infrastructure/sentry/server.ts +6 -13
- package/src/infrastructure/sentry/tanstack.ts +18 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../../src/infrastructure/sentry/server.ts"],"sourcesContent":["import * as Sentry from '@sentry/cloudflare'\n\nimport type { ObservabilityScope, ObservabilityService, ObservabilityUser } from '@/application'\nimport type { LoggerErrorHook } from '@/core'\n\n/** Application-owned deployment settings with the full Cloudflare SDK option contract. */\nexport type SentryServerOptions = Sentry.CloudflareOptions & {\n readonly dsn: string\n readonly environment: string\n readonly enabled: boolean\n}\n\n/**
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../../src/infrastructure/sentry/server.ts"],"sourcesContent":["import * as Sentry from '@sentry/cloudflare'\n\nimport type { ObservabilityScope, ObservabilityService, ObservabilityUser } from '@/application'\nimport type { LoggerErrorHook } from '@/core'\n\n/** Application-owned deployment settings with the full Cloudflare SDK option contract. */\nexport type SentryServerOptions = Sentry.CloudflareOptions & {\n readonly dsn: string\n readonly environment: string\n readonly enabled: boolean\n}\n\n/** Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults. */\nexport function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions {\n return options\n}\n\n/** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */\nexport function createSentryLoggerErrorHook(): LoggerErrorHook {\n return ({ error, userId, attributes }) => {\n if (userId !== undefined) {\n Sentry.captureException(error, { extra: attributes, user: { id: userId } })\n return\n }\n Sentry.captureException(error, { extra: attributes })\n }\n}\n\n/** Identity and scope adapter from Typist and Porte; call inside Sentry's instrumented runtime. */\nexport class SentryObservabilityService implements ObservabilityService {\n /** Attribute subsequent reports in the current isolation scope. */\n setUser(user: ObservabilityUser): void {\n Sentry.setUser({ id: user.id })\n }\n\n /** Await work in a separate scope for each queue message or concurrent operation. */\n async withIsolationScope<T>(callback: (scope: ObservabilityScope) => T | Promise<T>): Promise<T> {\n return await Sentry.withIsolationScope((scope) =>\n callback({ setUser: (user) => scope.setUser({ id: user.id }) }),\n )\n }\n}\n\nexport { Sentry }\n"],"mappings":";;;;AAaA,SAAgB,oBAAoB,SAAwD;CAC1F,OAAO;AACT;;AAGA,SAAgB,8BAA+C;CAC7D,QAAQ,EAAE,OAAO,QAAQ,iBAAiB;EACxC,IAAI,WAAW,QAAW;GACxB,OAAO,iBAAiB,OAAO;IAAE,OAAO;IAAY,MAAM,EAAE,IAAI,OAAO;GAAE,CAAC;GAC1E;EACF;EACA,OAAO,iBAAiB,OAAO,EAAE,OAAO,WAAW,CAAC;CACtD;AACF;;AAGA,IAAa,6BAAb,MAAwE;;CAEtE,QAAQ,MAA+B;EACrC,OAAO,QAAQ,EAAE,IAAI,KAAK,GAAG,CAAC;CAChC;;CAGA,MAAM,mBAAsB,UAAqE;EAC/F,OAAO,MAAM,OAAO,oBAAoB,UACtC,SAAS,EAAE,UAAU,SAAS,MAAM,QAAQ,EAAE,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC,CAChE;CACF;AACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { ot as ErrorTable } from "../index-DUFS0SJ_.js";
|
|
2
|
+
import { ErrorEvent, EventHint } from "@sentry/cloudflare";
|
|
3
|
+
//#region src/infrastructure/sentry/tanstack.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Create a Sentry beforeSend filter from the application's error table, as Suparelay does.
|
|
6
|
+
* Drop known payloads and Router control flow after the error middleware reports the original failure.
|
|
7
|
+
* Preserve real errors and unknown failures. Register the logger error hook before using this filter.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createSentryErrorFilter<Entries>(errors: ErrorTable<Entries>): (event: ErrorEvent, hint: EventHint) => ErrorEvent | null;
|
|
10
|
+
//#endregion
|
|
11
|
+
//# sourceMappingURL=tanstack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tanstack.d.ts","names":[],"sources":["../../src/infrastructure/sentry/tanstack.ts"],"mappings":";;;;;;;;wBAUgB,wBAAwB,SAAS,QAAQ,WAAW,YAAQ,OACxC,YAAU,MAAQ,cAAY"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { isNotFound, isRedirect } from "@tanstack/react-router";
|
|
2
|
+
|
|
3
|
+
//#region src/infrastructure/sentry/tanstack.ts
|
|
4
|
+
/**
|
|
5
|
+
* Create a Sentry beforeSend filter from the application's error table, as Suparelay does.
|
|
6
|
+
* Drop known payloads and Router control flow after the error middleware reports the original failure.
|
|
7
|
+
* Preserve real errors and unknown failures. Register the logger error hook before using this filter.
|
|
8
|
+
*/
|
|
9
|
+
function createSentryErrorFilter(errors) {
|
|
10
|
+
return function beforeSend(event, hint) {
|
|
11
|
+
const thrown = hint.originalException;
|
|
12
|
+
if (thrown instanceof Error) return event;
|
|
13
|
+
if (isRedirect(thrown) || isNotFound(thrown)) return null;
|
|
14
|
+
return errors.parse(thrown) === null ? event : null;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
export { createSentryErrorFilter };
|
|
20
|
+
//# sourceMappingURL=tanstack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tanstack.js","names":[],"sources":["../../src/infrastructure/sentry/tanstack.ts"],"sourcesContent":["import type { ErrorEvent, EventHint } from '@sentry/cloudflare'\nimport { isNotFound, isRedirect } from '@tanstack/react-router'\n\nimport type { ErrorTable } from '@/core'\n\n/**\n * Create a Sentry beforeSend filter from the application's error table, as Suparelay does.\n * Drop known payloads and Router control flow after the error middleware reports the original failure.\n * Preserve real errors and unknown failures. Register the logger error hook before using this filter.\n */\nexport function createSentryErrorFilter<Entries>(errors: ErrorTable<Entries>) {\n return function beforeSend(event: ErrorEvent, hint: EventHint): ErrorEvent | null {\n const thrown = hint.originalException\n if (thrown instanceof Error) return event\n if (isRedirect(thrown) || isNotFound(thrown)) return null\n return errors.parse(thrown) === null ? event : null\n }\n}\n"],"mappings":";;;;;;;;AAUA,SAAgB,wBAAiC,QAA6B;CAC5E,OAAO,SAAS,WAAW,OAAmB,MAAoC;EAChF,MAAM,SAAS,KAAK;EACpB,IAAI,kBAAkB,OAAO,OAAO;EACpC,IAAI,WAAW,MAAM,KAAK,WAAW,MAAM,GAAG,OAAO;EACrD,OAAO,OAAO,MAAM,MAAM,MAAM,OAAO,QAAQ;CACjD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vite.d.ts","names":[],"sources":["../../src/infrastructure/sentry/vite.ts"],"mappings":";;;YAMY,oBAAoB;;;;;wBAMhB,oBACd,UAAS,oBACR,
|
|
1
|
+
{"version":3,"file":"vite.d.ts","names":["sentryPlugin"],"sources":["../../src/infrastructure/sentry/vite.ts"],"mappings":";;;YAMY,oBAAoB;;;;;wBAMhB,oBACd,UAAS,oBACR,kBAAkBA"}
|
package/dist/tanstack.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { H as StaleSessionError, R as ValidationError, W as AuthenticationError, nt as AppError, r as createLogger } from "./core-
|
|
1
|
+
import { H as StaleSessionError, R as ValidationError, W as AuthenticationError, nt as AppError, r as createLogger } from "./core-BiN6S_cm.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { getReasonPhrase } from "http-status-codes";
|
|
4
4
|
import { isNotFound, isRedirect, redirect } from "@tanstack/react-router";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { nt as AppError } from "./core-
|
|
1
|
+
import { nt as AppError } from "./core-BiN6S_cm.js";
|
|
2
2
|
|
|
3
3
|
//#region src/infrastructure/http/webhook-verification.ts
|
|
4
4
|
/** A webhook cannot be trusted or parsed. The HTTP handler logs the rejection once. */
|
|
@@ -15,4 +15,4 @@ var WebhookVerificationError = class extends AppError {
|
|
|
15
15
|
|
|
16
16
|
//#endregion
|
|
17
17
|
export { WebhookVerificationError as t };
|
|
18
|
-
//# sourceMappingURL=webhook-verification-
|
|
18
|
+
//# sourceMappingURL=webhook-verification-lWso44-q.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webhook-verification-
|
|
1
|
+
{"version":3,"file":"webhook-verification-lWso44-q.js","names":[],"sources":["../src/infrastructure/http/webhook-verification.ts"],"sourcesContent":["import { AppError } from '@/core'\n\n/** A webhook cannot be trusted or parsed. The HTTP handler logs the rejection once. */\nexport class WebhookVerificationError extends AppError {\n readonly _tag = 'WebhookVerificationError'\n readonly classification = 'terminal'\n\n /** Keep secrets and payload details out of the public message. */\n constructor(\n readonly reason: 'missing_headers' | 'invalid_signature' | 'invalid_payload',\n cause?: unknown,\n ) {\n super(`Webhook rejected: ${reason}`, { cause })\n }\n}\n\n/** The provider owns signature verification, timestamp tolerance, and payload parsing. */\nexport interface WebhookVerifier<Verified> {\n /**\n * Verify the exact body before parsing it. Return null for an ignored webhook.\n * @throws WebhookVerificationError when the request cannot be trusted or parsed.\n */\n verify(body: string, headers: Headers): Promise<Verified | null>\n}\n"],"mappings":";;;;AAGA,IAAa,2BAAb,cAA8C,SAAS;CAM1C;CALX,AAAS,OAAO;CAChB,AAAS,iBAAiB;;CAG1B,YACE,AAAS,QACT,OACA;EACA,MAAM,qBAAqB,UAAU,EAAE,MAAM,CAAC;EAHrC;CAIX;AACF"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://www.schemastore.org/package.json",
|
|
3
3
|
"name": "better-ship",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.23.0",
|
|
5
5
|
"description": "Foundation for applications on Cloudflare Workers: logger, errors, message bus, infrastructure adapters, and UI primitives",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"./resend": "./dist/resend.js",
|
|
44
44
|
"./sentry/client": "./dist/sentry/client.js",
|
|
45
45
|
"./sentry/server": "./dist/sentry/server.js",
|
|
46
|
+
"./sentry/tanstack": "./dist/sentry/tanstack.js",
|
|
46
47
|
"./sentry/vite": "./dist/sentry/vite.js",
|
|
47
48
|
"./stripe/client": "./dist/stripe/client.js",
|
|
48
49
|
"./stripe/event-map": "./dist/stripe/event-map.js",
|
|
@@ -65,52 +66,52 @@
|
|
|
65
66
|
"better-result": "^3.0.1",
|
|
66
67
|
"http-status-codes": "^2.3.0",
|
|
67
68
|
"uuid": "^14.0.2",
|
|
68
|
-
"zod": "^4.6.
|
|
69
|
+
"zod": "^4.6.5"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
|
-
"@better-auth/stripe": "^1.7.
|
|
72
|
-
"@cloudflare/vitest-plugin": "^1.
|
|
73
|
-
"@cloudflare/workers-types": "5.
|
|
74
|
-
"@posthog/react": "^1.
|
|
75
|
-
"@sentry/cloudflare": "^
|
|
76
|
-
"@sentry/tanstackstart-react": "^
|
|
77
|
-
"@tanstack/react-router": "^1.170.
|
|
78
|
-
"@tanstack/react-start": "^1.168.
|
|
79
|
-
"@tanstack/start-client-core": "^1.170.
|
|
80
|
-
"@tanstack/start-storage-context": "^1.167.
|
|
81
|
-
"@types/node": "^26.
|
|
72
|
+
"@better-auth/stripe": "^1.7.6",
|
|
73
|
+
"@cloudflare/vitest-plugin": "^1.2.8",
|
|
74
|
+
"@cloudflare/workers-types": "5.20260926.1",
|
|
75
|
+
"@posthog/react": "^1.11.2",
|
|
76
|
+
"@sentry/cloudflare": "^11.0.0",
|
|
77
|
+
"@sentry/tanstackstart-react": "^11.0.0",
|
|
78
|
+
"@tanstack/react-router": "^1.170.39",
|
|
79
|
+
"@tanstack/react-start": "^1.168.58",
|
|
80
|
+
"@tanstack/start-client-core": "^1.170.32",
|
|
81
|
+
"@tanstack/start-storage-context": "^1.167.34",
|
|
82
|
+
"@types/node": "^26.6.3",
|
|
82
83
|
"@types/react": "^19.3.0",
|
|
83
84
|
"@vitest/browser-playwright": "^4.1.11",
|
|
84
|
-
"auth": "^1.7.
|
|
85
|
-
"better-auth": "^1.7.
|
|
86
|
-
"drizzle-kit": "^0.31.
|
|
87
|
-
"drizzle-orm": "^0.45.
|
|
85
|
+
"auth": "^1.7.6",
|
|
86
|
+
"better-auth": "^1.7.6",
|
|
87
|
+
"drizzle-kit": "^0.31.11",
|
|
88
|
+
"drizzle-orm": "^0.45.3",
|
|
88
89
|
"playwright": "^1.63.0",
|
|
89
90
|
"postgres": "^3.4.9",
|
|
90
|
-
"posthog-js": "^1.
|
|
91
|
-
"posthog-node": "^5.
|
|
91
|
+
"posthog-js": "^1.434.14",
|
|
92
|
+
"posthog-node": "^5.54.1",
|
|
92
93
|
"react": "^19.3.0",
|
|
93
|
-
"resend": "^6.
|
|
94
|
+
"resend": "^6.30.0",
|
|
94
95
|
"standardwebhooks": "^1.1.1",
|
|
95
96
|
"stripe": "^22.6.2",
|
|
96
97
|
"tsdown": "^0.23.0",
|
|
97
98
|
"typescript": "^7.0.2",
|
|
98
99
|
"vitest": "^4.1.11",
|
|
99
|
-
"wrangler": "^4.
|
|
100
|
+
"wrangler": "^4.141.0"
|
|
100
101
|
},
|
|
101
102
|
"peerDependencies": {
|
|
102
|
-
"@better-auth/stripe": "^1.7.
|
|
103
|
-
"@posthog/react": "^1.
|
|
104
|
-
"@sentry/cloudflare": "^
|
|
105
|
-
"@sentry/tanstackstart-react": "^
|
|
106
|
-
"@tanstack/react-router": "^1.170.
|
|
107
|
-
"@tanstack/react-start": "^1.168.
|
|
108
|
-
"better-auth": "^1.7.
|
|
109
|
-
"drizzle-orm": "^0.45.
|
|
110
|
-
"posthog-js": "^1.
|
|
111
|
-
"posthog-node": "^5.
|
|
103
|
+
"@better-auth/stripe": "^1.7.6",
|
|
104
|
+
"@posthog/react": "^1.11.2",
|
|
105
|
+
"@sentry/cloudflare": "^11.0.0",
|
|
106
|
+
"@sentry/tanstackstart-react": "^11.0.0",
|
|
107
|
+
"@tanstack/react-router": "^1.170.39",
|
|
108
|
+
"@tanstack/react-start": "^1.168.58",
|
|
109
|
+
"better-auth": "^1.7.6",
|
|
110
|
+
"drizzle-orm": "^0.45.3",
|
|
111
|
+
"posthog-js": "^1.434.14",
|
|
112
|
+
"posthog-node": "^5.54.1",
|
|
112
113
|
"react": "^19.3.0",
|
|
113
|
-
"resend": "^6.
|
|
114
|
+
"resend": "^6.30.0",
|
|
114
115
|
"stripe": "^22.6.2"
|
|
115
116
|
},
|
|
116
117
|
"peerDependenciesMeta": {
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact `TypeError` messages that `fetch` rejects with when the network fails.
|
|
3
|
+
*
|
|
4
|
+
* Ported from is-network-error 1.3.2. The Safari stack check is dropped, so a failed `import()` in Safari also matches.
|
|
5
|
+
* Source: https://github.com/sindresorhus/is-network-error/blob/57de24d4095c39ff19b75f35be846d4e47f1b687/index.js
|
|
6
|
+
*/
|
|
7
|
+
const FETCH_NETWORK_ERROR_MESSAGES = new Set([
|
|
8
|
+
'Failed to fetch', // Chrome
|
|
9
|
+
'Load failed', // Safari
|
|
10
|
+
'network error', // Chrome
|
|
11
|
+
'NetworkError when attempting to fetch resource.', // Firefox
|
|
12
|
+
'The Internet connection appears to be offline.', // Safari 16
|
|
13
|
+
'Network request failed', // cross-fetch
|
|
14
|
+
'fetch failed', // undici (Node.js)
|
|
15
|
+
'terminated', // undici (Node.js)
|
|
16
|
+
' A network error occurred.', // Bun
|
|
17
|
+
'Network connection lost', // Cloudflare Workers
|
|
18
|
+
])
|
|
19
|
+
|
|
20
|
+
/** Messages that Chrome and Safari may end with the request hostname, such as `Failed to fetch (example.com)`. */
|
|
21
|
+
const FETCH_NETWORK_ERROR_HOSTNAME_PREFIXES = ['Failed to fetch (', 'Load failed (']
|
|
22
|
+
|
|
23
|
+
/** Deno starts its network error message with this text. */
|
|
24
|
+
const DENO_NETWORK_ERROR_PREFIX = 'error sending request for url'
|
|
25
|
+
|
|
1
26
|
/**
|
|
2
27
|
* Messages that the platform-neutral transports raise for a failure that may pass.
|
|
3
28
|
*
|
|
@@ -20,9 +45,22 @@ export function isAbortError(cause: unknown): boolean {
|
|
|
20
45
|
return cause instanceof Error && cause.name === 'AbortError'
|
|
21
46
|
}
|
|
22
47
|
|
|
48
|
+
/** Match the `TypeError` that browser and server `fetch` raise when the network fails. */
|
|
49
|
+
function isFetchNetworkError(cause: Error): boolean {
|
|
50
|
+
if (cause.name !== 'TypeError') return false
|
|
51
|
+
const { message } = cause
|
|
52
|
+
if (FETCH_NETWORK_ERROR_MESSAGES.has(message)) return true
|
|
53
|
+
if (message.startsWith(DENO_NETWORK_ERROR_PREFIX)) return true
|
|
54
|
+
return (
|
|
55
|
+
message.endsWith(')') &&
|
|
56
|
+
FETCH_NETWORK_ERROR_HOSTNAME_PREFIXES.some((prefix) => message.startsWith(prefix))
|
|
57
|
+
)
|
|
58
|
+
}
|
|
59
|
+
|
|
23
60
|
/** Match known transient transport messages while excluding explicit cancellation. */
|
|
24
61
|
export function isTransientTransportError(cause: unknown): boolean {
|
|
25
62
|
if (!(cause instanceof Error) || isAbortError(cause)) return false
|
|
63
|
+
if (isFetchNetworkError(cause)) return true
|
|
26
64
|
const message = cause.message.toLowerCase()
|
|
27
65
|
return TRANSIENT_TRANSPORT_PATTERNS.some((pattern) => pattern.test(message))
|
|
28
66
|
}
|
|
@@ -10,18 +10,14 @@ export type SentryClientOptions = Sentry.BrowserOptions & {
|
|
|
10
10
|
readonly enabled: boolean
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** Filter known DOM errors and use SDK collection defaults; application options take precedence. */
|
|
14
14
|
export function createSentryClientOptions(options: SentryClientOptions): Sentry.BrowserOptions {
|
|
15
15
|
return {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
ignoreErrors: [
|
|
17
|
+
/^NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node\.$/,
|
|
18
|
+
/^NotFoundError: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node\.$/,
|
|
19
|
+
],
|
|
18
20
|
...options,
|
|
19
|
-
dataCollection: {
|
|
20
|
-
userInfo: false,
|
|
21
|
-
httpBodies: [],
|
|
22
|
-
...options.dataCollection,
|
|
23
|
-
genAI: { inputs: false, outputs: false, ...options.dataCollection?.genAI },
|
|
24
|
-
},
|
|
25
21
|
}
|
|
26
22
|
}
|
|
27
23
|
|
|
@@ -10,18 +10,9 @@ export type SentryServerOptions = Sentry.CloudflareOptions & {
|
|
|
10
10
|
readonly enabled: boolean
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
/**
|
|
13
|
+
/** Pass application options to Workers, Durable Objects, and Workflows with the SDK defaults. */
|
|
14
14
|
export function createSentryOptions(options: SentryServerOptions): Sentry.CloudflareOptions {
|
|
15
|
-
return
|
|
16
|
-
enableLogs: true,
|
|
17
|
-
...options,
|
|
18
|
-
dataCollection: {
|
|
19
|
-
userInfo: false,
|
|
20
|
-
httpBodies: [],
|
|
21
|
-
...options.dataCollection,
|
|
22
|
-
genAI: { inputs: false, outputs: false, ...options.dataCollection?.genAI },
|
|
23
|
-
},
|
|
24
|
-
}
|
|
15
|
+
return options
|
|
25
16
|
}
|
|
26
17
|
|
|
27
18
|
/** Register with setLoggerErrorHook once at startup; an explicit user applies only to this report. */
|
|
@@ -39,12 +30,14 @@ export function createSentryLoggerErrorHook(): LoggerErrorHook {
|
|
|
39
30
|
export class SentryObservabilityService implements ObservabilityService {
|
|
40
31
|
/** Attribute subsequent reports in the current isolation scope. */
|
|
41
32
|
setUser(user: ObservabilityUser): void {
|
|
42
|
-
Sentry.setUser(user)
|
|
33
|
+
Sentry.setUser({ id: user.id })
|
|
43
34
|
}
|
|
44
35
|
|
|
45
36
|
/** Await work in a separate scope for each queue message or concurrent operation. */
|
|
46
37
|
async withIsolationScope<T>(callback: (scope: ObservabilityScope) => T | Promise<T>): Promise<T> {
|
|
47
|
-
return await Sentry.withIsolationScope(
|
|
38
|
+
return await Sentry.withIsolationScope((scope) =>
|
|
39
|
+
callback({ setUser: (user) => scope.setUser({ id: user.id }) }),
|
|
40
|
+
)
|
|
48
41
|
}
|
|
49
42
|
}
|
|
50
43
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ErrorEvent, EventHint } from '@sentry/cloudflare'
|
|
2
|
+
import { isNotFound, isRedirect } from '@tanstack/react-router'
|
|
3
|
+
|
|
4
|
+
import type { ErrorTable } from '@/core'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create a Sentry beforeSend filter from the application's error table, as Suparelay does.
|
|
8
|
+
* Drop known payloads and Router control flow after the error middleware reports the original failure.
|
|
9
|
+
* Preserve real errors and unknown failures. Register the logger error hook before using this filter.
|
|
10
|
+
*/
|
|
11
|
+
export function createSentryErrorFilter<Entries>(errors: ErrorTable<Entries>) {
|
|
12
|
+
return function beforeSend(event: ErrorEvent, hint: EventHint): ErrorEvent | null {
|
|
13
|
+
const thrown = hint.originalException
|
|
14
|
+
if (thrown instanceof Error) return event
|
|
15
|
+
if (isRedirect(thrown) || isNotFound(thrown)) return null
|
|
16
|
+
return errors.parse(thrown) === null ? event : null
|
|
17
|
+
}
|
|
18
|
+
}
|