@beignet/provider-error-reporting-sentry 0.0.14
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/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/README.md +76 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/package.json +95 -0
- package/src/index.ts +342 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# @beignet/provider-error-reporting-sentry
|
|
2
|
+
|
|
3
|
+
## 0.0.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 0b106c3: Add first-class error reporting primitives and a Sentry-backed error reporting
|
|
8
|
+
provider.
|
|
9
|
+
|
|
10
|
+
## 0.0.13
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Initial Sentry-backed error reporting provider.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Taylor Bryant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# @beignet/provider-error-reporting-sentry
|
|
2
|
+
|
|
3
|
+
Sentry-backed `ErrorReporterPort` provider for Beignet applications.
|
|
4
|
+
|
|
5
|
+
The provider installs `ctx.ports.errorReporter` using `@sentry/node` while
|
|
6
|
+
keeping Sentry imports out of contracts, use cases, routes, and domain code.
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
bun add @beignet/provider-error-reporting-sentry @sentry/node
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Register
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
// server/providers.ts
|
|
18
|
+
import { createSentryErrorReportingProvider } from "@beignet/provider-error-reporting-sentry";
|
|
19
|
+
|
|
20
|
+
export const providers = [
|
|
21
|
+
createSentryErrorReportingProvider({
|
|
22
|
+
dsn: process.env.SENTRY_DSN,
|
|
23
|
+
init: {
|
|
24
|
+
environment: process.env.NODE_ENV,
|
|
25
|
+
},
|
|
26
|
+
}),
|
|
27
|
+
];
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
With no `dsn` or `SENTRY_DSN`, the provider still contributes the Beignet port
|
|
31
|
+
but does not initialize Sentry. This is useful for examples and local apps that
|
|
32
|
+
want stable wiring before production secrets exist.
|
|
33
|
+
|
|
34
|
+
## Use
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
await ctx.ports.errorReporter.captureException(error, {
|
|
38
|
+
level: "error",
|
|
39
|
+
requestId: ctx.requestId,
|
|
40
|
+
traceId: ctx.traceId,
|
|
41
|
+
tags: {
|
|
42
|
+
feature: "billing",
|
|
43
|
+
},
|
|
44
|
+
contexts: {
|
|
45
|
+
tenant: { id: ctx.tenant.id },
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The provider also contributes `ctx.ports.sentry` as an escape hatch with the raw
|
|
51
|
+
Sentry SDK and configured client.
|
|
52
|
+
|
|
53
|
+
## Devtools
|
|
54
|
+
|
|
55
|
+
Captured exceptions and messages are recorded as Beignet error instrumentation
|
|
56
|
+
under the `errors` watcher when devtools or another instrumentation sink is
|
|
57
|
+
installed before the provider.
|
|
58
|
+
|
|
59
|
+
## API
|
|
60
|
+
|
|
61
|
+
### `createSentryErrorReporter(options)`
|
|
62
|
+
|
|
63
|
+
Creates an `ErrorReporterPort` from a Sentry-compatible client. Use this for
|
|
64
|
+
tests or custom provider composition.
|
|
65
|
+
|
|
66
|
+
### `createSentryErrorReportingProvider(options)`
|
|
67
|
+
|
|
68
|
+
Creates a Beignet lifecycle provider that contributes:
|
|
69
|
+
|
|
70
|
+
- `ctx.ports.errorReporter`, the standard Beignet `ErrorReporterPort`
|
|
71
|
+
- `ctx.ports.sentry`, an escape hatch with the raw Sentry client and SDK
|
|
72
|
+
|
|
73
|
+
### `sentryErrorReportingProvider`
|
|
74
|
+
|
|
75
|
+
Ready-to-register provider using the default Sentry singleton and
|
|
76
|
+
`process.env.SENTRY_DSN` when present.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/provider-error-reporting-sentry
|
|
3
|
+
*
|
|
4
|
+
* Sentry-backed error reporting provider for Beignet.
|
|
5
|
+
*/
|
|
6
|
+
import type { ErrorReportContext, ErrorReporterFlushOptions, ErrorReporterPort, ErrorReportJsonValue, ErrorReportLevel, ErrorReportOptions, ErrorReportResult, ErrorReportTags, ErrorReportUser } from "@beignet/core/error-reporting";
|
|
7
|
+
import { type ProviderInstrumentationTarget } from "@beignet/core/providers";
|
|
8
|
+
import type { NodeOptions, Scope } from "@sentry/node";
|
|
9
|
+
import * as Sentry from "@sentry/node";
|
|
10
|
+
/**
|
|
11
|
+
* Sentry SDK surface used by the provider. Supplying this in tests avoids the
|
|
12
|
+
* process-wide singleton.
|
|
13
|
+
*/
|
|
14
|
+
export interface SentryErrorReportingClient {
|
|
15
|
+
captureException(error: unknown): string | undefined;
|
|
16
|
+
captureMessage(message: string): string | undefined;
|
|
17
|
+
setUser(user: SentryErrorReportingUser | null): void;
|
|
18
|
+
setTags(tags: Record<string, string>): void;
|
|
19
|
+
setContext(name: string, context: ErrorReportContext | null): void;
|
|
20
|
+
flush(timeout?: number): PromiseLike<boolean> | boolean;
|
|
21
|
+
close?(timeout?: number): PromiseLike<boolean> | boolean;
|
|
22
|
+
withScope<T>(callback: (scope: Scope) => T): T;
|
|
23
|
+
init?(options?: NodeOptions): void;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Escape hatch for apps that need the raw Sentry SDK.
|
|
27
|
+
*/
|
|
28
|
+
export interface SentryErrorReportingEscapeHatch {
|
|
29
|
+
client: SentryErrorReportingClient;
|
|
30
|
+
sdk: typeof Sentry;
|
|
31
|
+
}
|
|
32
|
+
export type SentryErrorReportingUser = Record<string, ErrorReportJsonValue | undefined>;
|
|
33
|
+
/**
|
|
34
|
+
* Ports contributed by the Sentry provider.
|
|
35
|
+
*/
|
|
36
|
+
export interface SentryErrorReportingProviderPorts {
|
|
37
|
+
errorReporter: ErrorReporterPort;
|
|
38
|
+
sentry: SentryErrorReportingEscapeHatch;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Options for the direct Sentry reporter factory.
|
|
42
|
+
*/
|
|
43
|
+
export interface CreateSentryErrorReporterOptions {
|
|
44
|
+
client?: SentryErrorReportingClient;
|
|
45
|
+
instrumentation?: ProviderInstrumentationTarget;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Options for the Sentry lifecycle provider.
|
|
49
|
+
*/
|
|
50
|
+
export interface CreateSentryErrorReportingProviderOptions {
|
|
51
|
+
client?: SentryErrorReportingClient;
|
|
52
|
+
/**
|
|
53
|
+
* Sentry DSN. When omitted, the provider checks `process.env.SENTRY_DSN`.
|
|
54
|
+
* With no DSN, the SDK is not initialized and captures are effectively no-op
|
|
55
|
+
* until the app initializes Sentry elsewhere.
|
|
56
|
+
*/
|
|
57
|
+
dsn?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Additional Sentry initialization options.
|
|
60
|
+
*/
|
|
61
|
+
init?: Omit<NodeOptions, "dsn">;
|
|
62
|
+
/**
|
|
63
|
+
* Close the Sentry client on server stop.
|
|
64
|
+
*
|
|
65
|
+
* @default false
|
|
66
|
+
*/
|
|
67
|
+
closeOnStop?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Flush/close timeout in milliseconds.
|
|
70
|
+
*
|
|
71
|
+
* @default 2000
|
|
72
|
+
*/
|
|
73
|
+
flushTimeoutMs?: number;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Create a Beignet error reporter backed by Sentry.
|
|
77
|
+
*/
|
|
78
|
+
export declare function createSentryErrorReporter(options?: CreateSentryErrorReporterOptions): ErrorReporterPort;
|
|
79
|
+
/**
|
|
80
|
+
* Create a Beignet provider that contributes `ports.errorReporter`.
|
|
81
|
+
*/
|
|
82
|
+
export declare function createSentryErrorReportingProvider(options?: CreateSentryErrorReportingProviderOptions): import("@beignet/core/providers").ServiceProvider<unknown, import("@standard-schema/spec").StandardSchemaV1<void, void>, {
|
|
83
|
+
errorReporter: ErrorReporterPort;
|
|
84
|
+
sentry: {
|
|
85
|
+
client: typeof Sentry | SentryErrorReportingClient;
|
|
86
|
+
sdk: typeof Sentry;
|
|
87
|
+
};
|
|
88
|
+
}, unknown, void>;
|
|
89
|
+
/**
|
|
90
|
+
* Ready-to-register Sentry error reporting provider using the default Sentry
|
|
91
|
+
* singleton.
|
|
92
|
+
*/
|
|
93
|
+
export declare const sentryErrorReportingProvider: import("@beignet/core/providers").ServiceProvider<unknown, import("@standard-schema/spec").StandardSchemaV1<void, void>, {
|
|
94
|
+
errorReporter: ErrorReporterPort;
|
|
95
|
+
sentry: {
|
|
96
|
+
client: typeof Sentry | SentryErrorReportingClient;
|
|
97
|
+
sdk: typeof Sentry;
|
|
98
|
+
};
|
|
99
|
+
}, unknown, void>;
|
|
100
|
+
export type { ErrorReportContext, ErrorReporterFlushOptions, ErrorReporterPort, ErrorReportJsonValue, ErrorReportLevel, ErrorReportOptions, ErrorReportResult, ErrorReportTags, ErrorReportUser, };
|
|
101
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,eAAe,EAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAGL,KAAK,6BAA6B,EACnC,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAiB,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,MAAM,MAAM,cAAc,CAAC;AAEvC;;;GAGG;AACH,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC;IACrD,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,OAAO,CAAC,IAAI,EAAE,wBAAwB,GAAG,IAAI,GAAG,IAAI,CAAC;IACrD,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IAC5C,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAAC;IACnE,KAAK,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACxD,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACzD,SAAS,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,CAAC,GAAG,CAAC,CAAC;IAC/C,IAAI,CAAC,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,0BAA0B,CAAC;IACnC,GAAG,EAAE,OAAO,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,wBAAwB,GAAG,MAAM,CAC3C,MAAM,EACN,oBAAoB,GAAG,SAAS,CACjC,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,iCAAiC;IAChD,aAAa,EAAE,iBAAiB,CAAC;IACjC,MAAM,EAAE,+BAA+B,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,gCAAgC;IAC/C,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC,eAAe,CAAC,EAAE,6BAA6B,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,WAAW,yCAAyC;IACxD,MAAM,CAAC,EAAE,0BAA0B,CAAC;IACpC;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,iBAAiB,CAmFnB;AAED;;GAEG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,GAAE,yCAA8C;;;;;;kBAgCxD;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B;;;;;;iBACH,CAAC;AAmGvC,YAAY,EACV,kBAAkB,EAClB,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,eAAe,EACf,eAAe,GAChB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/provider-error-reporting-sentry
|
|
3
|
+
*
|
|
4
|
+
* Sentry-backed error reporting provider for Beignet.
|
|
5
|
+
*/
|
|
6
|
+
import { createProvider, createProviderInstrumentation, } from "@beignet/core/providers";
|
|
7
|
+
import * as Sentry from "@sentry/node";
|
|
8
|
+
/**
|
|
9
|
+
* Create a Beignet error reporter backed by Sentry.
|
|
10
|
+
*/
|
|
11
|
+
export function createSentryErrorReporter(options = {}) {
|
|
12
|
+
const client = options.client ?? Sentry;
|
|
13
|
+
const instrumentation = createProviderInstrumentation(options.instrumentation, {
|
|
14
|
+
providerName: "error-reporting-sentry",
|
|
15
|
+
watcher: "errors",
|
|
16
|
+
});
|
|
17
|
+
return {
|
|
18
|
+
async captureException(error, reportOptions) {
|
|
19
|
+
const startedAt = Date.now();
|
|
20
|
+
const id = withConfiguredScope(client, reportOptions, () => client.captureException(error));
|
|
21
|
+
instrumentation.record({
|
|
22
|
+
type: "error",
|
|
23
|
+
watcher: "errors",
|
|
24
|
+
owner: "provider",
|
|
25
|
+
message: errorMessage(error),
|
|
26
|
+
stack: errorStack(error),
|
|
27
|
+
requestId: reportOptions?.requestId,
|
|
28
|
+
traceId: reportOptions?.traceId,
|
|
29
|
+
spanId: reportOptions?.spanId,
|
|
30
|
+
parentSpanId: reportOptions?.parentSpanId,
|
|
31
|
+
traceparent: reportOptions?.traceparent,
|
|
32
|
+
details: {
|
|
33
|
+
provider: "sentry",
|
|
34
|
+
eventId: id,
|
|
35
|
+
level: reportOptions?.level ?? "error",
|
|
36
|
+
durationMs: Date.now() - startedAt,
|
|
37
|
+
tags: reportOptions?.tags,
|
|
38
|
+
contexts: reportOptions?.contexts,
|
|
39
|
+
extra: reportOptions?.extra,
|
|
40
|
+
mechanism: reportOptions?.mechanism,
|
|
41
|
+
handled: reportOptions?.handled,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
return result(id);
|
|
45
|
+
},
|
|
46
|
+
async captureMessage(message, reportOptions) {
|
|
47
|
+
const startedAt = Date.now();
|
|
48
|
+
const id = withConfiguredScope(client, reportOptions, () => client.captureMessage(message));
|
|
49
|
+
instrumentation.record({
|
|
50
|
+
type: "error",
|
|
51
|
+
watcher: "errors",
|
|
52
|
+
owner: "provider",
|
|
53
|
+
message,
|
|
54
|
+
requestId: reportOptions?.requestId,
|
|
55
|
+
traceId: reportOptions?.traceId,
|
|
56
|
+
spanId: reportOptions?.spanId,
|
|
57
|
+
parentSpanId: reportOptions?.parentSpanId,
|
|
58
|
+
traceparent: reportOptions?.traceparent,
|
|
59
|
+
details: {
|
|
60
|
+
provider: "sentry",
|
|
61
|
+
eventId: id,
|
|
62
|
+
level: reportOptions?.level ?? "error",
|
|
63
|
+
durationMs: Date.now() - startedAt,
|
|
64
|
+
tags: reportOptions?.tags,
|
|
65
|
+
contexts: reportOptions?.contexts,
|
|
66
|
+
extra: reportOptions?.extra,
|
|
67
|
+
mechanism: reportOptions?.mechanism,
|
|
68
|
+
handled: reportOptions?.handled,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
return result(id);
|
|
72
|
+
},
|
|
73
|
+
setUser(user) {
|
|
74
|
+
client.setUser(toSentryUser(user));
|
|
75
|
+
},
|
|
76
|
+
setTags(tags) {
|
|
77
|
+
client.setTags(normalizeTags(tags));
|
|
78
|
+
},
|
|
79
|
+
setContext(name, context) {
|
|
80
|
+
client.setContext(name, context);
|
|
81
|
+
},
|
|
82
|
+
async flush(flushOptions) {
|
|
83
|
+
return Boolean(await client.flush(flushOptions?.timeoutMs));
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create a Beignet provider that contributes `ports.errorReporter`.
|
|
89
|
+
*/
|
|
90
|
+
export function createSentryErrorReportingProvider(options = {}) {
|
|
91
|
+
return createProvider({
|
|
92
|
+
name: "error-reporting-sentry",
|
|
93
|
+
metadata: {
|
|
94
|
+
packageName: "@beignet/provider-error-reporting-sentry",
|
|
95
|
+
ports: ["errorReporter", "sentry"],
|
|
96
|
+
watchers: ["errors"],
|
|
97
|
+
},
|
|
98
|
+
setup({ ports }) {
|
|
99
|
+
const client = options.client ?? Sentry;
|
|
100
|
+
initializeSentry(client, options);
|
|
101
|
+
return {
|
|
102
|
+
ports: {
|
|
103
|
+
errorReporter: createSentryErrorReporter({
|
|
104
|
+
client,
|
|
105
|
+
instrumentation: ports,
|
|
106
|
+
}),
|
|
107
|
+
sentry: {
|
|
108
|
+
client,
|
|
109
|
+
sdk: Sentry,
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
async stop() {
|
|
113
|
+
if (options.closeOnStop && client.close) {
|
|
114
|
+
await client.close(options.flushTimeoutMs ?? 2000);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Ready-to-register Sentry error reporting provider using the default Sentry
|
|
123
|
+
* singleton.
|
|
124
|
+
*/
|
|
125
|
+
export const sentryErrorReportingProvider = createSentryErrorReportingProvider();
|
|
126
|
+
function initializeSentry(client, options) {
|
|
127
|
+
if (!client.init)
|
|
128
|
+
return;
|
|
129
|
+
const dsn = options.dsn ?? process.env.SENTRY_DSN;
|
|
130
|
+
if (!dsn && !options.init)
|
|
131
|
+
return;
|
|
132
|
+
client.init({
|
|
133
|
+
...options.init,
|
|
134
|
+
dsn,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function withConfiguredScope(client, options, capture) {
|
|
138
|
+
return client.withScope((scope) => {
|
|
139
|
+
applyScope(scope, options);
|
|
140
|
+
return capture();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
function applyScope(scope, options) {
|
|
144
|
+
if (!options)
|
|
145
|
+
return;
|
|
146
|
+
if (options.level)
|
|
147
|
+
scope.setLevel(toSentryLevel(options.level));
|
|
148
|
+
if (options.user !== undefined)
|
|
149
|
+
scope.setUser(toSentryUser(options.user));
|
|
150
|
+
if (options.tags)
|
|
151
|
+
scope.setTags(normalizeTags(options.tags));
|
|
152
|
+
if (options.extra)
|
|
153
|
+
scope.setExtras(stripUndefined(options.extra));
|
|
154
|
+
if (options.fingerprint)
|
|
155
|
+
scope.setFingerprint([...options.fingerprint]);
|
|
156
|
+
for (const [name, context] of Object.entries(options.contexts ?? {})) {
|
|
157
|
+
if (context !== undefined) {
|
|
158
|
+
scope.setContext(name, stripUndefined(context));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
scope.setContext("beignet", stripUndefined({
|
|
162
|
+
requestId: options.requestId,
|
|
163
|
+
traceId: options.traceId,
|
|
164
|
+
spanId: options.spanId,
|
|
165
|
+
parentSpanId: options.parentSpanId,
|
|
166
|
+
traceparent: options.traceparent,
|
|
167
|
+
mechanism: options.mechanism,
|
|
168
|
+
handled: options.handled,
|
|
169
|
+
}));
|
|
170
|
+
}
|
|
171
|
+
function toSentryLevel(level) {
|
|
172
|
+
return level === "warning" ? "warning" : level;
|
|
173
|
+
}
|
|
174
|
+
function normalizeTags(tags) {
|
|
175
|
+
return Object.fromEntries(Object.entries(tags)
|
|
176
|
+
.filter(([, value]) => value !== undefined)
|
|
177
|
+
.map(([key, value]) => [key, String(value)]));
|
|
178
|
+
}
|
|
179
|
+
function toSentryUser(user) {
|
|
180
|
+
if (user === null)
|
|
181
|
+
return null;
|
|
182
|
+
const { ipAddress, ...rest } = user;
|
|
183
|
+
return stripUndefined({
|
|
184
|
+
...rest,
|
|
185
|
+
ip_address: ipAddress,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
function stripUndefined(value) {
|
|
189
|
+
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
190
|
+
}
|
|
191
|
+
function errorMessage(error) {
|
|
192
|
+
return error instanceof Error ? error.message : String(error);
|
|
193
|
+
}
|
|
194
|
+
function errorStack(error) {
|
|
195
|
+
return error instanceof Error ? error.stack : undefined;
|
|
196
|
+
}
|
|
197
|
+
function result(id) {
|
|
198
|
+
return id ? { id } : {};
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@beignet/provider-error-reporting-sentry",
|
|
3
|
+
"version": "0.0.14",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Sentry error reporting provider for Beignet",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"default": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"dist",
|
|
16
|
+
"src",
|
|
17
|
+
"!src/**/*.test.ts",
|
|
18
|
+
"!src/**/*.test.tsx",
|
|
19
|
+
"!src/**/*.test-d.ts",
|
|
20
|
+
"README.md",
|
|
21
|
+
"CHANGELOG.md"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"dev": "tsc --watch",
|
|
26
|
+
"clean": "rm -rf dist coverage .turbo",
|
|
27
|
+
"test": "bun test",
|
|
28
|
+
"test:coverage": "bun test --coverage",
|
|
29
|
+
"lint": "biome check ."
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"beignet",
|
|
33
|
+
"sentry",
|
|
34
|
+
"error-reporting",
|
|
35
|
+
"provider",
|
|
36
|
+
"ports"
|
|
37
|
+
],
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/taylorbryant/beignet.git",
|
|
42
|
+
"directory": "packages/provider-error-reporting-sentry"
|
|
43
|
+
},
|
|
44
|
+
"author": "Taylor Bryant",
|
|
45
|
+
"homepage": "https://github.com/taylorbryant/beignet#readme",
|
|
46
|
+
"bugs": "https://github.com/taylorbryant/beignet/issues",
|
|
47
|
+
"sideEffects": false,
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"access": "public"
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=18.0.0"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"@beignet/core": ">=0.0.3 <1.0.0",
|
|
56
|
+
"@sentry/node": "^10.58.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@beignet/core": "*",
|
|
60
|
+
"@beignet/devtools": "*",
|
|
61
|
+
"@sentry/node": "^10.58.0",
|
|
62
|
+
"@types/bun": "^1.3.13",
|
|
63
|
+
"@types/node": "^20.10.0",
|
|
64
|
+
"typescript": "^5.3.0"
|
|
65
|
+
},
|
|
66
|
+
"beignet": {
|
|
67
|
+
"provider": {
|
|
68
|
+
"displayName": "Sentry error reporting provider",
|
|
69
|
+
"ports": [
|
|
70
|
+
"errorReporter",
|
|
71
|
+
"sentry"
|
|
72
|
+
],
|
|
73
|
+
"appPorts": [
|
|
74
|
+
{
|
|
75
|
+
"name": "errorReporter",
|
|
76
|
+
"type": "ErrorReporterPort"
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"env": [
|
|
80
|
+
"SENTRY_DSN"
|
|
81
|
+
],
|
|
82
|
+
"requiredEnv": [],
|
|
83
|
+
"watchers": [
|
|
84
|
+
"errors"
|
|
85
|
+
],
|
|
86
|
+
"registration": {
|
|
87
|
+
"required": true,
|
|
88
|
+
"tokens": [
|
|
89
|
+
"sentryErrorReportingProvider",
|
|
90
|
+
"createSentryErrorReportingProvider"
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @beignet/provider-error-reporting-sentry
|
|
3
|
+
*
|
|
4
|
+
* Sentry-backed error reporting provider for Beignet.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type {
|
|
8
|
+
ErrorReportContext,
|
|
9
|
+
ErrorReporterFlushOptions,
|
|
10
|
+
ErrorReporterPort,
|
|
11
|
+
ErrorReportJsonValue,
|
|
12
|
+
ErrorReportLevel,
|
|
13
|
+
ErrorReportOptions,
|
|
14
|
+
ErrorReportResult,
|
|
15
|
+
ErrorReportTags,
|
|
16
|
+
ErrorReportUser,
|
|
17
|
+
} from "@beignet/core/error-reporting";
|
|
18
|
+
import {
|
|
19
|
+
createProvider,
|
|
20
|
+
createProviderInstrumentation,
|
|
21
|
+
type ProviderInstrumentationTarget,
|
|
22
|
+
} from "@beignet/core/providers";
|
|
23
|
+
import type { NodeOptions, Scope, SeverityLevel } from "@sentry/node";
|
|
24
|
+
import * as Sentry from "@sentry/node";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sentry SDK surface used by the provider. Supplying this in tests avoids the
|
|
28
|
+
* process-wide singleton.
|
|
29
|
+
*/
|
|
30
|
+
export interface SentryErrorReportingClient {
|
|
31
|
+
captureException(error: unknown): string | undefined;
|
|
32
|
+
captureMessage(message: string): string | undefined;
|
|
33
|
+
setUser(user: SentryErrorReportingUser | null): void;
|
|
34
|
+
setTags(tags: Record<string, string>): void;
|
|
35
|
+
setContext(name: string, context: ErrorReportContext | null): void;
|
|
36
|
+
flush(timeout?: number): PromiseLike<boolean> | boolean;
|
|
37
|
+
close?(timeout?: number): PromiseLike<boolean> | boolean;
|
|
38
|
+
withScope<T>(callback: (scope: Scope) => T): T;
|
|
39
|
+
init?(options?: NodeOptions): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Escape hatch for apps that need the raw Sentry SDK.
|
|
44
|
+
*/
|
|
45
|
+
export interface SentryErrorReportingEscapeHatch {
|
|
46
|
+
client: SentryErrorReportingClient;
|
|
47
|
+
sdk: typeof Sentry;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export type SentryErrorReportingUser = Record<
|
|
51
|
+
string,
|
|
52
|
+
ErrorReportJsonValue | undefined
|
|
53
|
+
>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Ports contributed by the Sentry provider.
|
|
57
|
+
*/
|
|
58
|
+
export interface SentryErrorReportingProviderPorts {
|
|
59
|
+
errorReporter: ErrorReporterPort;
|
|
60
|
+
sentry: SentryErrorReportingEscapeHatch;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Options for the direct Sentry reporter factory.
|
|
65
|
+
*/
|
|
66
|
+
export interface CreateSentryErrorReporterOptions {
|
|
67
|
+
client?: SentryErrorReportingClient;
|
|
68
|
+
instrumentation?: ProviderInstrumentationTarget;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Options for the Sentry lifecycle provider.
|
|
73
|
+
*/
|
|
74
|
+
export interface CreateSentryErrorReportingProviderOptions {
|
|
75
|
+
client?: SentryErrorReportingClient;
|
|
76
|
+
/**
|
|
77
|
+
* Sentry DSN. When omitted, the provider checks `process.env.SENTRY_DSN`.
|
|
78
|
+
* With no DSN, the SDK is not initialized and captures are effectively no-op
|
|
79
|
+
* until the app initializes Sentry elsewhere.
|
|
80
|
+
*/
|
|
81
|
+
dsn?: string;
|
|
82
|
+
/**
|
|
83
|
+
* Additional Sentry initialization options.
|
|
84
|
+
*/
|
|
85
|
+
init?: Omit<NodeOptions, "dsn">;
|
|
86
|
+
/**
|
|
87
|
+
* Close the Sentry client on server stop.
|
|
88
|
+
*
|
|
89
|
+
* @default false
|
|
90
|
+
*/
|
|
91
|
+
closeOnStop?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Flush/close timeout in milliseconds.
|
|
94
|
+
*
|
|
95
|
+
* @default 2000
|
|
96
|
+
*/
|
|
97
|
+
flushTimeoutMs?: number;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a Beignet error reporter backed by Sentry.
|
|
102
|
+
*/
|
|
103
|
+
export function createSentryErrorReporter(
|
|
104
|
+
options: CreateSentryErrorReporterOptions = {},
|
|
105
|
+
): ErrorReporterPort {
|
|
106
|
+
const client = options.client ?? Sentry;
|
|
107
|
+
const instrumentation = createProviderInstrumentation(
|
|
108
|
+
options.instrumentation,
|
|
109
|
+
{
|
|
110
|
+
providerName: "error-reporting-sentry",
|
|
111
|
+
watcher: "errors",
|
|
112
|
+
},
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
async captureException(error, reportOptions) {
|
|
117
|
+
const startedAt = Date.now();
|
|
118
|
+
const id = withConfiguredScope(client, reportOptions, () =>
|
|
119
|
+
client.captureException(error),
|
|
120
|
+
);
|
|
121
|
+
instrumentation.record({
|
|
122
|
+
type: "error",
|
|
123
|
+
watcher: "errors",
|
|
124
|
+
owner: "provider",
|
|
125
|
+
message: errorMessage(error),
|
|
126
|
+
stack: errorStack(error),
|
|
127
|
+
requestId: reportOptions?.requestId,
|
|
128
|
+
traceId: reportOptions?.traceId,
|
|
129
|
+
spanId: reportOptions?.spanId,
|
|
130
|
+
parentSpanId: reportOptions?.parentSpanId,
|
|
131
|
+
traceparent: reportOptions?.traceparent,
|
|
132
|
+
details: {
|
|
133
|
+
provider: "sentry",
|
|
134
|
+
eventId: id,
|
|
135
|
+
level: reportOptions?.level ?? "error",
|
|
136
|
+
durationMs: Date.now() - startedAt,
|
|
137
|
+
tags: reportOptions?.tags,
|
|
138
|
+
contexts: reportOptions?.contexts,
|
|
139
|
+
extra: reportOptions?.extra,
|
|
140
|
+
mechanism: reportOptions?.mechanism,
|
|
141
|
+
handled: reportOptions?.handled,
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
return result(id);
|
|
145
|
+
},
|
|
146
|
+
async captureMessage(message, reportOptions) {
|
|
147
|
+
const startedAt = Date.now();
|
|
148
|
+
const id = withConfiguredScope(client, reportOptions, () =>
|
|
149
|
+
client.captureMessage(message),
|
|
150
|
+
);
|
|
151
|
+
instrumentation.record({
|
|
152
|
+
type: "error",
|
|
153
|
+
watcher: "errors",
|
|
154
|
+
owner: "provider",
|
|
155
|
+
message,
|
|
156
|
+
requestId: reportOptions?.requestId,
|
|
157
|
+
traceId: reportOptions?.traceId,
|
|
158
|
+
spanId: reportOptions?.spanId,
|
|
159
|
+
parentSpanId: reportOptions?.parentSpanId,
|
|
160
|
+
traceparent: reportOptions?.traceparent,
|
|
161
|
+
details: {
|
|
162
|
+
provider: "sentry",
|
|
163
|
+
eventId: id,
|
|
164
|
+
level: reportOptions?.level ?? "error",
|
|
165
|
+
durationMs: Date.now() - startedAt,
|
|
166
|
+
tags: reportOptions?.tags,
|
|
167
|
+
contexts: reportOptions?.contexts,
|
|
168
|
+
extra: reportOptions?.extra,
|
|
169
|
+
mechanism: reportOptions?.mechanism,
|
|
170
|
+
handled: reportOptions?.handled,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
return result(id);
|
|
174
|
+
},
|
|
175
|
+
setUser(user) {
|
|
176
|
+
client.setUser(toSentryUser(user));
|
|
177
|
+
},
|
|
178
|
+
setTags(tags) {
|
|
179
|
+
client.setTags(normalizeTags(tags));
|
|
180
|
+
},
|
|
181
|
+
setContext(name, context) {
|
|
182
|
+
client.setContext(name, context);
|
|
183
|
+
},
|
|
184
|
+
async flush(flushOptions) {
|
|
185
|
+
return Boolean(await client.flush(flushOptions?.timeoutMs));
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Create a Beignet provider that contributes `ports.errorReporter`.
|
|
192
|
+
*/
|
|
193
|
+
export function createSentryErrorReportingProvider(
|
|
194
|
+
options: CreateSentryErrorReportingProviderOptions = {},
|
|
195
|
+
) {
|
|
196
|
+
return createProvider({
|
|
197
|
+
name: "error-reporting-sentry",
|
|
198
|
+
metadata: {
|
|
199
|
+
packageName: "@beignet/provider-error-reporting-sentry",
|
|
200
|
+
ports: ["errorReporter", "sentry"],
|
|
201
|
+
watchers: ["errors"],
|
|
202
|
+
},
|
|
203
|
+
setup({ ports }) {
|
|
204
|
+
const client = options.client ?? Sentry;
|
|
205
|
+
initializeSentry(client, options);
|
|
206
|
+
|
|
207
|
+
return {
|
|
208
|
+
ports: {
|
|
209
|
+
errorReporter: createSentryErrorReporter({
|
|
210
|
+
client,
|
|
211
|
+
instrumentation: ports,
|
|
212
|
+
}),
|
|
213
|
+
sentry: {
|
|
214
|
+
client,
|
|
215
|
+
sdk: Sentry,
|
|
216
|
+
},
|
|
217
|
+
} satisfies SentryErrorReportingProviderPorts,
|
|
218
|
+
async stop() {
|
|
219
|
+
if (options.closeOnStop && client.close) {
|
|
220
|
+
await client.close(options.flushTimeoutMs ?? 2000);
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Ready-to-register Sentry error reporting provider using the default Sentry
|
|
230
|
+
* singleton.
|
|
231
|
+
*/
|
|
232
|
+
export const sentryErrorReportingProvider =
|
|
233
|
+
createSentryErrorReportingProvider();
|
|
234
|
+
|
|
235
|
+
function initializeSentry(
|
|
236
|
+
client: SentryErrorReportingClient,
|
|
237
|
+
options: CreateSentryErrorReportingProviderOptions,
|
|
238
|
+
) {
|
|
239
|
+
if (!client.init) return;
|
|
240
|
+
const dsn = options.dsn ?? process.env.SENTRY_DSN;
|
|
241
|
+
if (!dsn && !options.init) return;
|
|
242
|
+
|
|
243
|
+
client.init({
|
|
244
|
+
...options.init,
|
|
245
|
+
dsn,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function withConfiguredScope<T>(
|
|
250
|
+
client: SentryErrorReportingClient,
|
|
251
|
+
options: ErrorReportOptions | undefined,
|
|
252
|
+
capture: () => T,
|
|
253
|
+
): T {
|
|
254
|
+
return client.withScope((scope) => {
|
|
255
|
+
applyScope(scope, options);
|
|
256
|
+
return capture();
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function applyScope(scope: Scope, options: ErrorReportOptions | undefined) {
|
|
261
|
+
if (!options) return;
|
|
262
|
+
if (options.level) scope.setLevel(toSentryLevel(options.level));
|
|
263
|
+
if (options.user !== undefined) scope.setUser(toSentryUser(options.user));
|
|
264
|
+
if (options.tags) scope.setTags(normalizeTags(options.tags));
|
|
265
|
+
if (options.extra) scope.setExtras(stripUndefined(options.extra));
|
|
266
|
+
if (options.fingerprint) scope.setFingerprint([...options.fingerprint]);
|
|
267
|
+
|
|
268
|
+
for (const [name, context] of Object.entries(options.contexts ?? {})) {
|
|
269
|
+
if (context !== undefined) {
|
|
270
|
+
scope.setContext(name, stripUndefined(context));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
scope.setContext(
|
|
275
|
+
"beignet",
|
|
276
|
+
stripUndefined({
|
|
277
|
+
requestId: options.requestId,
|
|
278
|
+
traceId: options.traceId,
|
|
279
|
+
spanId: options.spanId,
|
|
280
|
+
parentSpanId: options.parentSpanId,
|
|
281
|
+
traceparent: options.traceparent,
|
|
282
|
+
mechanism: options.mechanism,
|
|
283
|
+
handled: options.handled,
|
|
284
|
+
}),
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function toSentryLevel(level: ErrorReportLevel): SeverityLevel {
|
|
289
|
+
return level === "warning" ? "warning" : level;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function normalizeTags(tags: ErrorReportTags): Record<string, string> {
|
|
293
|
+
return Object.fromEntries(
|
|
294
|
+
Object.entries(tags)
|
|
295
|
+
.filter(([, value]) => value !== undefined)
|
|
296
|
+
.map(([key, value]) => [key, String(value)]),
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function toSentryUser(
|
|
301
|
+
user: ErrorReportUser | null,
|
|
302
|
+
): SentryErrorReportingUser | null {
|
|
303
|
+
if (user === null) return null;
|
|
304
|
+
const { ipAddress, ...rest } = user;
|
|
305
|
+
|
|
306
|
+
return stripUndefined({
|
|
307
|
+
...rest,
|
|
308
|
+
ip_address: ipAddress,
|
|
309
|
+
}) as SentryErrorReportingUser;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function stripUndefined(
|
|
313
|
+
value: Record<string, unknown>,
|
|
314
|
+
): Record<string, unknown> {
|
|
315
|
+
return Object.fromEntries(
|
|
316
|
+
Object.entries(value).filter(([, entry]) => entry !== undefined),
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function errorMessage(error: unknown): string {
|
|
321
|
+
return error instanceof Error ? error.message : String(error);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function errorStack(error: unknown): string | undefined {
|
|
325
|
+
return error instanceof Error ? error.stack : undefined;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function result(id: string | undefined): ErrorReportResult {
|
|
329
|
+
return id ? { id } : {};
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export type {
|
|
333
|
+
ErrorReportContext,
|
|
334
|
+
ErrorReporterFlushOptions,
|
|
335
|
+
ErrorReporterPort,
|
|
336
|
+
ErrorReportJsonValue,
|
|
337
|
+
ErrorReportLevel,
|
|
338
|
+
ErrorReportOptions,
|
|
339
|
+
ErrorReportResult,
|
|
340
|
+
ErrorReportTags,
|
|
341
|
+
ErrorReportUser,
|
|
342
|
+
};
|