@statoss/sdk 0.1.1 → 0.3.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/README.md +102 -15
- package/dist/cjs/client.d.ts +9 -2
- package/dist/cjs/client.js +39 -2
- package/dist/cjs/debug-ids.d.ts +16 -0
- package/dist/cjs/debug-ids.js +112 -0
- package/dist/cjs/index.d.ts +11 -2
- package/dist/cjs/index.js +11 -6
- package/dist/cjs/levels.d.ts +4 -1
- package/dist/cjs/levels.js +23 -12
- package/dist/cjs/schema.d.ts +199 -0
- package/dist/cjs/schema.js +119 -0
- package/dist/cjs/types.d.ts +10 -0
- package/dist/cjs/version.d.ts +1 -1
- package/dist/cjs/version.js +1 -1
- package/dist/esm/client.d.ts +9 -2
- package/dist/esm/client.js +39 -2
- package/dist/esm/debug-ids.d.ts +16 -0
- package/dist/esm/debug-ids.js +108 -0
- package/dist/esm/index.d.ts +11 -2
- package/dist/esm/index.js +9 -5
- package/dist/esm/levels.d.ts +4 -1
- package/dist/esm/levels.js +20 -10
- package/dist/esm/schema.d.ts +199 -0
- package/dist/esm/schema.js +114 -0
- package/dist/esm/types.d.ts +10 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -252,20 +252,105 @@ The Edge runtime has no global error hook either: capture where you handle
|
|
|
252
252
|
errors and `await StatOSS.flush(2000)` (or pass it to `waitUntil`) before
|
|
253
253
|
returning. In Next.js, the `instrumentation.ts` above covers it.
|
|
254
254
|
|
|
255
|
-
##
|
|
255
|
+
## Source maps
|
|
256
|
+
|
|
257
|
+
After the build, give the bundles and their maps debug ids, then upload the
|
|
258
|
+
maps with Sentry's CLI:
|
|
259
|
+
|
|
260
|
+
```sh
|
|
261
|
+
npx @sentry/cli sourcemaps inject ./dist
|
|
262
|
+
|
|
263
|
+
SENTRY_URL=https://statoss.com \
|
|
264
|
+
SENTRY_AUTH_TOKEN=<a StatOSS API key with write access> \
|
|
265
|
+
npx @sentry/cli sourcemaps upload --org statoss --project <app slug> ./dist
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Adding `--release <release>` to the upload is optional. Deploy the bundles as
|
|
269
|
+
`inject` left them: the SDK reads their debug ids and sends them with each
|
|
270
|
+
event. Frames are then shown as source and grouped by their source names.
|
|
271
|
+
|
|
272
|
+
## Your own levels, categories and fields
|
|
256
273
|
|
|
257
274
|
Sentry has five levels: `fatal`, `error`, `warning`, `info` and `debug`.
|
|
258
|
-
StatOSS
|
|
259
|
-
|
|
260
|
-
|
|
275
|
+
StatOSS adds levels of your own, categories and fields. They travel as tags
|
|
276
|
+
next to the nearest Sentry level, so Sentry and any Sentry-based tool still
|
|
277
|
+
read the event normally.
|
|
278
|
+
|
|
279
|
+
Declare them with `defineSchema` and give the schema to `init`. The capture
|
|
280
|
+
functions `init` returns take only the schema's names, so a wrong one fails
|
|
281
|
+
to compile.
|
|
261
282
|
|
|
262
283
|
```ts
|
|
263
|
-
StatOSS
|
|
264
|
-
|
|
284
|
+
import * as StatOSS from "@statoss/sdk";
|
|
285
|
+
|
|
286
|
+
export const schema = StatOSS.defineSchema({
|
|
265
287
|
levels: {
|
|
266
|
-
"payment-failed": "error",
|
|
288
|
+
"payment-failed": { like: "error", label: "Payment failed" },
|
|
267
289
|
"slow-query": "warning",
|
|
268
290
|
},
|
|
291
|
+
categories: {
|
|
292
|
+
billing: { label: "Billing", color: "plum" },
|
|
293
|
+
auth: {},
|
|
294
|
+
},
|
|
295
|
+
fields: {
|
|
296
|
+
route: { label: "Route", filterable: true },
|
|
297
|
+
duration_ms: { type: "number", unit: "ms", label: "Duration" },
|
|
298
|
+
},
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const statoss = StatOSS.init({ dsn: process.env.STATOSS_DSN, schema });
|
|
302
|
+
|
|
303
|
+
statoss.captureException(error, {
|
|
304
|
+
level: "payment-failed",
|
|
305
|
+
category: "billing",
|
|
306
|
+
fields: { route: "/pay" },
|
|
307
|
+
});
|
|
308
|
+
// level "error", tags { "statoss.level": "payment-failed",
|
|
309
|
+
// "statoss.category": "billing", route: "/pay" }
|
|
310
|
+
|
|
311
|
+
statoss.captureMessage("Search took 2.4 s", {
|
|
312
|
+
level: "slow-query",
|
|
313
|
+
fields: { duration_ms: 2400 },
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
// @ts-expect-error: not a level of this schema
|
|
317
|
+
statoss.captureException(error, { level: "payment-faild" });
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
- A level ranks like `fatal`, `error`, `warning`, `info`, `debug` or
|
|
321
|
+
`trace`: give that alone, or `like` with an optional `severity` (1 to 24,
|
|
322
|
+
inside its range), `label` and `color`. It is sent as the nearest Sentry
|
|
323
|
+
level (`trace` as `debug`; `error` when none is given).
|
|
324
|
+
- A category takes an optional `label`, `color`, `owner` (a team member's
|
|
325
|
+
email) and `hidden`.
|
|
326
|
+
- A field is a tag key, with an optional `label`, `type` (`text` or
|
|
327
|
+
`number`), `unit` and `filterable`. Field values are strings, numbers or
|
|
328
|
+
booleans.
|
|
329
|
+
- Categories and fields can also be lists of names.
|
|
330
|
+
- `rules` set a level or a category for events that match, such as
|
|
331
|
+
`{ subject: "logger", value: "payments", category: "billing" }`. They name
|
|
332
|
+
the schema's own levels and categories.
|
|
333
|
+
- Colours: `crimson`, `red`, `orange`, `amber`, `yellow`, `green`, `aqua`,
|
|
334
|
+
`teal`, `blue`, `violet`, `indigo`, `magenta`, `plum` and `grey`.
|
|
335
|
+
- Names: a level is 1 to 32 characters and a category 1 to 64, of lowercase
|
|
336
|
+
letters, digits, dots, dashes and underscores. A field is any tag key not
|
|
337
|
+
starting `statoss.`.
|
|
338
|
+
|
|
339
|
+
`defineSchema` refuses a wrong name, range, severity, colour or setting at
|
|
340
|
+
compile time (TypeScript 5.0 or later). It returns the schema as given:
|
|
341
|
+
plain data in the shape StatOSS's schema API takes. A function of yours
|
|
342
|
+
that hands a schema to `init` types it as
|
|
343
|
+
`<const S extends SchemaSpec & CheckSchema<S>>(schema: S)`.
|
|
344
|
+
|
|
345
|
+
### Without a schema
|
|
346
|
+
|
|
347
|
+
The module-level functions take any level or category name, and `levels` in
|
|
348
|
+
`init` maps your names to Sentry levels:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
StatOSS.init({
|
|
352
|
+
dsn: "...",
|
|
353
|
+
levels: { "payment-failed": "error", "slow-query": "warning" },
|
|
269
354
|
});
|
|
270
355
|
|
|
271
356
|
StatOSS.captureMessage("Card declined", "payment-failed");
|
|
@@ -278,11 +363,10 @@ StatOSS.captureException(error, {
|
|
|
278
363
|
// level "warning", tags { "statoss.level": "slow-query", "statoss.category": "db.postgres" }
|
|
279
364
|
```
|
|
280
365
|
|
|
281
|
-
-
|
|
282
|
-
`levels`
|
|
283
|
-
-
|
|
284
|
-
|
|
285
|
-
underscores. Others are ignored; with `debug: true` the SDK logs each one.
|
|
366
|
+
- A name in neither `levels` nor the schema is sent as `error`. Given both,
|
|
367
|
+
`levels` wins.
|
|
368
|
+
- Names the SDK cannot send are ignored; with `debug: true` it logs each
|
|
369
|
+
one.
|
|
286
370
|
- StatOSS filters and groups on these tags like any other.
|
|
287
371
|
|
|
288
372
|
## What is captured
|
|
@@ -339,7 +423,8 @@ await StatOSS.withScope(async (scope) => {
|
|
|
339
423
|
## API
|
|
340
424
|
|
|
341
425
|
```ts
|
|
342
|
-
init(options):
|
|
426
|
+
init(options): handle // handle.captureException, captureMessage, captureEvent
|
|
427
|
+
defineSchema(schema): schema // the schema as given, its names as types
|
|
343
428
|
captureException(error, context?): string // returns the event id
|
|
344
429
|
captureMessage(message, levelOrContext?): string
|
|
345
430
|
captureEvent(event, context?): string
|
|
@@ -357,8 +442,9 @@ scope.setUser, setTag, setTags, setExtra, setExtras, setContext, setLevel,
|
|
|
357
442
|
scope.captureException, captureMessage, captureEvent // with this scope
|
|
358
443
|
```
|
|
359
444
|
|
|
360
|
-
`context` is `{ level, tags, extra, contexts, user, fingerprint, category }
|
|
361
|
-
It wins over the scope for
|
|
445
|
+
`context` is `{ level, tags, extra, contexts, user, fingerprint, category }`,
|
|
446
|
+
and on the handle `init` returns also `fields`. It wins over the scope for
|
|
447
|
+
that one event.
|
|
362
448
|
|
|
363
449
|
### Options
|
|
364
450
|
|
|
@@ -375,6 +461,7 @@ It wins over the scope for that one event.
|
|
|
375
461
|
| `sendDefaultPii` | `false` | Record the sender's IP address |
|
|
376
462
|
| `tags` | | Tags on every event |
|
|
377
463
|
| `initialScope` | | `{ tags, extra, contexts, user, level, fingerprint }` |
|
|
464
|
+
| `schema` | | From `defineSchema`: your levels, categories, fields |
|
|
378
465
|
| `levels` | | Your level names and the Sentry level each is sent as |
|
|
379
466
|
| `integrations` | all on | See above |
|
|
380
467
|
| `enabled` | `true` | |
|
package/dist/cjs/client.d.ts
CHANGED
|
@@ -2,6 +2,10 @@ import { type Logger } from "./logger.js";
|
|
|
2
2
|
import { type RuntimeInfo } from "./runtime.js";
|
|
3
3
|
import { type Scope } from "./scope.js";
|
|
4
4
|
import type { Breadcrumb, BreadcrumbHint, CaptureContext, Event, InitOptions } from "./types.js";
|
|
5
|
+
/** init's options as the client reads them: init may pass a schema. */
|
|
6
|
+
export type ClientOptions = InitOptions & {
|
|
7
|
+
schema?: unknown;
|
|
8
|
+
};
|
|
5
9
|
export declare class Client {
|
|
6
10
|
readonly logger: Logger;
|
|
7
11
|
readonly runtime: RuntimeInfo;
|
|
@@ -20,7 +24,7 @@ export declare class Client {
|
|
|
20
24
|
private reportTimer?;
|
|
21
25
|
private previousSignature?;
|
|
22
26
|
private closed;
|
|
23
|
-
constructor(options:
|
|
27
|
+
constructor(options: ClientOptions);
|
|
24
28
|
/** Installs the integrations that apply to this runtime. */
|
|
25
29
|
setup(): void;
|
|
26
30
|
captureException(error: unknown, context: CaptureContext | undefined, scope: Scope): string;
|
|
@@ -44,7 +48,10 @@ export declare class Client {
|
|
|
44
48
|
/** Merges scope, context and options into the event, in Sentry's field order. */
|
|
45
49
|
private prepare;
|
|
46
50
|
private process;
|
|
47
|
-
/**
|
|
51
|
+
/**
|
|
52
|
+
* Makes what beforeSend handed back valid again (a Sentry level, string
|
|
53
|
+
* tags, a sane size) and names the debug ids of the files its frames ran in.
|
|
54
|
+
*/
|
|
48
55
|
private finish;
|
|
49
56
|
private send;
|
|
50
57
|
}
|
package/dist/cjs/client.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Client = void 0;
|
|
4
|
+
const debug_ids_js_1 = require("./debug-ids.js");
|
|
4
5
|
const dsn_js_1 = require("./dsn.js");
|
|
5
6
|
const envelope_js_1 = require("./envelope.js");
|
|
6
7
|
const event_js_1 = require("./event.js");
|
|
@@ -13,6 +14,7 @@ const handlers_js_1 = require("./integrations/handlers.js");
|
|
|
13
14
|
const levels_js_1 = require("./levels.js");
|
|
14
15
|
const logger_js_1 = require("./logger.js");
|
|
15
16
|
const runtime_js_1 = require("./runtime.js");
|
|
17
|
+
const schema_js_1 = require("./schema.js");
|
|
16
18
|
const scope_js_1 = require("./scope.js");
|
|
17
19
|
const transport_js_1 = require("./transport.js");
|
|
18
20
|
const utils_js_1 = require("./utils.js");
|
|
@@ -53,6 +55,32 @@ function cleanTags(tags) {
|
|
|
53
55
|
}
|
|
54
56
|
return out;
|
|
55
57
|
}
|
|
58
|
+
/** A capture's fields, as tags. Keys starting statoss. are not fields. */
|
|
59
|
+
function fieldTags(fields, logger) {
|
|
60
|
+
const out = {};
|
|
61
|
+
if (fields === undefined || fields === null)
|
|
62
|
+
return out;
|
|
63
|
+
if (!(0, utils_js_1.isObject)(fields) || Array.isArray(fields)) {
|
|
64
|
+
logger.warn('Ignored `fields`: it should be an object such as { route: "/pay" }.');
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
for (const [key, value] of Object.entries(fields)) {
|
|
68
|
+
if (value === undefined || value === null)
|
|
69
|
+
continue;
|
|
70
|
+
if (key === "" || key.startsWith("statoss.")) {
|
|
71
|
+
logger.warn(`Ignored field "${key}": fields are not empty and do not start statoss.`);
|
|
72
|
+
}
|
|
73
|
+
else if (typeof value === "string" ||
|
|
74
|
+
typeof value === "number" ||
|
|
75
|
+
typeof value === "boolean") {
|
|
76
|
+
out[key] = value;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
logger.warn(`Ignored field "${key}": its value should be a string, number or boolean.`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
56
84
|
function cleanContexts(contexts) {
|
|
57
85
|
const out = {};
|
|
58
86
|
for (const [name, value] of Object.entries(contexts)) {
|
|
@@ -102,7 +130,11 @@ class Client {
|
|
|
102
130
|
this.options = options;
|
|
103
131
|
this.logger = (0, logger_js_1.makeLogger)(options.debug === true);
|
|
104
132
|
this.runtime = (0, runtime_js_1.describeRuntime)();
|
|
105
|
-
|
|
133
|
+
// What `levels` says wins over the schema.
|
|
134
|
+
this.levels = {
|
|
135
|
+
...(0, schema_js_1.readSchema)(options.schema, this.logger),
|
|
136
|
+
...(0, levels_js_1.readLevels)(options.levels, this.logger),
|
|
137
|
+
};
|
|
106
138
|
const rate = options.sampleRate;
|
|
107
139
|
if (rate !== undefined &&
|
|
108
140
|
!(typeof rate === "number" && rate >= 0 && rate <= 1)) {
|
|
@@ -328,6 +360,7 @@ class Client {
|
|
|
328
360
|
const tags = {
|
|
329
361
|
...scope._tags,
|
|
330
362
|
...ctx.tags,
|
|
363
|
+
...fieldTags(ctx.fields, this.logger),
|
|
331
364
|
...base.tags,
|
|
332
365
|
};
|
|
333
366
|
if (choice.custom)
|
|
@@ -434,7 +467,10 @@ class Client {
|
|
|
434
467
|
}
|
|
435
468
|
this.send(this.finish(final));
|
|
436
469
|
}
|
|
437
|
-
/**
|
|
470
|
+
/**
|
|
471
|
+
* Makes what beforeSend handed back valid again (a Sentry level, string
|
|
472
|
+
* tags, a sane size) and names the debug ids of the files its frames ran in.
|
|
473
|
+
*/
|
|
438
474
|
finish(event) {
|
|
439
475
|
if (!(0, levels_js_1.isSentryLevel)(event.level)) {
|
|
440
476
|
const choice = (0, levels_js_1.chooseLevel)([event.level], this.levels, "error", this.logger);
|
|
@@ -444,6 +480,7 @@ class Client {
|
|
|
444
480
|
}
|
|
445
481
|
if ((0, utils_js_1.isObject)(event.tags))
|
|
446
482
|
event.tags = cleanTags(event.tags);
|
|
483
|
+
(0, debug_ids_js_1.addDebugImages)(event);
|
|
447
484
|
const size = () => (0, utils_js_1.safely)(() => JSON.stringify(event).length, 0) ?? 0;
|
|
448
485
|
if (size() > MAX_EVENT_CHARS) {
|
|
449
486
|
this.logger.warn("The event is over 1 MB; sending it without breadcrumbs and extra.");
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Event } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Debug ids by the abs_path of the bundle they belong to, as Sentry's
|
|
4
|
+
* getFilenameToDebugIdMap reads them. `sentry-cli sourcemaps inject` and
|
|
5
|
+
* Sentry's bundler plugins put a snippet in each bundle that sets
|
|
6
|
+
* `globalThis._sentryDebugIds[new Error().stack] = "<debug id>"`, so the
|
|
7
|
+
* newest frame of each key ran in that bundle. Read again only when the
|
|
8
|
+
* number of entries changes (or the object is replaced).
|
|
9
|
+
*/
|
|
10
|
+
export declare function debugIdsByFile(): ReadonlyMap<string, string>;
|
|
11
|
+
/**
|
|
12
|
+
* Adds `{ type: "sourcemap", code_file, debug_id }` to event.debug_meta.images
|
|
13
|
+
* for each file in the event's frames that has a debug id, after the images
|
|
14
|
+
* already there, so the server can map those frames to source. Never throws.
|
|
15
|
+
*/
|
|
16
|
+
export declare function addDebugImages(event: Event): void;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.debugIdsByFile = debugIdsByFile;
|
|
4
|
+
exports.addDebugImages = addDebugImages;
|
|
5
|
+
const stack_js_1 = require("./stack.js");
|
|
6
|
+
const utils_js_1 = require("./utils.js");
|
|
7
|
+
const NONE = new Map();
|
|
8
|
+
// The file each stack string names, so each is parsed once.
|
|
9
|
+
const fileOfStack = new Map();
|
|
10
|
+
let lastIds;
|
|
11
|
+
let lastCount = -1;
|
|
12
|
+
let lastMap = NONE;
|
|
13
|
+
/**
|
|
14
|
+
* Debug ids by the abs_path of the bundle they belong to, as Sentry's
|
|
15
|
+
* getFilenameToDebugIdMap reads them. `sentry-cli sourcemaps inject` and
|
|
16
|
+
* Sentry's bundler plugins put a snippet in each bundle that sets
|
|
17
|
+
* `globalThis._sentryDebugIds[new Error().stack] = "<debug id>"`, so the
|
|
18
|
+
* newest frame of each key ran in that bundle. Read again only when the
|
|
19
|
+
* number of entries changes (or the object is replaced).
|
|
20
|
+
*/
|
|
21
|
+
function debugIdsByFile() {
|
|
22
|
+
const ids = globalThis._sentryDebugIds;
|
|
23
|
+
if (!(0, utils_js_1.isObject)(ids) || Array.isArray(ids))
|
|
24
|
+
return NONE;
|
|
25
|
+
const keys = Object.keys(ids);
|
|
26
|
+
if (ids === lastIds && keys.length === lastCount)
|
|
27
|
+
return lastMap;
|
|
28
|
+
const map = new Map();
|
|
29
|
+
for (const key of keys) {
|
|
30
|
+
const id = ids[key];
|
|
31
|
+
if (typeof id !== "string" || id === "")
|
|
32
|
+
continue;
|
|
33
|
+
if (!fileOfStack.has(key))
|
|
34
|
+
fileOfStack.set(key, newestFile(key));
|
|
35
|
+
const file = fileOfStack.get(key);
|
|
36
|
+
if (file)
|
|
37
|
+
map.set(file, id);
|
|
38
|
+
}
|
|
39
|
+
lastIds = ids;
|
|
40
|
+
lastCount = keys.length;
|
|
41
|
+
lastMap = map;
|
|
42
|
+
return map;
|
|
43
|
+
}
|
|
44
|
+
// As Sentry does: walking from the newest frame, the first that names a file.
|
|
45
|
+
function newestFile(stack) {
|
|
46
|
+
const frames = (0, stack_js_1.parseStack)(stack);
|
|
47
|
+
for (let i = frames.length - 1; i >= 0; i--) {
|
|
48
|
+
const file = frames[i].abs_path;
|
|
49
|
+
if (file)
|
|
50
|
+
return file;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
// Sentry's schema allows { values: [...] } or a bare list.
|
|
55
|
+
function valuesOf(value) {
|
|
56
|
+
if (Array.isArray(value))
|
|
57
|
+
return value;
|
|
58
|
+
if ((0, utils_js_1.isObject)(value) && Array.isArray(value.values))
|
|
59
|
+
return value.values;
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
/** The abs_path of every frame in the event's exceptions and threads. */
|
|
63
|
+
function framePaths(event) {
|
|
64
|
+
const paths = new Set();
|
|
65
|
+
const holders = [...valuesOf(event.exception), ...valuesOf(event.threads)];
|
|
66
|
+
for (const holder of holders) {
|
|
67
|
+
if (!(0, utils_js_1.isObject)(holder) || !(0, utils_js_1.isObject)(holder.stacktrace))
|
|
68
|
+
continue;
|
|
69
|
+
const frames = holder.stacktrace.frames;
|
|
70
|
+
if (!Array.isArray(frames))
|
|
71
|
+
continue;
|
|
72
|
+
for (const frame of frames) {
|
|
73
|
+
if ((0, utils_js_1.isObject)(frame) && typeof frame.abs_path === "string")
|
|
74
|
+
paths.add(frame.abs_path);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return paths;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Adds `{ type: "sourcemap", code_file, debug_id }` to event.debug_meta.images
|
|
81
|
+
* for each file in the event's frames that has a debug id, after the images
|
|
82
|
+
* already there, so the server can map those frames to source. Never throws.
|
|
83
|
+
*/
|
|
84
|
+
function addDebugImages(event) {
|
|
85
|
+
try {
|
|
86
|
+
const ids = debugIdsByFile();
|
|
87
|
+
if (!ids.size)
|
|
88
|
+
return;
|
|
89
|
+
const meta = (0, utils_js_1.isObject)(event.debug_meta)
|
|
90
|
+
? event.debug_meta
|
|
91
|
+
: {};
|
|
92
|
+
const images = Array.isArray(meta.images)
|
|
93
|
+
? [...meta.images]
|
|
94
|
+
: [];
|
|
95
|
+
const named = new Set();
|
|
96
|
+
for (const image of images) {
|
|
97
|
+
if ((0, utils_js_1.isObject)(image) && image.type === "sourcemap")
|
|
98
|
+
named.add(image.code_file);
|
|
99
|
+
}
|
|
100
|
+
const before = images.length;
|
|
101
|
+
for (const path of framePaths(event)) {
|
|
102
|
+
const id = ids.get(path);
|
|
103
|
+
if (id !== undefined && !named.has(path))
|
|
104
|
+
images.push({ type: "sourcemap", code_file: path, debug_id: id });
|
|
105
|
+
}
|
|
106
|
+
if (images.length > before)
|
|
107
|
+
event.debug_meta = { ...meta, images: images };
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// A malformed _sentryDebugIds or event costs the images, nothing more.
|
|
111
|
+
}
|
|
112
|
+
}
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
import type { CheckSchema, Handle, SchemaSpec } from "./schema.js";
|
|
1
2
|
import type { Breadcrumb, CaptureContext, Event, InitOptions, Level, Primitive, User } from "./types.js";
|
|
2
3
|
export { withScope } from "./hub.js";
|
|
4
|
+
export { defineSchema } from "./schema.js";
|
|
5
|
+
export type { CategoryOf, CategorySettings, CheckSchema, FieldOf, FieldSettings, FieldType, FieldValue, Handle, LevelOf, LevelSettings, Like, RuleMatch, RuleSubject, SchemaColor, SchemaContext, SchemaRule, SchemaSpec, Severity, } from "./schema.js";
|
|
3
6
|
export type { Scope } from "./scope.js";
|
|
4
7
|
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";
|
|
8
|
+
export type { Breadcrumb, BreadcrumbHint, CaptureContext, DebugImage, Event, EventHint, Exception, InitOptions, IntegrationToggles, Level, Mechanism, Primitive, RequestInfo, ScopeData, SdkInfo, SentryLevel, StackFrame, Transport, TransportRequest, TransportResponse, User, } from "./types.js";
|
|
6
9
|
/**
|
|
7
10
|
* Starts the SDK. Call it once, as early as you can. Calling it again replaces
|
|
8
11
|
* the earlier setup. Without a valid DSN, or with enabled: false, nothing is
|
|
9
12
|
* sent and nothing is patched.
|
|
13
|
+
*
|
|
14
|
+
* With a `schema` from defineSchema, its levels are sent as the Sentry level
|
|
15
|
+
* each ranks like, and the capture functions it returns take only the
|
|
16
|
+
* schema's levels, categories and fields.
|
|
10
17
|
*/
|
|
11
|
-
export declare function init(options?: InitOptions
|
|
18
|
+
export declare function init<const S extends SchemaSpec & CheckSchema<S> = SchemaSpec>(options?: InitOptions & {
|
|
19
|
+
schema?: S;
|
|
20
|
+
}): Handle<S>;
|
|
12
21
|
/** Sends an error (or anything thrown). Returns the event id. */
|
|
13
22
|
export declare function captureException(error: unknown, context?: CaptureContext): string;
|
|
14
23
|
/**
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SDK_VERSION = exports.SDK_NAME = exports.withScope = void 0;
|
|
3
|
+
exports.SDK_VERSION = exports.SDK_NAME = exports.defineSchema = exports.withScope = void 0;
|
|
4
4
|
exports.init = init;
|
|
5
5
|
exports.captureException = captureException;
|
|
6
6
|
exports.captureMessage = captureMessage;
|
|
@@ -19,14 +19,11 @@ const hub_js_1 = require("./hub.js");
|
|
|
19
19
|
const utils_js_1 = require("./utils.js");
|
|
20
20
|
var hub_js_2 = require("./hub.js");
|
|
21
21
|
Object.defineProperty(exports, "withScope", { enumerable: true, get: function () { return hub_js_2.withScope; } });
|
|
22
|
+
var schema_js_1 = require("./schema.js");
|
|
23
|
+
Object.defineProperty(exports, "defineSchema", { enumerable: true, get: function () { return schema_js_1.defineSchema; } });
|
|
22
24
|
var version_js_1 = require("./version.js");
|
|
23
25
|
Object.defineProperty(exports, "SDK_NAME", { enumerable: true, get: function () { return version_js_1.SDK_NAME; } });
|
|
24
26
|
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
27
|
function init(options = {}) {
|
|
31
28
|
try {
|
|
32
29
|
const carrier = (0, hub_js_1.getCarrier)();
|
|
@@ -45,6 +42,7 @@ function init(options = {}) {
|
|
|
45
42
|
(0, utils_js_1.safely)(() => console.error("[StatOSS] init failed:", error));
|
|
46
43
|
}
|
|
47
44
|
}
|
|
45
|
+
return HANDLE;
|
|
48
46
|
}
|
|
49
47
|
/** Sends an error (or anything thrown). Returns the event id. */
|
|
50
48
|
function captureException(error, context) {
|
|
@@ -61,6 +59,13 @@ function captureMessage(message, levelOrContext) {
|
|
|
61
59
|
function captureEvent(event, context) {
|
|
62
60
|
return (0, hub_js_1.captureWith)(undefined, (client, scope) => client.captureEvent(event, context, scope));
|
|
63
61
|
}
|
|
62
|
+
// What init returns: the same functions, typed by the schema. The client
|
|
63
|
+
// reads a context's fields whichever function it came through.
|
|
64
|
+
const HANDLE = Object.freeze({
|
|
65
|
+
captureException,
|
|
66
|
+
captureMessage,
|
|
67
|
+
captureEvent,
|
|
68
|
+
});
|
|
64
69
|
/** Pass null to forget the user. */
|
|
65
70
|
function setUser(user) {
|
|
66
71
|
(0, utils_js_1.safely)(() => (0, hub_js_1.getCurrentScope)().setUser(user));
|
package/dist/cjs/levels.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import type { Logger } from "./logger.js";
|
|
2
2
|
import type { SentryLevel } from "./types.js";
|
|
3
3
|
export declare const SENTRY_LEVELS: readonly SentryLevel[];
|
|
4
|
+
export declare const LEVEL_RULE = "use 1 to 32 lowercase letters, digits, dots, dashes or underscores";
|
|
5
|
+
export declare const CATEGORY_RULE = "use 1 to 64 lowercase letters, digits, dots, dashes or underscores";
|
|
4
6
|
export declare const LEVEL_TAG = "statoss.level";
|
|
5
7
|
export declare const CATEGORY_TAG = "statoss.category";
|
|
6
8
|
export declare function isSentryLevel(value: unknown): value is SentryLevel;
|
|
7
|
-
export declare function
|
|
9
|
+
export declare function isLevelName(value: unknown): value is string;
|
|
10
|
+
export declare function isCategoryName(value: unknown): value is string;
|
|
8
11
|
/** Keeps the entries of init's `levels` that are usable, logging the rest in debug mode. */
|
|
9
12
|
export declare function readLevels(input: unknown, logger: Logger): Record<string, SentryLevel>;
|
|
10
13
|
export interface LevelChoice {
|
package/dist/cjs/levels.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CATEGORY_TAG = exports.LEVEL_TAG = exports.SENTRY_LEVELS = void 0;
|
|
3
|
+
exports.CATEGORY_TAG = exports.LEVEL_TAG = exports.CATEGORY_RULE = exports.LEVEL_RULE = exports.SENTRY_LEVELS = void 0;
|
|
4
4
|
exports.isSentryLevel = isSentryLevel;
|
|
5
|
-
exports.
|
|
5
|
+
exports.isLevelName = isLevelName;
|
|
6
|
+
exports.isCategoryName = isCategoryName;
|
|
6
7
|
exports.readLevels = readLevels;
|
|
7
8
|
exports.chooseLevel = chooseLevel;
|
|
8
9
|
exports.readCategory = readCategory;
|
|
@@ -14,16 +15,26 @@ exports.SENTRY_LEVELS = [
|
|
|
14
15
|
"info",
|
|
15
16
|
"debug",
|
|
16
17
|
];
|
|
17
|
-
/**
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Names of your own: lowercase letters, digits, dots, dashes and
|
|
20
|
+
* underscores. A level is at most 32 characters, as the server keeps it; a
|
|
21
|
+
* category at most 64.
|
|
22
|
+
*/
|
|
23
|
+
const LEVEL_RE = /^[a-z0-9._-]{1,32}$/;
|
|
24
|
+
const CATEGORY_RE = /^[a-z0-9._-]{1,64}$/;
|
|
25
|
+
exports.LEVEL_RULE = "use 1 to 32 lowercase letters, digits, dots, dashes or underscores";
|
|
26
|
+
exports.CATEGORY_RULE = "use 1 to 64 lowercase letters, digits, dots, dashes or underscores";
|
|
19
27
|
exports.LEVEL_TAG = "statoss.level";
|
|
20
28
|
exports.CATEGORY_TAG = "statoss.category";
|
|
21
29
|
function isSentryLevel(value) {
|
|
22
30
|
return (typeof value === "string" &&
|
|
23
31
|
exports.SENTRY_LEVELS.includes(value));
|
|
24
32
|
}
|
|
25
|
-
function
|
|
26
|
-
return typeof value === "string" &&
|
|
33
|
+
function isLevelName(value) {
|
|
34
|
+
return typeof value === "string" && LEVEL_RE.test(value);
|
|
35
|
+
}
|
|
36
|
+
function isCategoryName(value) {
|
|
37
|
+
return typeof value === "string" && CATEGORY_RE.test(value);
|
|
27
38
|
}
|
|
28
39
|
/** Keeps the entries of init's `levels` that are usable, logging the rest in debug mode. */
|
|
29
40
|
function readLevels(input, logger) {
|
|
@@ -38,8 +49,8 @@ function readLevels(input, logger) {
|
|
|
38
49
|
if (isSentryLevel(name)) {
|
|
39
50
|
logger.warn(`Ignored level "${name}" in \`levels\`: it is already a Sentry level.`);
|
|
40
51
|
}
|
|
41
|
-
else if (!
|
|
42
|
-
logger.warn(`Ignored level "${name}" in \`levels\`:
|
|
52
|
+
else if (!isLevelName(name)) {
|
|
53
|
+
logger.warn(`Ignored level "${name}" in \`levels\`: ${exports.LEVEL_RULE}.`);
|
|
43
54
|
}
|
|
44
55
|
else if (!isSentryLevel(level)) {
|
|
45
56
|
logger.warn(`Ignored level "${name}" in \`levels\`: map it to fatal, error, warning, info or debug.`);
|
|
@@ -60,18 +71,18 @@ function chooseLevel(candidates, levels, fallback, logger) {
|
|
|
60
71
|
continue;
|
|
61
72
|
if (isSentryLevel(candidate))
|
|
62
73
|
return { level: candidate };
|
|
63
|
-
if (
|
|
74
|
+
if (isLevelName(candidate)) {
|
|
64
75
|
return { level: levels[candidate] ?? "error", custom: candidate };
|
|
65
76
|
}
|
|
66
|
-
logger.warn(`Ignored level "${(0, utils_js_1.safeString)(candidate)}": use a Sentry level or 1 to
|
|
77
|
+
logger.warn(`Ignored level "${(0, utils_js_1.safeString)(candidate)}": use a Sentry level or 1 to 32 lowercase letters, digits, dots, dashes or underscores.`);
|
|
67
78
|
}
|
|
68
79
|
return { level: fallback };
|
|
69
80
|
}
|
|
70
81
|
function readCategory(value, logger) {
|
|
71
82
|
if (value === undefined || value === null)
|
|
72
83
|
return undefined;
|
|
73
|
-
if (
|
|
84
|
+
if (isCategoryName(value))
|
|
74
85
|
return value;
|
|
75
|
-
logger.warn(`Ignored category "${(0, utils_js_1.safeString)(value)}":
|
|
86
|
+
logger.warn(`Ignored category "${(0, utils_js_1.safeString)(value)}": ${exports.CATEGORY_RULE}.`);
|
|
76
87
|
return undefined;
|
|
77
88
|
}
|