@statoss/sdk 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +411 -2
- package/dist/cjs/client.d.ts +50 -0
- package/dist/cjs/client.js +462 -0
- package/dist/cjs/dsn.d.ts +25 -0
- package/dist/cjs/dsn.js +62 -0
- package/dist/cjs/envelope.d.ts +36 -0
- package/dist/cjs/envelope.js +55 -0
- package/dist/cjs/event.d.ts +9 -0
- package/dist/cjs/event.js +128 -0
- package/dist/cjs/hub.d.ts +37 -0
- package/dist/cjs/hub.js +119 -0
- package/dist/cjs/index.d.ts +38 -0
- package/dist/cjs/index.js +107 -0
- package/dist/cjs/integrations/browser.d.ts +7 -0
- package/dist/cjs/integrations/browser.js +124 -0
- package/dist/cjs/integrations/console.d.ts +3 -0
- package/dist/cjs/integrations/console.js +106 -0
- package/dist/cjs/integrations/dedupe.d.ts +7 -0
- package/dist/cjs/integrations/dedupe.js +32 -0
- package/dist/cjs/integrations/fetch.d.ts +3 -0
- package/dist/cjs/integrations/fetch.js +69 -0
- package/dist/cjs/integrations/handlers.d.ts +9 -0
- package/dist/cjs/integrations/handlers.js +192 -0
- package/dist/cjs/levels.d.ts +20 -0
- package/dist/cjs/levels.js +77 -0
- package/dist/cjs/logger.d.ts +18 -0
- package/dist/cjs/logger.js +38 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/ratelimit.d.ts +11 -0
- package/dist/cjs/ratelimit.js +50 -0
- package/dist/cjs/runtime.d.ts +42 -0
- package/dist/cjs/runtime.js +188 -0
- package/dist/cjs/scope.d.ts +30 -0
- package/dist/cjs/scope.js +127 -0
- package/dist/cjs/stack.d.ts +9 -0
- package/dist/cjs/stack.js +129 -0
- package/dist/cjs/transport.d.ts +40 -0
- package/dist/cjs/transport.js +218 -0
- package/dist/cjs/types.d.ts +180 -0
- package/dist/cjs/types.js +2 -0
- package/dist/cjs/utils.d.ts +27 -0
- package/dist/cjs/utils.js +227 -0
- package/dist/cjs/version.d.ts +3 -0
- package/dist/cjs/version.js +7 -0
- package/dist/esm/client.d.ts +50 -0
- package/dist/esm/client.js +458 -0
- package/dist/esm/dsn.d.ts +25 -0
- package/dist/esm/dsn.js +55 -0
- package/dist/esm/envelope.d.ts +36 -0
- package/dist/esm/envelope.js +49 -0
- package/dist/esm/event.d.ts +9 -0
- package/dist/esm/event.js +124 -0
- package/dist/esm/hub.d.ts +37 -0
- package/dist/esm/hub.js +111 -0
- package/dist/esm/index.d.ts +38 -0
- package/dist/esm/index.js +88 -0
- package/dist/esm/integrations/browser.d.ts +7 -0
- package/dist/esm/integrations/browser.js +119 -0
- package/dist/esm/integrations/console.d.ts +3 -0
- package/dist/esm/integrations/console.js +103 -0
- package/dist/esm/integrations/dedupe.d.ts +7 -0
- package/dist/esm/integrations/dedupe.js +29 -0
- package/dist/esm/integrations/fetch.d.ts +3 -0
- package/dist/esm/integrations/fetch.js +66 -0
- package/dist/esm/integrations/handlers.d.ts +9 -0
- package/dist/esm/integrations/handlers.js +188 -0
- package/dist/esm/levels.d.ts +20 -0
- package/dist/esm/levels.js +69 -0
- package/dist/esm/logger.d.ts +18 -0
- package/dist/esm/logger.js +33 -0
- package/dist/esm/ratelimit.d.ts +11 -0
- package/dist/esm/ratelimit.js +45 -0
- package/dist/esm/runtime.d.ts +42 -0
- package/dist/esm/runtime.js +181 -0
- package/dist/esm/scope.d.ts +30 -0
- package/dist/esm/scope.js +123 -0
- package/dist/esm/stack.d.ts +9 -0
- package/dist/esm/stack.js +125 -0
- package/dist/esm/transport.d.ts +40 -0
- package/dist/esm/transport.js +213 -0
- package/dist/esm/types.d.ts +180 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/utils.d.ts +27 -0
- package/dist/esm/utils.js +211 -0
- package/dist/esm/version.d.ts +3 -0
- package/dist/esm/version.js +4 -0
- package/package.json +48 -4
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.eventFromUnknown = eventFromUnknown;
|
|
4
|
+
exports.addInitialFrame = addInitialFrame;
|
|
5
|
+
const stack_js_1 = require("./stack.js");
|
|
6
|
+
const utils_js_1 = require("./utils.js");
|
|
7
|
+
const MAX_CAUSES = 5;
|
|
8
|
+
function exceptionFromError(error, mechanism) {
|
|
9
|
+
const name = (0, utils_js_1.safely)(() => error.name);
|
|
10
|
+
const message = (0, utils_js_1.safely)(() => error.message);
|
|
11
|
+
const exception = {
|
|
12
|
+
type: typeof name === "string" && name ? name : "Error",
|
|
13
|
+
value: typeof message === "string" ? (0, utils_js_1.truncate)(message) : "",
|
|
14
|
+
mechanism,
|
|
15
|
+
};
|
|
16
|
+
const frames = (0, stack_js_1.parseStack)((0, utils_js_1.safely)(() => error.stack));
|
|
17
|
+
if (frames.length)
|
|
18
|
+
exception.stacktrace = { frames };
|
|
19
|
+
return exception;
|
|
20
|
+
}
|
|
21
|
+
/** The error and its causes, as Sentry orders them: the thrown one last. */
|
|
22
|
+
function exceptionChain(error, mechanism) {
|
|
23
|
+
const chain = [];
|
|
24
|
+
let current = error;
|
|
25
|
+
while ((0, utils_js_1.isErrorLike)(current) &&
|
|
26
|
+
chain.length < MAX_CAUSES &&
|
|
27
|
+
!chain.includes(current)) {
|
|
28
|
+
chain.push(current);
|
|
29
|
+
current = (0, utils_js_1.safely)(() => current.cause);
|
|
30
|
+
}
|
|
31
|
+
const values = chain.map((e, i) => exceptionFromError(e, i === 0
|
|
32
|
+
? chain.length > 1
|
|
33
|
+
? { ...mechanism, exception_id: 0 }
|
|
34
|
+
: mechanism
|
|
35
|
+
: {
|
|
36
|
+
type: "chained",
|
|
37
|
+
handled: mechanism.handled,
|
|
38
|
+
source: "cause",
|
|
39
|
+
exception_id: i,
|
|
40
|
+
parent_id: i - 1,
|
|
41
|
+
}));
|
|
42
|
+
return values.reverse();
|
|
43
|
+
}
|
|
44
|
+
function keysForMessage(value) {
|
|
45
|
+
// Only the first keys: the message holds 40 characters of them.
|
|
46
|
+
const keys = [];
|
|
47
|
+
for (const key in value) {
|
|
48
|
+
if (keys.length >= 100)
|
|
49
|
+
break;
|
|
50
|
+
if (Object.prototype.hasOwnProperty.call(value, key))
|
|
51
|
+
keys.push(key);
|
|
52
|
+
}
|
|
53
|
+
keys.sort();
|
|
54
|
+
let out = "";
|
|
55
|
+
for (const key of keys) {
|
|
56
|
+
const next = out ? `${out}, ${key}` : key;
|
|
57
|
+
if (next.length > 40)
|
|
58
|
+
return out || (0, utils_js_1.truncate)(key, 40);
|
|
59
|
+
out = next;
|
|
60
|
+
}
|
|
61
|
+
return out || "[none]";
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Builds an event from anything thrown or rejected, as Sentry's SDKs do:
|
|
65
|
+
* errors with their stacks and causes, other objects and plain values as
|
|
66
|
+
* synthetic exceptions.
|
|
67
|
+
*/
|
|
68
|
+
function eventFromUnknown(input, mechanism, rejection = false) {
|
|
69
|
+
let value = input;
|
|
70
|
+
const tag = (0, utils_js_1.toStringTag)(value);
|
|
71
|
+
if (tag === "[object ErrorEvent]" &&
|
|
72
|
+
(0, utils_js_1.isObject)(value) &&
|
|
73
|
+
(0, utils_js_1.isErrorLike)(value.error)) {
|
|
74
|
+
value = value.error;
|
|
75
|
+
}
|
|
76
|
+
else if (tag === "[object PromiseRejectionEvent]" &&
|
|
77
|
+
(0, utils_js_1.isObject)(value) &&
|
|
78
|
+
"reason" in value) {
|
|
79
|
+
value = value.reason;
|
|
80
|
+
}
|
|
81
|
+
if ((0, utils_js_1.isErrorLike)(value)) {
|
|
82
|
+
return { exception: { values: exceptionChain(value, mechanism) } };
|
|
83
|
+
}
|
|
84
|
+
const synthetic = { ...mechanism, synthetic: true };
|
|
85
|
+
if ((0, utils_js_1.isObject)(value)) {
|
|
86
|
+
const name = (0, utils_js_1.safely)(() => value.name);
|
|
87
|
+
const message = (0, utils_js_1.safely)(() => value.message);
|
|
88
|
+
return {
|
|
89
|
+
exception: {
|
|
90
|
+
values: [
|
|
91
|
+
{
|
|
92
|
+
type: typeof name === "string" && name
|
|
93
|
+
? name
|
|
94
|
+
: rejection
|
|
95
|
+
? "UnhandledRejection"
|
|
96
|
+
: "Error",
|
|
97
|
+
value: typeof message === "string"
|
|
98
|
+
? (0, utils_js_1.truncate)(message)
|
|
99
|
+
: `Object captured as ${rejection ? "promise rejection" : "exception"} ${(0, utils_js_1.summarize)(value) ?? `with keys: ${keysForMessage(value)}`}`,
|
|
100
|
+
mechanism: synthetic,
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
extra: { __serialized__: (0, utils_js_1.normalize)(value, 3) },
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
const text = (0, utils_js_1.safeString)(value);
|
|
108
|
+
return {
|
|
109
|
+
exception: {
|
|
110
|
+
values: [
|
|
111
|
+
{
|
|
112
|
+
type: rejection ? "UnhandledRejection" : "Error",
|
|
113
|
+
value: (0, utils_js_1.truncate)(rejection
|
|
114
|
+
? `Non-Error promise rejection captured with value: ${text}`
|
|
115
|
+
: text),
|
|
116
|
+
mechanism: synthetic,
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
/** For a browser error event that carried no Error: the file and line it names. */
|
|
123
|
+
function addInitialFrame(event, frame) {
|
|
124
|
+
const first = event.exception?.values[0];
|
|
125
|
+
if (first && !first.stacktrace)
|
|
126
|
+
first.stacktrace = { frames: [frame] };
|
|
127
|
+
return event;
|
|
128
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Client } from "./client.js";
|
|
2
|
+
import { Scope } from "./scope.js";
|
|
3
|
+
import type { CaptureContext, Level } from "./types.js";
|
|
4
|
+
interface ScopeStorage {
|
|
5
|
+
getStore(): Scope | undefined;
|
|
6
|
+
run<T>(store: Scope, callback: () => T): T;
|
|
7
|
+
}
|
|
8
|
+
export interface Carrier {
|
|
9
|
+
client?: Client;
|
|
10
|
+
root: Scope;
|
|
11
|
+
/** Without AsyncLocalStorage: the scopes of withScope callbacks still running synchronously. */
|
|
12
|
+
stack: Scope[];
|
|
13
|
+
/** Undefined until first needed, null when the runtime has none. */
|
|
14
|
+
storage?: ScopeStorage | null;
|
|
15
|
+
warnedNoStorage?: boolean;
|
|
16
|
+
lastEventId?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function getCarrier(): Carrier;
|
|
19
|
+
export declare function getClient(): Client | undefined;
|
|
20
|
+
export declare function getCurrentScope(): Scope;
|
|
21
|
+
/**
|
|
22
|
+
* Runs callback with a copy of the current scope, so what it sets applies only
|
|
23
|
+
* to events captured inside it.
|
|
24
|
+
*
|
|
25
|
+
* With AsyncLocalStorage (Node 20.16+ and 22.3+, Bun, Deno, Vercel Edge,
|
|
26
|
+
* Workers with nodejs_compat) the copy follows the callback's async work.
|
|
27
|
+
* Without it (browsers, Workers without nodejs_compat) the copy is current
|
|
28
|
+
* only until the callback first awaits: it is never left current for other
|
|
29
|
+
* work to pick up, which would send one request's user with another's error.
|
|
30
|
+
* After an await, capture through the scope argument (scope.captureException).
|
|
31
|
+
*/
|
|
32
|
+
export declare function withScope<T>(callback: (scope: Scope) => T): T;
|
|
33
|
+
/** Runs a capture against the client with the given scope, or the current one. */
|
|
34
|
+
export declare function captureWith(scope: Scope | undefined, run: (client: Client, scope: Scope) => string): string;
|
|
35
|
+
/** captureMessage's second argument: a level or a context. */
|
|
36
|
+
export declare function messageContext(levelOrContext: Level | CaptureContext | undefined): CaptureContext | undefined;
|
|
37
|
+
export {};
|
package/dist/cjs/hub.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getCarrier = getCarrier;
|
|
4
|
+
exports.getClient = getClient;
|
|
5
|
+
exports.getCurrentScope = getCurrentScope;
|
|
6
|
+
exports.withScope = withScope;
|
|
7
|
+
exports.captureWith = captureWith;
|
|
8
|
+
exports.messageContext = messageContext;
|
|
9
|
+
const runtime_js_1 = require("./runtime.js");
|
|
10
|
+
const scope_js_1 = require("./scope.js");
|
|
11
|
+
const utils_js_1 = require("./utils.js");
|
|
12
|
+
// On globalThis so the ESM and CJS builds, if both get loaded, share one SDK.
|
|
13
|
+
const KEY = "__statossSdk";
|
|
14
|
+
let fallback;
|
|
15
|
+
function getCarrier() {
|
|
16
|
+
const g = globalThis;
|
|
17
|
+
const existing = g[KEY] ?? fallback;
|
|
18
|
+
if (existing)
|
|
19
|
+
return existing;
|
|
20
|
+
const carrier = { root: new scope_js_1.Scope(), stack: [] };
|
|
21
|
+
try {
|
|
22
|
+
Object.defineProperty(g, KEY, {
|
|
23
|
+
value: carrier,
|
|
24
|
+
configurable: true,
|
|
25
|
+
enumerable: false,
|
|
26
|
+
writable: true,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
fallback = carrier;
|
|
31
|
+
}
|
|
32
|
+
return carrier;
|
|
33
|
+
}
|
|
34
|
+
function getClient() {
|
|
35
|
+
return getCarrier().client;
|
|
36
|
+
}
|
|
37
|
+
function getStorage(carrier) {
|
|
38
|
+
if (carrier.storage !== undefined)
|
|
39
|
+
return carrier.storage;
|
|
40
|
+
let storage = null;
|
|
41
|
+
try {
|
|
42
|
+
// Vercel Edge and Next.js edge expose it as a global; Node, Bun and Deno through node:async_hooks.
|
|
43
|
+
const global = globalThis
|
|
44
|
+
.AsyncLocalStorage;
|
|
45
|
+
const Ctor = typeof global === "function"
|
|
46
|
+
? global
|
|
47
|
+
: (0, runtime_js_1.getBuiltin)("node:async_hooks")
|
|
48
|
+
?.AsyncLocalStorage;
|
|
49
|
+
if (typeof Ctor === "function")
|
|
50
|
+
storage = new Ctor();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
storage = null;
|
|
54
|
+
}
|
|
55
|
+
carrier.storage = storage;
|
|
56
|
+
return storage;
|
|
57
|
+
}
|
|
58
|
+
function getCurrentScope() {
|
|
59
|
+
const carrier = getCarrier();
|
|
60
|
+
const fromStorage = carrier.storage ? carrier.storage.getStore() : undefined;
|
|
61
|
+
if (fromStorage)
|
|
62
|
+
return fromStorage;
|
|
63
|
+
return carrier.stack.length > 0
|
|
64
|
+
? carrier.stack[carrier.stack.length - 1]
|
|
65
|
+
: carrier.root;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Runs callback with a copy of the current scope, so what it sets applies only
|
|
69
|
+
* to events captured inside it.
|
|
70
|
+
*
|
|
71
|
+
* With AsyncLocalStorage (Node 20.16+ and 22.3+, Bun, Deno, Vercel Edge,
|
|
72
|
+
* Workers with nodejs_compat) the copy follows the callback's async work.
|
|
73
|
+
* Without it (browsers, Workers without nodejs_compat) the copy is current
|
|
74
|
+
* only until the callback first awaits: it is never left current for other
|
|
75
|
+
* work to pick up, which would send one request's user with another's error.
|
|
76
|
+
* After an await, capture through the scope argument (scope.captureException).
|
|
77
|
+
*/
|
|
78
|
+
function withScope(callback) {
|
|
79
|
+
const carrier = getCarrier();
|
|
80
|
+
const scope = getCurrentScope().clone();
|
|
81
|
+
const storage = getStorage(carrier);
|
|
82
|
+
if (storage)
|
|
83
|
+
return storage.run(scope, () => callback(scope));
|
|
84
|
+
const logger = carrier.client?.logger;
|
|
85
|
+
if (logger?.enabled && !carrier.warnedNoStorage) {
|
|
86
|
+
carrier.warnedNoStorage = true;
|
|
87
|
+
logger.warn("withScope cannot isolate across awaits here (no AsyncLocalStorage): its scope is current only until the callback first awaits. After that, capture with the scope it passes, such as scope.captureException(error).");
|
|
88
|
+
}
|
|
89
|
+
carrier.stack.push(scope);
|
|
90
|
+
try {
|
|
91
|
+
return callback(scope);
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
const i = carrier.stack.lastIndexOf(scope);
|
|
95
|
+
if (i >= 0)
|
|
96
|
+
carrier.stack.splice(i, 1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Runs a capture against the client with the given scope, or the current one. */
|
|
100
|
+
function captureWith(scope, run) {
|
|
101
|
+
try {
|
|
102
|
+
const carrier = getCarrier();
|
|
103
|
+
if (carrier.client) {
|
|
104
|
+
const id = run(carrier.client, scope ?? getCurrentScope());
|
|
105
|
+
carrier.lastEventId = id;
|
|
106
|
+
return id;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// never throw into the app
|
|
111
|
+
}
|
|
112
|
+
return (0, utils_js_1.uuid4)();
|
|
113
|
+
}
|
|
114
|
+
/** captureMessage's second argument: a level or a context. */
|
|
115
|
+
function messageContext(levelOrContext) {
|
|
116
|
+
return typeof levelOrContext === "string"
|
|
117
|
+
? { level: levelOrContext }
|
|
118
|
+
: levelOrContext;
|
|
119
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { Breadcrumb, CaptureContext, Event, InitOptions, Level, Primitive, User } from "./types.js";
|
|
2
|
+
export { withScope } from "./hub.js";
|
|
3
|
+
export type { Scope } from "./scope.js";
|
|
4
|
+
export { SDK_NAME, SDK_VERSION } from "./version.js";
|
|
5
|
+
export type { Breadcrumb, BreadcrumbHint, CaptureContext, Event, EventHint, Exception, InitOptions, IntegrationToggles, Level, Mechanism, Primitive, RequestInfo, ScopeData, SdkInfo, SentryLevel, StackFrame, Transport, TransportRequest, TransportResponse, User, } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Starts the SDK. Call it once, as early as you can. Calling it again replaces
|
|
8
|
+
* the earlier setup. Without a valid DSN, or with enabled: false, nothing is
|
|
9
|
+
* sent and nothing is patched.
|
|
10
|
+
*/
|
|
11
|
+
export declare function init(options?: InitOptions): void;
|
|
12
|
+
/** Sends an error (or anything thrown). Returns the event id. */
|
|
13
|
+
export declare function captureException(error: unknown, context?: CaptureContext): string;
|
|
14
|
+
/**
|
|
15
|
+
* Sends a message. The second argument is a level (Sentry's or your own) or
|
|
16
|
+
* a context. Returns the event id.
|
|
17
|
+
*/
|
|
18
|
+
export declare function captureMessage(message: string, levelOrContext?: Level | CaptureContext): string;
|
|
19
|
+
/** Sends an event you built in Sentry's schema. Returns the event id. */
|
|
20
|
+
export declare function captureEvent(event: Event, context?: CaptureContext): string;
|
|
21
|
+
/** Pass null to forget the user. */
|
|
22
|
+
export declare function setUser(user: User | null): void;
|
|
23
|
+
export declare function setTag(key: string, value: Primitive): void;
|
|
24
|
+
export declare function setTags(tags: Record<string, Primitive>): void;
|
|
25
|
+
/** Pass null to remove the context. */
|
|
26
|
+
export declare function setContext(name: string, context: Record<string, unknown> | null): void;
|
|
27
|
+
export declare function setExtra(key: string, value: unknown): void;
|
|
28
|
+
/** Records a breadcrumb on the current scope, through beforeBreadcrumb. Ignored before init. */
|
|
29
|
+
export declare function addBreadcrumb(breadcrumb: Breadcrumb): void;
|
|
30
|
+
/**
|
|
31
|
+
* Waits up to timeoutMs (default 2000) for captured events to be sent.
|
|
32
|
+
* Resolves true once nothing is left to send, false on timeout or before init.
|
|
33
|
+
*/
|
|
34
|
+
export declare function flush(timeoutMs?: number): Promise<boolean>;
|
|
35
|
+
/** Flushes, then stops the SDK and removes its integrations. */
|
|
36
|
+
export declare function close(timeoutMs?: number): Promise<boolean>;
|
|
37
|
+
/** The id of the last event captured, sent or not. */
|
|
38
|
+
export declare function lastEventId(): string | undefined;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SDK_VERSION = exports.SDK_NAME = exports.withScope = void 0;
|
|
4
|
+
exports.init = init;
|
|
5
|
+
exports.captureException = captureException;
|
|
6
|
+
exports.captureMessage = captureMessage;
|
|
7
|
+
exports.captureEvent = captureEvent;
|
|
8
|
+
exports.setUser = setUser;
|
|
9
|
+
exports.setTag = setTag;
|
|
10
|
+
exports.setTags = setTags;
|
|
11
|
+
exports.setContext = setContext;
|
|
12
|
+
exports.setExtra = setExtra;
|
|
13
|
+
exports.addBreadcrumb = addBreadcrumb;
|
|
14
|
+
exports.flush = flush;
|
|
15
|
+
exports.close = close;
|
|
16
|
+
exports.lastEventId = lastEventId;
|
|
17
|
+
const client_js_1 = require("./client.js");
|
|
18
|
+
const hub_js_1 = require("./hub.js");
|
|
19
|
+
const utils_js_1 = require("./utils.js");
|
|
20
|
+
var hub_js_2 = require("./hub.js");
|
|
21
|
+
Object.defineProperty(exports, "withScope", { enumerable: true, get: function () { return hub_js_2.withScope; } });
|
|
22
|
+
var version_js_1 = require("./version.js");
|
|
23
|
+
Object.defineProperty(exports, "SDK_NAME", { enumerable: true, get: function () { return version_js_1.SDK_NAME; } });
|
|
24
|
+
Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return version_js_1.SDK_VERSION; } });
|
|
25
|
+
/**
|
|
26
|
+
* Starts the SDK. Call it once, as early as you can. Calling it again replaces
|
|
27
|
+
* the earlier setup. Without a valid DSN, or with enabled: false, nothing is
|
|
28
|
+
* sent and nothing is patched.
|
|
29
|
+
*/
|
|
30
|
+
function init(options = {}) {
|
|
31
|
+
try {
|
|
32
|
+
const carrier = (0, hub_js_1.getCarrier)();
|
|
33
|
+
carrier.client?.teardown();
|
|
34
|
+
carrier.client = undefined;
|
|
35
|
+
const opts = (0, utils_js_1.isObject)(options) ? options : {};
|
|
36
|
+
const client = new client_js_1.Client(opts);
|
|
37
|
+
carrier.root._apply(opts.initialScope);
|
|
38
|
+
if (opts.tags)
|
|
39
|
+
carrier.root.setTags(opts.tags);
|
|
40
|
+
carrier.client = client;
|
|
41
|
+
client.setup();
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
if ((0, utils_js_1.isObject)(options) && options.debug) {
|
|
45
|
+
(0, utils_js_1.safely)(() => console.error("[StatOSS] init failed:", error));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/** Sends an error (or anything thrown). Returns the event id. */
|
|
50
|
+
function captureException(error, context) {
|
|
51
|
+
return (0, hub_js_1.captureWith)(undefined, (client, scope) => client.captureException(error, context, scope));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Sends a message. The second argument is a level (Sentry's or your own) or
|
|
55
|
+
* a context. Returns the event id.
|
|
56
|
+
*/
|
|
57
|
+
function captureMessage(message, levelOrContext) {
|
|
58
|
+
return (0, hub_js_1.captureWith)(undefined, (client, scope) => client.captureMessage(message, (0, hub_js_1.messageContext)(levelOrContext), scope));
|
|
59
|
+
}
|
|
60
|
+
/** Sends an event you built in Sentry's schema. Returns the event id. */
|
|
61
|
+
function captureEvent(event, context) {
|
|
62
|
+
return (0, hub_js_1.captureWith)(undefined, (client, scope) => client.captureEvent(event, context, scope));
|
|
63
|
+
}
|
|
64
|
+
/** Pass null to forget the user. */
|
|
65
|
+
function setUser(user) {
|
|
66
|
+
(0, utils_js_1.safely)(() => (0, hub_js_1.getCurrentScope)().setUser(user));
|
|
67
|
+
}
|
|
68
|
+
function setTag(key, value) {
|
|
69
|
+
(0, utils_js_1.safely)(() => (0, hub_js_1.getCurrentScope)().setTag(key, value));
|
|
70
|
+
}
|
|
71
|
+
function setTags(tags) {
|
|
72
|
+
(0, utils_js_1.safely)(() => (0, hub_js_1.getCurrentScope)().setTags(tags));
|
|
73
|
+
}
|
|
74
|
+
/** Pass null to remove the context. */
|
|
75
|
+
function setContext(name, context) {
|
|
76
|
+
(0, utils_js_1.safely)(() => (0, hub_js_1.getCurrentScope)().setContext(name, context));
|
|
77
|
+
}
|
|
78
|
+
function setExtra(key, value) {
|
|
79
|
+
(0, utils_js_1.safely)(() => (0, hub_js_1.getCurrentScope)().setExtra(key, value));
|
|
80
|
+
}
|
|
81
|
+
/** Records a breadcrumb on the current scope, through beforeBreadcrumb. Ignored before init. */
|
|
82
|
+
function addBreadcrumb(breadcrumb) {
|
|
83
|
+
(0, utils_js_1.safely)(() => (0, hub_js_1.getCarrier)().client?.addBreadcrumb(breadcrumb));
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Waits up to timeoutMs (default 2000) for captured events to be sent.
|
|
87
|
+
* Resolves true once nothing is left to send, false on timeout or before init.
|
|
88
|
+
*/
|
|
89
|
+
function flush(timeoutMs) {
|
|
90
|
+
const client = (0, utils_js_1.safely)(() => (0, hub_js_1.getCarrier)().client);
|
|
91
|
+
if (!client)
|
|
92
|
+
return Promise.resolve(false);
|
|
93
|
+
return client.flush(timeoutMs).catch(() => false);
|
|
94
|
+
}
|
|
95
|
+
/** Flushes, then stops the SDK and removes its integrations. */
|
|
96
|
+
function close(timeoutMs) {
|
|
97
|
+
const carrier = (0, utils_js_1.safely)(() => (0, hub_js_1.getCarrier)());
|
|
98
|
+
const client = carrier?.client;
|
|
99
|
+
if (!carrier || !client)
|
|
100
|
+
return Promise.resolve(false);
|
|
101
|
+
carrier.client = undefined;
|
|
102
|
+
return client.close(timeoutMs).catch(() => false);
|
|
103
|
+
}
|
|
104
|
+
/** The id of the last event captured, sent or not. */
|
|
105
|
+
function lastEventId() {
|
|
106
|
+
return (0, utils_js_1.safely)(() => (0, hub_js_1.getCarrier)().lastEventId);
|
|
107
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Client } from "../client.js";
|
|
2
|
+
/** Breadcrumbs for pushState, replaceState and popstate. */
|
|
3
|
+
export declare function installNavigation(client: Client): (() => void) | undefined;
|
|
4
|
+
/** A short CSS-like path to an element, such as `div#app > button.buy[type="submit"]`. */
|
|
5
|
+
export declare function selectorFor(target: unknown): string;
|
|
6
|
+
/** A breadcrumb per click, once a second at most for the same element. */
|
|
7
|
+
export declare function installClicks(client: Client): (() => void) | undefined;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installNavigation = installNavigation;
|
|
4
|
+
exports.selectorFor = selectorFor;
|
|
5
|
+
exports.installClicks = installClicks;
|
|
6
|
+
const utils_js_1 = require("../utils.js");
|
|
7
|
+
function relative(url, origin) {
|
|
8
|
+
const clean = (0, utils_js_1.scrubUrl)(url);
|
|
9
|
+
return origin && clean.startsWith(`${origin}/`)
|
|
10
|
+
? clean.slice(origin.length)
|
|
11
|
+
: clean;
|
|
12
|
+
}
|
|
13
|
+
/** Breadcrumbs for pushState, replaceState and popstate. */
|
|
14
|
+
function installNavigation(client) {
|
|
15
|
+
const w = globalThis;
|
|
16
|
+
const history = w.history;
|
|
17
|
+
if (!history || !w.location)
|
|
18
|
+
return undefined;
|
|
19
|
+
let last = (0, utils_js_1.safely)(() => w.location?.href) ?? "";
|
|
20
|
+
const record = () => {
|
|
21
|
+
const to = (0, utils_js_1.safely)(() => w.location?.href);
|
|
22
|
+
if (typeof to !== "string" || to === last)
|
|
23
|
+
return;
|
|
24
|
+
const origin = (0, utils_js_1.safely)(() => w.location?.origin);
|
|
25
|
+
const from = last;
|
|
26
|
+
last = to;
|
|
27
|
+
client.addBreadcrumb({
|
|
28
|
+
category: "navigation",
|
|
29
|
+
data: { from: relative(from, origin), to: relative(to, origin) },
|
|
30
|
+
}, { from, to });
|
|
31
|
+
};
|
|
32
|
+
const restore = [];
|
|
33
|
+
for (const name of ["pushState", "replaceState"]) {
|
|
34
|
+
const original = history[name];
|
|
35
|
+
if (typeof original !== "function")
|
|
36
|
+
continue;
|
|
37
|
+
const fn = original;
|
|
38
|
+
const wrapper = function (...args) {
|
|
39
|
+
const result = fn.apply(this, args);
|
|
40
|
+
(0, utils_js_1.safely)(record);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
history[name] = wrapper;
|
|
44
|
+
restore.push(() => {
|
|
45
|
+
if (history[name] === wrapper)
|
|
46
|
+
history[name] = original;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
w.addEventListener?.("popstate", record);
|
|
50
|
+
return () => {
|
|
51
|
+
for (const undo of restore)
|
|
52
|
+
undo();
|
|
53
|
+
(0, utils_js_1.safely)(() => w.removeEventListener?.("popstate", record));
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
const ATTRIBUTES = ["name", "type", "title", "alt", "aria-label"];
|
|
57
|
+
const MAX_SELECTOR = 80;
|
|
58
|
+
const MAX_DEPTH = 5;
|
|
59
|
+
function describeElement(el) {
|
|
60
|
+
if (typeof el.tagName !== "string")
|
|
61
|
+
return "";
|
|
62
|
+
let out = el.tagName.toLowerCase();
|
|
63
|
+
if (typeof el.id === "string" && el.id)
|
|
64
|
+
out += `#${el.id}`;
|
|
65
|
+
if (typeof el.className === "string" && el.className.trim()) {
|
|
66
|
+
out += el.className
|
|
67
|
+
.trim()
|
|
68
|
+
.split(/\s+/)
|
|
69
|
+
.slice(0, 3)
|
|
70
|
+
.map((c) => `.${c}`)
|
|
71
|
+
.join("");
|
|
72
|
+
}
|
|
73
|
+
for (const attr of ATTRIBUTES) {
|
|
74
|
+
const value = (0, utils_js_1.safely)(() => el.getAttribute?.(attr));
|
|
75
|
+
if (typeof value === "string" && value)
|
|
76
|
+
out += `[${attr}="${value.slice(0, 30)}"]`;
|
|
77
|
+
}
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
/** A short CSS-like path to an element, such as `div#app > button.buy[type="submit"]`. */
|
|
81
|
+
function selectorFor(target) {
|
|
82
|
+
const parts = [];
|
|
83
|
+
let el = target;
|
|
84
|
+
let length = 0;
|
|
85
|
+
for (let depth = 0; (0, utils_js_1.isObject)(el) && depth < MAX_DEPTH; depth++) {
|
|
86
|
+
const part = describeElement(el);
|
|
87
|
+
if (!part || part === "html")
|
|
88
|
+
break;
|
|
89
|
+
if (parts.length > 0 && length + part.length + 3 > MAX_SELECTOR)
|
|
90
|
+
break;
|
|
91
|
+
parts.unshift(part);
|
|
92
|
+
length += part.length + 3;
|
|
93
|
+
el = el.parentElement;
|
|
94
|
+
}
|
|
95
|
+
return parts.join(" > ");
|
|
96
|
+
}
|
|
97
|
+
/** A breadcrumb per click, once a second at most for the same element. */
|
|
98
|
+
function installClicks(client) {
|
|
99
|
+
const doc = globalThis.document;
|
|
100
|
+
if (!doc || typeof doc.addEventListener !== "function")
|
|
101
|
+
return undefined;
|
|
102
|
+
let lastTarget;
|
|
103
|
+
let lastTime = 0;
|
|
104
|
+
const onClick = (event) => {
|
|
105
|
+
(0, utils_js_1.safely)(() => {
|
|
106
|
+
const target = (0, utils_js_1.isObject)(event) ? event.target : undefined;
|
|
107
|
+
if (!(0, utils_js_1.isObject)(target))
|
|
108
|
+
return;
|
|
109
|
+
const time = Date.now();
|
|
110
|
+
if (target === lastTarget && time - lastTime < 1000)
|
|
111
|
+
return;
|
|
112
|
+
lastTarget = target;
|
|
113
|
+
lastTime = time;
|
|
114
|
+
const selector = selectorFor(target);
|
|
115
|
+
if (selector)
|
|
116
|
+
client.addBreadcrumb({ category: "ui.click", message: selector }, { event });
|
|
117
|
+
});
|
|
118
|
+
};
|
|
119
|
+
const options = { capture: true, passive: true };
|
|
120
|
+
doc.addEventListener("click", onClick, options);
|
|
121
|
+
return () => {
|
|
122
|
+
(0, utils_js_1.safely)(() => doc.removeEventListener?.("click", onClick, options));
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.installConsole = installConsole;
|
|
4
|
+
const logger_js_1 = require("../logger.js");
|
|
5
|
+
const utils_js_1 = require("../utils.js");
|
|
6
|
+
const METHODS = [
|
|
7
|
+
"debug",
|
|
8
|
+
"info",
|
|
9
|
+
"warn",
|
|
10
|
+
"error",
|
|
11
|
+
"log",
|
|
12
|
+
"assert",
|
|
13
|
+
];
|
|
14
|
+
const LEVELS = {
|
|
15
|
+
debug: "debug",
|
|
16
|
+
info: "info",
|
|
17
|
+
warn: "warning",
|
|
18
|
+
error: "error",
|
|
19
|
+
log: "info",
|
|
20
|
+
assert: "error",
|
|
21
|
+
};
|
|
22
|
+
const MAX_MESSAGE = 1024;
|
|
23
|
+
// Every part is cut before it is joined, so a huge argument is never copied whole.
|
|
24
|
+
function format(arg) {
|
|
25
|
+
if (typeof arg === "string")
|
|
26
|
+
return (0, utils_js_1.truncate)(arg, MAX_MESSAGE);
|
|
27
|
+
if ((0, utils_js_1.isErrorLike)(arg)) {
|
|
28
|
+
return `${(0, utils_js_1.truncate)((0, utils_js_1.safeString)(arg.name), 200)}: ${(0, utils_js_1.truncate)((0, utils_js_1.safeString)(arg.message), MAX_MESSAGE)}`;
|
|
29
|
+
}
|
|
30
|
+
if (arg !== null && typeof arg === "object") {
|
|
31
|
+
const normalized = (0, utils_js_1.normalize)(arg, 2, 200, 20);
|
|
32
|
+
if (typeof normalized === "string")
|
|
33
|
+
return normalized;
|
|
34
|
+
try {
|
|
35
|
+
return (0, utils_js_1.truncate)(JSON.stringify(normalized), MAX_MESSAGE);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return "[Object]";
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return (0, utils_js_1.truncate)((0, utils_js_1.safeString)(arg), MAX_MESSAGE);
|
|
42
|
+
}
|
|
43
|
+
function join(args) {
|
|
44
|
+
let out = "";
|
|
45
|
+
for (let i = 0; i < args.length && out.length <= MAX_MESSAGE; i++) {
|
|
46
|
+
out += (i > 0 ? " " : "") + format(args[i]);
|
|
47
|
+
}
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/** Breadcrumbs for console calls; the original methods still run, unchanged. */
|
|
51
|
+
function installConsole(client) {
|
|
52
|
+
const target = globalThis.console;
|
|
53
|
+
if (!target)
|
|
54
|
+
return undefined;
|
|
55
|
+
const wrapped = {};
|
|
56
|
+
let inside = false;
|
|
57
|
+
for (const method of METHODS) {
|
|
58
|
+
const original = target[method];
|
|
59
|
+
if (typeof original !== "function")
|
|
60
|
+
continue;
|
|
61
|
+
const fn = original;
|
|
62
|
+
logger_js_1.originalConsole[method] = fn;
|
|
63
|
+
// One breadcrumb per call: console.assert, for one, calls console.warn itself.
|
|
64
|
+
const wrapper = function (...args) {
|
|
65
|
+
if (inside)
|
|
66
|
+
return fn.apply(this, args);
|
|
67
|
+
inside = true;
|
|
68
|
+
try {
|
|
69
|
+
(0, utils_js_1.safely)(() => record(client, method, args));
|
|
70
|
+
return fn.apply(this, args);
|
|
71
|
+
}
|
|
72
|
+
finally {
|
|
73
|
+
inside = false;
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
wrapped[method] = wrapper;
|
|
77
|
+
target[method] = wrapper;
|
|
78
|
+
}
|
|
79
|
+
return () => {
|
|
80
|
+
for (const method of METHODS) {
|
|
81
|
+
// Leave it if something else has wrapped the console since.
|
|
82
|
+
if (wrapped[method] && target[method] === wrapped[method]) {
|
|
83
|
+
target[method] = logger_js_1.originalConsole[method];
|
|
84
|
+
}
|
|
85
|
+
delete logger_js_1.originalConsole[method];
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function record(client, method, args) {
|
|
90
|
+
let message;
|
|
91
|
+
if (method === "assert") {
|
|
92
|
+
if (args[0])
|
|
93
|
+
return;
|
|
94
|
+
const rest = join(args.slice(1));
|
|
95
|
+
message = `Assertion failed: ${rest || "console.assert"}`;
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
message = join(args);
|
|
99
|
+
}
|
|
100
|
+
client.addBreadcrumb({
|
|
101
|
+
category: "console",
|
|
102
|
+
level: LEVELS[method],
|
|
103
|
+
message: (0, utils_js_1.truncate)(message, MAX_MESSAGE),
|
|
104
|
+
data: { logger: "console" },
|
|
105
|
+
}, { input: args, level: method });
|
|
106
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Event } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* What makes two events the same for dedupe, as Sentry's Dedupe compares
|
|
4
|
+
* them: message, exception types and values, frames and fingerprint.
|
|
5
|
+
* Undefined for events with neither message nor exception.
|
|
6
|
+
*/
|
|
7
|
+
export declare function eventSignature(event: Event): string | undefined;
|