Skip to content

Commit cd0bc3b

Browse files
authored
feat(vercel-edge): add Vercel Edge Runtime package (#9041)
This PR adds a `@sentry/vercel-edge` SDK that can be used by our Next.js or Sveltekit SDKs for edge runtime support.
1 parent d6d1dd4 commit cd0bc3b

22 files changed

+1179
-159
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"postpublish": "lerna run --stream --concurrency 1 postpublish",
2828
"test": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\" test",
2929
"test:unit": "lerna run --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\" test:unit",
30-
"test-ci-browser": "lerna run test --ignore \"@sentry/{bun,node,node-experimental,opentelemetry-node,serverless,nextjs,remix,gatsby,sveltekit}\" --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\"",
30+
"test-ci-browser": "lerna run test --ignore \"@sentry/{bun,node,node-experimental,opentelemetry-node,serverless,nextjs,remix,gatsby,sveltekit,vercel-edge}\" --ignore \"@sentry-internal/{browser-integration-tests,e2e-tests,integration-shims,node-integration-tests,overhead-metrics}\"",
3131
"test-ci-node": "ts-node ./scripts/node-unit-tests.ts",
3232
"test-ci-bun": "lerna run test --scope @sentry/bun",
3333
"test:update-snapshots": "lerna run test:update-snapshots",
@@ -70,6 +70,7 @@
7070
"packages/types",
7171
"packages/typescript",
7272
"packages/utils",
73+
"packages/vercel-edge",
7374
"packages/vue",
7475
"packages/wasm"
7576
],

packages/e2e-tests/verdaccio-config/config.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,12 @@ packages:
164164
unpublish: $all
165165
# proxy: npmjs # Don't proxy for E2E tests!
166166

167+
'@sentry/vercel-edge':
168+
access: $all
169+
publish: $all
170+
unpublish: $all
171+
# proxy: npmjs # Don't proxy for E2E tests!
172+
167173
'@sentry/vue':
168174
access: $all
169175
publish: $all

packages/vercel-edge/.eslintrc.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
module.exports = {
2+
env: {
3+
node: true,
4+
},
5+
extends: ['../../.eslintrc.js'],
6+
rules: {
7+
'@sentry-internal/sdk/no-optional-chaining': 'off',
8+
'@sentry-internal/sdk/no-nullish-coalescing': 'off',
9+
'@sentry-internal/sdk/no-unsupported-es6-methods': 'off',
10+
'@sentry-internal/sdk/no-class-field-initializers': 'off',
11+
},
12+
};

