Skip to content

Commit c29ddfa

Browse files
authored
1 parent fce854c commit c29ddfa

File tree

5 files changed

+207
-75
lines changed

5 files changed

+207
-75
lines changed

.changeset/silent-swans-rescue.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
'toucan-js': major
3+
---
4+
5+
This release upgrades the underlying Sentry SDKs to v8.
6+
7+
- Toucan now extends [ScopeClass](https://github.com/getsentry/sentry-javascript/blob/master/packages/core/src/scope.ts) instead of Hub.
8+
- Class-based integrations have been removed in Sentry v8. Toucan adapts to this change by renaming:
9+
- `Dedupe` integration to `dedupeIntegration`
10+
- `ExtraErrorData` integration to `extraErrorDataIntegration`
11+
- `RewriteFrames` integration to `rewriteFramesIntegration`
12+
- `SessionTiming` integration to `sessionTimingIntegration`
13+
- `LinkedErrors` integration to `linkedErrorsIntegration`
14+
- `RequestData` integration to `requestDataIntegration`
15+
- Additionally, `Transaction` integration is no longer provided.
16+
- Toucan instance can now be deeply copied using `Toucan.clone()`.
17+
18+
Refer to [Sentry v8 release notes](https://github.com/getsentry/sentry-javascript/releases/tag/8.0.0) and [Sentry v7->v8](https://github.com/getsentry/sentry-javascript/blob/8.0.0/MIGRATION.md) for additional context.

packages/toucan-js/README.md

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
## Features
1818

19-
This SDK provides all options and methods of [Hub](https://github.com/getsentry/sentry-javascript/blob/master/packages/core/src/hub.ts) and additionally:
19+
This SDK provides all options and methods of [ScopeClass](https://github.com/getsentry/sentry-javascript/blob/master/packages/core/src/scope.ts) and additionally:
2020

2121
### Additional constructor options
2222

@@ -44,30 +44,27 @@ On top of base `transportOptions` you can pass additional configuration:
4444

4545
## Integrations
4646

47-
You can use custom integrations to enhance `toucan-js` as you would any other Sentry SDK. Some integrations are provided in [@sentry/integrations](https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations) package, and you can also write your own! To ensure an integration will work properly in `toucan-js`, it must:
47+
You can use custom integrations to enhance `toucan-js` as you would any other Sentry SDK. Some integrations are provided in various [Sentry packages](https://github.com/getsentry/sentry-javascript/tree/develop/packages), and you can also write your own! To ensure an integration will work properly in `toucan-js`, it must:
4848

49-
- not use global `getCurrentHub` from `@sentry/core`.
5049
- not enhance or wrap global runtime methods (such as `console.log`).
5150
- not use runtime APIs that aren't available in Cloudflare Workers (NodeJS runtime functions, `window` object, etc...).
5251

53-
Supported integrations from [@sentry/integrations](https://github.com/getsentry/sentry-javascript/tree/master/packages/integrations) are re-exported from `toucan-js`:
52+
Supported integrations from [@sentry/core](https://github.com/getsentry/sentry-javascript/tree/develop/packages/core) are re-exported from `toucan-js`:
5453

55-
- [Dedupe](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/dedupe.ts)
56-
- [ExtraErrorData](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/extraerrordata.ts)
57-
- [RewriteFrames](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/rewriteframes.ts)
58-
- [SessionTiming](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/sessiontiming.ts)
59-
- [Transaction](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/transaction.ts)
54+
- [dedupeIntegration](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/dedupe.ts)
55+
- [extraErrorDataIntegration](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/extraerrordata.ts)
56+
- [rewriteFramesIntegration](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/rewriteframes.ts)
57+
- [sessionTimingIntegration](https://github.com/getsentry/sentry-javascript/blob/master/packages/integrations/src/sessiontiming.ts)
6058

6159
`toucan-js` also provides 2 integrations that are enabled by default, but are provided if you need to reconfigure them:
6260

63-
- [LinkedErrors](src/integrations/linkedErrors.ts)
64-
- [RequestData](src/integrations/requestData.ts)
61+
- [linkedErrorsIntegration](src/integrations/linkedErrors.ts)
62+
- [requestDataIntegration](src/integrations/requestData.ts)
6563

6664
### Custom integration example:
6765

6866
```ts
69-
import { Toucan } from 'toucan-js';
70-
import { RewriteFrames } from '@sentry/integrations';
67+
import { Toucan, rewriteFramesIntegration } from 'toucan-js';
7168

7269
type Env = {
7370
SENTRY_DSN: string;
@@ -79,7 +76,7 @@ export default {
7976
dsn: env.SENTRY_DSN,
8077
context,
8178
request,
82-
integrations: [new RewriteFrames({ root: '/' })],
79+
integrations: [rewriteFramesIntegration({ root: '/' })],
8380
});
8481

8582
...

packages/toucan-js/src/sdk.ts

Lines changed: 65 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,49 +15,55 @@ import type { Breadcrumb, CheckIn, MonitorConfig } from '@sentry/types';
1515
* The Cloudflare Workers SDK.
1616
*/
1717
export class Toucan extends Scope {
18-
constructor(options: Options | ToucanClient) {
18+
#options: Options;
19+
20+
constructor(options: Options) {
1921
super();
2022

21-
if (options instanceof ToucanClient) {
22-
this.setClient(options);
23-
options.setSdk(this);
24-
} else {
25-
options.defaultIntegrations =
26-
options.defaultIntegrations === false
27-
? []
28-
: [
29-
...(Array.isArray(options.defaultIntegrations)
30-
? options.defaultIntegrations
31-
: [
32-
requestDataIntegration(options.requestDataOptions),
33-
linkedErrorsIntegration(),
34-
]),
35-
];
36-
37-
if (options.release === undefined) {
38-
const detectedRelease = getSentryRelease();
39-
if (detectedRelease !== undefined) {
40-
options.release = detectedRelease;
41-
}
42-
}
23+
options.defaultIntegrations =
24+
options.defaultIntegrations === false
25+
? []
26+
: [
27+
...(Array.isArray(options.defaultIntegrations)
28+
? options.defaultIntegrations
29+
: [
30+
requestDataIntegration(options.requestDataOptions),
31+
linkedErrorsIntegration(),
32+
]),
33+
];
4334

44-
const client = new ToucanClient({
45-
...options,
46-
transport: makeFetchTransport,
47-
integrations: getIntegrationsToSetup(options),
48-
stackParser: stackParserFromStackParserOptions(
49-
options.stackParser || defaultStackParser,
50-
),
51-
transportOptions: {
52-
...options.transportOptions,
53-
context: options.context,
54-
},
55-
});
56-
57-
this.setClient(client);
58-
client.setSdk(this);
59-
client.setupIntegrations();
35+
if (options.release === undefined) {
36+
const detectedRelease = getSentryRelease();
37+
if (detectedRelease !== undefined) {
38+
options.release = detectedRelease;
39+
}
6040
}
41+
42+
this.#options = options;
43+
44+
this.attachNewClient();
45+
}
46+
47+
/**
48+
* Creates new ToucanClient and links it to this instance.
49+
*/
50+
protected attachNewClient() {
51+
const client = new ToucanClient({
52+
...this.#options,
53+
transport: makeFetchTransport,
54+
integrations: getIntegrationsToSetup(this.#options),
55+
stackParser: stackParserFromStackParserOptions(
56+
this.#options.stackParser || defaultStackParser,
57+
),
58+
transportOptions: {
59+
...this.#options.transportOptions,
60+
context: this.#options.context,
61+
},
62+
});
63+
64+
this.setClient(client);
65+
client.setSdk(this);
66+
client.setupIntegrations();
6167
}
6268

6369
/**
@@ -112,13 +118,15 @@ export class Toucan extends Scope {
112118
}
113119

114120
/**
115-
* Creates a new scope with and executes the given operation within.
116-
* The scope is automatically removed once the operation
117-
* finishes or throws.
121+
* Clone all data from this instance into a new Toucan instance.
122+
*
123+
* @override
124+
* @returns New Toucan instance.
118125
*/
119-
withScope<T>(callback: (scope: Toucan) => T): T {
120-
// Clone this scope
121-
const toucan = new Toucan(this.getClient<ToucanClient>() as ToucanClient);
126+
clone(): Toucan {
127+
// Create new scope using the same options
128+
const toucan = new Toucan({ ...this.#options });
129+
122130
// And copy all the scope data
123131
toucan._breadcrumbs = [...this._breadcrumbs];
124132
toucan._tags = { ...this._tags };
@@ -134,6 +142,18 @@ export class Toucan extends Scope {
134142
toucan._attachments = [...this._attachments];
135143
toucan._sdkProcessingMetadata = { ...this._sdkProcessingMetadata };
136144
toucan._propagationContext = { ...this._propagationContext };
145+
toucan._lastEventId = this._lastEventId;
146+
147+
return toucan;
148+
}
149+
150+
/**
151+
* Creates a new scope with and executes the given operation within.
152+
* The scope is automatically removed once the operation
153+
* finishes or throws.
154+
*/
155+
withScope<T>(callback: (scope: Toucan) => T): T {
156+
const toucan = this.clone();
137157
return callback(toucan);
138158
}
139159
}

packages/toucan-js/test/index.spec.ts

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Event } from '@sentry/types';
1+
import type { Event, Session } from '@sentry/types';
22
import { defineIntegration } from '@sentry/core';
33
import { Toucan } from 'toucan-js';
44
import {
@@ -1106,6 +1106,103 @@ describe('Toucan', () => {
11061106
});
11071107
});
11081108

1109+
describe('cloning', () => {
1110+
test('clone', async () => {
1111+
const toucan = new Toucan({ dsn: VALID_DSN, context });
1112+
1113+
toucan.addBreadcrumb({ message: 'test' });
1114+
toucan.setTag('foo', 'bar');
1115+
toucan.setExtra('foo', 'bar');
1116+
toucan.setContext('foo', { foo: 'bar' });
1117+
toucan.setUser({ email: '[email protected]' });
1118+
toucan.setLevel('debug');
1119+
toucan.setSession({ ipAddress: '1.1.1.1' } as Session);
1120+
toucan.setTransactionName('foo');
1121+
toucan.setFingerprint(['foo']);
1122+
toucan.addEventProcessor((event) => {
1123+
// Verifying 'extra' added by event processor allows us to verify that event processors get cloned as well
1124+
if (event.extra) event.extra['bar'] = 'baz';
1125+
return event;
1126+
});
1127+
toucan.setRequestSession({ status: 'ok' });
1128+
toucan.setSDKProcessingMetadata({ foo: 'bar' });
1129+
toucan.setPropagationContext({ spanId: 'foo', traceId: 'bar' });
1130+
toucan.setLastEventId('foo');
1131+
1132+
const toucanClone1 = toucan.clone();
1133+
const toucanClone2 = toucan.clone();
1134+
const toucanClone3 = toucanClone2.clone();
1135+
1136+
// Verify we always create new client instance during Toucan.clone()
1137+
expect(toucan.getClient()).not.toBe(toucanClone1.getClient());
1138+
expect(toucan.getClient()).not.toBe(toucanClone2.getClient());
1139+
expect(toucan.getClient()).not.toBe(toucanClone3.getClient());
1140+
expect(toucanClone1.getClient()).not.toBe(toucanClone2.getClient());
1141+
expect(toucanClone1.getClient()).not.toBe(toucanClone3.getClient());
1142+
expect(toucanClone2.getClient()).not.toBe(toucanClone3.getClient());
1143+
1144+
// Capture an exception on original and cloned instances and verify the metadata in payloads are the same
1145+
toucan.captureException(new Error('error!'));
1146+
toucanClone1.captureException(new Error('error!'));
1147+
toucanClone2.captureException(new Error('error!'));
1148+
toucanClone3.captureException(new Error('error!'));
1149+
1150+
const waitUntilResults = await getMiniflareWaitUntil(context);
1151+
1152+
expect(waitUntilResults.length).toBe(4);
1153+
expect(requests.length).toBe(4);
1154+
1155+
const assertEnvelopePayloadsEqual = (
1156+
p1: Record<string, any>,
1157+
p2: Record<string, any>,
1158+
) => {
1159+
const assertPropertyExistsAndEqual = (propertyName: string) => {
1160+
expect(propertyName in p1).toBe(true);
1161+
expect(p1[propertyName]).toBeTruthy();
1162+
1163+
expect(propertyName in p2).toBe(true);
1164+
expect(p2[propertyName]).toBeTruthy();
1165+
1166+
expect(p1[propertyName]).toStrictEqual(p2[propertyName]);
1167+
};
1168+
1169+
assertPropertyExistsAndEqual('breadcrumbs');
1170+
assertPropertyExistsAndEqual('tags');
1171+
assertPropertyExistsAndEqual('extra');
1172+
assertPropertyExistsAndEqual('contexts');
1173+
assertPropertyExistsAndEqual('user');
1174+
assertPropertyExistsAndEqual('level');
1175+
assertPropertyExistsAndEqual('transaction');
1176+
assertPropertyExistsAndEqual('fingerprint');
1177+
};
1178+
1179+
assertEnvelopePayloadsEqual(
1180+
await requests[0].envelopePayload(),
1181+
await requests[1].envelopePayload(),
1182+
);
1183+
assertEnvelopePayloadsEqual(
1184+
await requests[0].envelopePayload(),
1185+
await requests[2].envelopePayload(),
1186+
);
1187+
assertEnvelopePayloadsEqual(
1188+
await requests[0].envelopePayload(),
1189+
await requests[3].envelopePayload(),
1190+
);
1191+
assertEnvelopePayloadsEqual(
1192+
await requests[1].envelopePayload(),
1193+
await requests[2].envelopePayload(),
1194+
);
1195+
assertEnvelopePayloadsEqual(
1196+
await requests[1].envelopePayload(),
1197+
await requests[3].envelopePayload(),
1198+
);
1199+
assertEnvelopePayloadsEqual(
1200+
await requests[2].envelopePayload(),
1201+
await requests[3].envelopePayload(),
1202+
);
1203+
});
1204+
});
1205+
11091206
describe('scope', () => {
11101207
test('withScope', async () => {
11111208
const toucan = new Toucan({

yarn.lock

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,13 +1610,13 @@
16101610
"@sentry/utils" "7.23.0"
16111611
tslib "^1.9.3"
16121612

1613-
"@sentry/core@8.3.0":
1614-
version "8.3.0"
1615-
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.3.0.tgz#beb91aeceb6be2a623773d7cabd6f8396c3f5926"
1616-
integrity sha512-X7r0WujE7DILtgA5zt4hmHMBTF3xbjJB7Dgrw1Hv5C7WG5qkNqhDPpnRX7WswtHcLSgVPY2GRTQ5Iid7i33zEQ==
1613+
"@sentry/core@8.9.2":
1614+
version "8.9.2"
1615+
resolved "https://registry.yarnpkg.com/@sentry/core/-/core-8.9.2.tgz#af0f2ec25b88da5467cf327d2ffcd555323c30e6"
1616+
integrity sha512-ixm8NISFlPlEo3FjSaqmq4nnd13BRHoafwJ5MG+okCz6BKGZ1SexEggP42/QpGvDprUUHnfncG6WUMgcarr1zA==
16171617
dependencies:
1618-
"@sentry/types" "8.3.0"
1619-
"@sentry/utils" "8.3.0"
1618+
"@sentry/types" "8.9.2"
1619+
"@sentry/utils" "8.9.2"
16201620

16211621
"@sentry/esbuild-plugin@^0.2.3":
16221622
version "0.2.3"
@@ -1660,10 +1660,10 @@
16601660
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-7.23.0.tgz#5d2ce94d81d7c1fad702645306f3c0932708cad5"
16611661
integrity sha512-fZ5XfVRswVZhKoCutQ27UpIHP16tvyc6ws+xq+njHv8Jg8gFBCoOxlJxuFhegD2xxylAn1aiSHNAErFWdajbpA==
16621662

1663-
"@sentry/types@8.3.0":
1664-
version "8.3.0"
1665-
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.3.0.tgz#c5df6d5702a7e2483cf87e82df0cd82297c9c3d7"
1666-
integrity sha512-DbwRTdx5xn8c2EhElD1UneDbcfqz220INPJYiATGJ9pR+cV5kGohyxI6lJxebPdPhPLEFQqYdLXGA6Cjm/uC6A==
1663+
"@sentry/types@8.9.2":
1664+
version "8.9.2"
1665+
resolved "https://registry.yarnpkg.com/@sentry/types/-/types-8.9.2.tgz#d143383fc35552d9f153042cc6d56c5ee8ec2fa6"
1666+
integrity sha512-+LFOyQGl+zk5SZRGZD2MEURf7i5RHgP/mt3s85Rza+vz8M211WJ0YsjkIGUJFSY842nged5QLx4JysLaBlLymg==
16671667

16681668
16691669
version "7.23.0"
@@ -1673,12 +1673,12 @@
16731673
"@sentry/types" "7.23.0"
16741674
tslib "^1.9.3"
16751675

1676-
"@sentry/utils@8.3.0":
1677-
version "8.3.0"
1678-
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.3.0.tgz#c06f3a3ff40d8f8d1d8901a50d676c748575e66d"
1679-
integrity sha512-WFaUZy0OWsF6Mrjenxj4N8zGzA6+pdH2lCV60/IB+V5PvGPgl40MUyjN6aLA/E9BRpqwLNQRMroZjln+J/3aiA==
1676+
"@sentry/utils@8.9.2":
1677+
version "8.9.2"
1678+
resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-8.9.2.tgz#58b003d9c1302f61192e7c99ea42bf1cd5cad7f7"
1679+
integrity sha512-A4srR9mEBFdVXwSEKjQ94msUbVkMr8JeFiEj9ouOFORw/Y/ux/WV2bWVD/ZI9wq0TcTNK8L1wBgU8UMS5lIq3A==
16801680
dependencies:
1681-
"@sentry/types" "8.3.0"
1681+
"@sentry/types" "8.9.2"
16821682

16831683
"@sentry/vite-plugin@^0.2.3":
16841684
version "0.2.3"

0 commit comments

Comments
 (0)