packages/vercel-edge/LICENSE

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Copyright (c) 2023 Sentry (https://sentry.io) and individual contributors. All rights reserved.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
4+
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
5+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
6+
persons to whom the Software is furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
9+
Software.
10+
11+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
12+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
14+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

packages/vercel-edge/README.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<p align="center">
2+
<a href="https://sentry.io/?utm_source=github&utm_medium=logo" target="_blank">
3+
<img src="https://sentry-brand.storage.googleapis.com/sentry-wordmark-dark-280x84.png" alt="Sentry" width="280" height="84">
4+
</a>
5+
</p>
6+
7+
# Official Sentry SDK for Vercel Edge Runtime [ALPHA]
8+
9+
[![npm version](https://img.shields.io/npm/v/@sentry/vercel-edge.svg)](https://www.npmjs.com/package/@sentry/vercel-edge)
10+
[![npm dm](https://img.shields.io/npm/dm/@sentry/vercel-edge.svg)](https://www.npmjs.com/package/@sentry/vercel-edge)
11+
[![npm dt](https://img.shields.io/npm/dt/@sentry/vercel-edge.svg)](https://www.npmjs.com/package/@sentry/vercel-edge)
12+
13+
## Links
14+
15+
- [Official SDK Docs](https://docs.sentry.io/quickstart/)
16+
- [TypeDoc](http://getsentry.github.io/sentry-javascript/)
17+
18+
**Note: This SDK is still in an alpha state. Breaking changes can occur at any time.**
19+
20+
## Usage
21+
22+
To use this SDK, call `init(options)` as early as possible in the main entry module. This will initialize the SDK and
23+
hook into the environment. Note that you can turn off almost all side effects using the respective options.
24+
25+
```javascript
26+
// ES5 Syntax
27+
const Sentry = require('@sentry/vercel-edge');
28+
// ES6 Syntax
29+
import * as Sentry from '@sentry/vercel-edge';
30+
31+
Sentry.init({
32+
dsn: '__DSN__',
33+
// ...
34+
});
35+
```
36+
37+
To set context information or send manual events, use the exported functions of `@sentry/vercel-edge`. Note that these
38+
functions will not perform any action before you have called `init()`:
39+
40+
```javascript
41+
// Set user information, as well as tags and further extras
42+
Sentry.configureScope(scope => {
43+
scope.setExtra('battery', 0.7);
44+
scope.setTag('user_mode', 'admin');
45+
scope.setUser({ id: '4711' });
46+
// scope.clear();
47+
});
48+
49+
// Add a breadcrumb for future events
50+
Sentry.addBreadcrumb({
51+
message: 'My Breadcrumb',
52+
// ...
53+
});
54+
55+
// Capture exceptions, messages or manual events
56+
Sentry.captureMessage('Hello, world!');
57+
Sentry.captureException(new Error('Good bye'));
58+
Sentry.captureEvent({
59+
message: 'Manual',
60+
stacktrace: [
61+
// ...
62+
],
63+
});
64+
```

packages/vercel-edge/jest.config.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const baseConfig = require('../../jest/jest.config.js');
2+
3+
module.exports = {
4+
...baseConfig,
5+
// TODO: Fix tests to work with the Edge environment
6+
// testEnvironment: '@edge-runtime/jest-environment',
7+
};

packages/vercel-edge/package.json

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"name": "@sentry/vercel-edge",
3+
"version": "7.69.0",
4+
"description": "Offical Sentry SDK for the Vercel Edge Runtime",
5+
"repository": "git://github.com/getsentry/sentry-javascript.git",
6+
"homepage": "https://github.com/getsentry/sentry-javascript/tree/master/packages/vercel-edge",
7+
"author": "Sentry",
8+
"license": "MIT",
9+
"engines": {
10+
"node": ">=8"
11+
},
12+
"main": "build/cjs/index.js",
13+
"module": "build/esm/index.js",
14+
"types": "build/types/index.d.ts",
15+
"typesVersions": {
16+
"<4.9": {
17+
"build/types/index.d.ts": [
18+
"build/types-ts3.8/index.d.ts"
19+
]
20+
}
21+
},
22+
"publishConfig": {
23+
"access": "public"
24+
},
25+
"dependencies": {
26+
"@sentry/core": "7.69.0",
27+
"@sentry/types": "7.69.0",
28+
"@sentry/utils": "7.69.0",
29+
"tslib": "^2.4.1 || ^1.9.3"
30+
},
31+
"devDependencies": {
32+
"@edge-runtime/jest-environment": "2.2.3",
33+
"@edge-runtime/types": "2.2.3"
34+
},
35+
"scripts": {
36+
"build": "run-p build:transpile build:types",
37+
"build:dev": "yarn build",
38+
"build:transpile": "rollup -c rollup.npm.config.js",
39+
"build:types": "run-s build:types:core build:types:downlevel",
40+
"build:types:core": "tsc -p tsconfig.types.json",
41+
"build:types:downlevel": "yarn downlevel-dts build/types build/types-ts3.8 --to ts3.8",
42+
"build:watch": "run-p build:transpile:watch build:types:watch",
43+
"build:dev:watch": "yarn build:watch",
44+
"build:transpile:watch": "rollup -c rollup.npm.config.js --watch",
45+
"build:types:watch": "tsc -p tsconfig.types.json --watch",
46+
"build:tarball": "ts-node ../../scripts/prepack.ts && npm pack ./build",
47+
"circularDepCheck": "madge --circular src/index.ts",
48+
"clean": "rimraf build coverage sentry-core-*.tgz",
49+
"fix": "run-s fix:eslint fix:prettier",
50+
"fix:eslint": "eslint . --format stylish --fix",
51+
"fix:prettier": "prettier --write \"{src,test,scripts}/**/**.ts\"",
52+
"lint": "run-s lint:prettier lint:eslint",
53+
"lint:eslint": "eslint . --format stylish",
54+
"lint:prettier": "prettier --check \"{src,test,scripts}/**/**.ts\"",
55+
"test": "jest",
56+
"test:watch": "jest --watch",
57+
"version": "node ../../scripts/versionbump.js src/version.ts",
58+
"yalc:publish": "ts-node ../../scripts/prepack.ts && yalc publish build --push"
59+
},
60+
"volta": {
61+
"extends": "../../package.json"
62+
},
63+
"sideEffects": false,
64+
"madge":{
65+
"detectiveOptions": {
66+
"ts": {
67+
"skipTypeImports": true
68+
}
69+
}
70+
}
71+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { makeBaseNPMConfig, makeNPMConfigVariants } from '../../rollup/index.js';
2+
3+
export default makeNPMConfigVariants(makeBaseNPMConfig());

packages/vercel-edge/src/async.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { Carrier, Hub, RunWithAsyncContextOptions } from '@sentry/core';
2+
import { ensureHubOnCarrier, getHubFromCarrier, setAsyncContextStrategy } from '@sentry/core';
3+
import { GLOBAL_OBJ, logger } from '@sentry/utils';
4+
5+
interface AsyncLocalStorage<T> {
6+
getStore(): T | undefined;
7+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
8+
run<R, TArgs extends any[]>(store: T, callback: (...args: TArgs) => R, ...args: TArgs): R;
9+
}
10+
11+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-explicit-any
12+
const MaybeGlobalAsyncLocalStorage = (GLOBAL_OBJ as any).AsyncLocalStorage;
13+
14+
let asyncStorage: AsyncLocalStorage<Hub>;
15+
16+
/**
17+
* Sets the async context strategy to use AsyncLocalStorage which should be available in the edge runtime.
18+
*/
19+
export function setAsyncLocalStorageAsyncContextStrategy(): void {
20+
if (!MaybeGlobalAsyncLocalStorage) {
21+
__DEBUG_BUILD__ &&
22+
logger.warn(
23+
"Tried to register AsyncLocalStorage async context strategy in a runtime that doesn't support AsyncLocalStorage.",
24+
);
25+
return;
26+
}
27+
28+
if (!asyncStorage) {
29+
asyncStorage = new MaybeGlobalAsyncLocalStorage();
30+
}
31+
32+
function getCurrentHub(): Hub | undefined {
33+
return asyncStorage.getStore();
34+
}
35+
36+
function createNewHub(parent: Hub | undefined): Hub {
37+
const carrier: Carrier = {};
38+
ensureHubOnCarrier(carrier, parent);
39+
return getHubFromCarrier(carrier);
40+
}
41+
42+
function runWithAsyncContext<T>(callback: () => T, options: RunWithAsyncContextOptions): T {
43+
const existingHub = getCurrentHub();
44+
45+
if (existingHub && options?.reuseExisting) {
46+
// We're already in an async context, so we don't need to create a new one
47+
// just call the callback with the current hub
48+
return callback();
49+
}
50+
51+
const newHub = createNewHub(existingHub);
52+
53+
return asyncStorage.run(newHub, () => {
54+
return callback();
55+
});
56+
}
57+
58+
setAsyncContextStrategy({ getCurrentHub, runWithAsyncContext });
59+
}

packages/vercel-edge/src/client.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import type { ServerRuntimeClientOptions } from '@sentry/core';
2+
import { SDK_VERSION, ServerRuntimeClient } from '@sentry/core';
3+
4+
import type { VercelEdgeClientOptions } from './types';
5+
6+
declare const process: {
7+
env: Record<string, string>;
8+
};
9+
10+
/**
11+
* The Sentry Vercel Edge Runtime SDK Client.
12+
*
13+
* @see VercelEdgeClientOptions for documentation on configuration options.
14+
* @see ServerRuntimeClient for usage documentation.
15+
*/
16+
export class VercelEdgeClient extends ServerRuntimeClient<VercelEdgeClientOptions> {
17+
/**
18+
* Creates a new Vercel Edge Runtime SDK instance.
19+
* @param options Configuration options for this SDK.
20+
*/
21+
public constructor(options: VercelEdgeClientOptions) {
22+
options._metadata = options._metadata || {};
23+
options._metadata.sdk = options._metadata.sdk || {
24+
name: 'sentry.javascript.vercel-edge',
25+
packages: [
26+
{
27+
name: 'npm:@sentry/vercel-edge',
28+
version: SDK_VERSION,
29+
},
30+
],
31+
version: SDK_VERSION,
32+
};
33+
34+
const clientOptions: ServerRuntimeClientOptions = {
35+
...options,
36+
platform: 'vercel-edge',
37+
// TODO: Grab version information
38+
runtime: { name: 'vercel-edge' },
39+
serverName: options.serverName || process.env.SENTRY_NAME,
40+
};
41+
42+
super(clientOptions);
43+
}
44+
}

packages/vercel-edge/src/index.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
export type {
2+
Breadcrumb,
3+
BreadcrumbHint,
4+
PolymorphicRequest,
5+
Request,
6+
SdkInfo,
7+
Event,
8+
EventHint,
9+
Exception,
10+
Session,
11+
// eslint-disable-next-line deprecation/deprecation
12+
Severity,
13+
SeverityLevel,
14+
Span,
15+
StackFrame,
16+
Stacktrace,
17+
Thread,
18+
Transaction,
19+
User,
20+
} from '@sentry/types';
21+
export type { AddRequestDataToEventOptions } from '@sentry/utils';
22+
23+
export type { VercelEdgeOptions } from './types';
24+
25+
export {
26+
addGlobalEventProcessor,
27+
addBreadcrumb,
28+
captureException,
29+
captureEvent,
30+
captureMessage,
31+
close,
32+
configureScope,
33+
createTransport,
34+
extractTraceparentData,
35+
flush,
36+
getActiveTransaction,
37+
getHubFromCarrier,
38+
getCurrentHub,
39+
Hub,
40+
lastEventId,
41+
makeMain,
42+
runWithAsyncContext,
43+
Scope,
44+
startTransaction,
45+
SDK_VERSION,
46+
setContext,
47+
setExtra,
48+
setExtras,
49+
setTag,
50+
setTags,
51+
setUser,
52+
spanStatusfromHttpCode,
53+
trace,
54+
withScope,
55+
captureCheckIn,
56+
setMeasurement,
57+
getActiveSpan,
58+
startSpan,
59+
startInactiveSpan,
60+
startSpanManual,
61+
} from '@sentry/core';
62+
export type { SpanStatusType } from '@sentry/core';
63+
64+
export { VercelEdgeClient } from './client';
65+
export { defaultIntegrations, init } from './sdk';
66+
67+
import { Integrations as CoreIntegrations } from '@sentry/core';
68+
69+
const INTEGRATIONS = {
70+
...CoreIntegrations,
71+
};
72+
73+
export { INTEGRATIONS as Integrations };

0 commit comments

Comments
 (0)