@statoss/sdk 0.0.0-stage → 0.1.1
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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 StatOSS
|
|
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
CHANGED
|
@@ -1,3 +1,412 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @statoss/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Error tracking for [StatOSS](https://statoss.com). One package for browsers,
|
|
4
|
+
Node, Bun, Deno, Cloudflare Workers and Vercel Edge, with TypeScript types
|
|
5
|
+
included and no dependencies.
|
|
6
|
+
|
|
7
|
+
The SDK speaks Sentry's protocol (envelopes, event schema, rate limits,
|
|
8
|
+
client reports), so it can also send to Sentry, and Sentry's own SDKs can
|
|
9
|
+
send to StatOSS.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add @statoss/sdk # or: npm install, yarn add, bun add
|
|
15
|
+
deno add npm:@statoss/sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Node 20.16 or later on 20, or 22.3 or later. Older Node lacks
|
|
19
|
+
`process.getBuiltinModule`, which the SDK uses to reach `AsyncLocalStorage`
|
|
20
|
+
without a static import that would break browser and edge bundles; without it,
|
|
21
|
+
`withScope` could not keep concurrent requests apart (see
|
|
22
|
+
[Scopes](#scopes)).
|
|
23
|
+
|
|
24
|
+
## Start
|
|
25
|
+
|
|
26
|
+
Call `init` once, as early as you can, with your app's DSN. The DSN is on the
|
|
27
|
+
app's setup page in StatOSS and looks like
|
|
28
|
+
`https://<key>@ingest.statoss.com/<app number>`.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import * as StatOSS from "@statoss/sdk";
|
|
32
|
+
|
|
33
|
+
StatOSS.init({
|
|
34
|
+
dsn: "https://0123456789abcdef0123456789abcdef@ingest.statoss.com/42",
|
|
35
|
+
environment: "production",
|
|
36
|
+
release: "shop@1.4.0",
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
await chargeCard(order);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
StatOSS.captureException(error, { tags: { order: order.id } });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
StatOSS.captureMessage("Nightly export finished late", "warning");
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Without a DSN, with an invalid one, or with `enabled: false`, the SDK stays
|
|
49
|
+
off: nothing is sent and nothing is patched. The SDK never throws into your
|
|
50
|
+
app and never logs, unless you pass `debug: true`.
|
|
51
|
+
|
|
52
|
+
## Browser
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import * as StatOSS from "@statoss/sdk";
|
|
56
|
+
|
|
57
|
+
StatOSS.init({
|
|
58
|
+
dsn: "https://<key>@ingest.statoss.com/<app number>",
|
|
59
|
+
release: "web@5.0.0",
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Uncaught errors and unhandled rejections are captured. Breadcrumbs record
|
|
64
|
+
console calls, fetch requests, history navigation and clicks. Each event
|
|
65
|
+
carries the page URL, the user agent, and the browser and OS read from it.
|
|
66
|
+
|
|
67
|
+
The key travels in the URL query and the body as `text/plain`, so sending
|
|
68
|
+
needs no CORS preflight. Small envelopes use `keepalive`, so an error
|
|
69
|
+
captured just before the page unloads still arrives.
|
|
70
|
+
|
|
71
|
+
Script tags from other origins only report "Script error." unless they are
|
|
72
|
+
served with CORS headers and loaded with `crossorigin="anonymous"`.
|
|
73
|
+
|
|
74
|
+
## Node
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import * as StatOSS from "@statoss/sdk";
|
|
78
|
+
|
|
79
|
+
StatOSS.init({
|
|
80
|
+
dsn: process.env.STATOSS_DSN,
|
|
81
|
+
environment: process.env.NODE_ENV,
|
|
82
|
+
release: process.env.RELEASE,
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
- An uncaught exception is captured at level `fatal`. If your app has no
|
|
87
|
+
`uncaughtException` listener of its own, the SDK prints the error, sets the
|
|
88
|
+
exit code to 1, sends the event (2 seconds at most) and exits with 1, as Node
|
|
89
|
+
would have. If it has one, your listener decides.
|
|
90
|
+
- An unhandled rejection is captured and then handled as Node's
|
|
91
|
+
`--unhandled-rejections` mode says, which the SDK reads from the command
|
|
92
|
+
line and `NODE_OPTIONS`:
|
|
93
|
+
|
|
94
|
+
| Mode | After capturing |
|
|
95
|
+
| ---------------------- | -------------------------------------------------------------------------------------------------- |
|
|
96
|
+
| `throw` (the default) | Raised as an uncaught exception: your `uncaughtException` listener gets it, or the process exits 1 |
|
|
97
|
+
| `strict` | The same; Node raises it itself |
|
|
98
|
+
| `warn-with-error-code` | Printed; the process keeps running and later exits with 1 |
|
|
99
|
+
| `warn`, `none` | The process keeps running |
|
|
100
|
+
|
|
101
|
+
An `unhandledRejection` listener of your own decides instead. To keep
|
|
102
|
+
running after unhandled rejections, as Sentry's default does, start Node
|
|
103
|
+
with `--unhandled-rejections=warn`.
|
|
104
|
+
|
|
105
|
+
- `server_name` is the host name. Events also carry the Node version, the OS
|
|
106
|
+
and the CPU architecture.
|
|
107
|
+
- `withScope` uses `AsyncLocalStorage`, so what you set inside one stays with
|
|
108
|
+
that request across `await`s.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
app.use((req, res, next) => {
|
|
112
|
+
StatOSS.withScope((scope) => {
|
|
113
|
+
scope.setUser({ id: req.user?.id });
|
|
114
|
+
scope.setTag("route", req.route?.path);
|
|
115
|
+
next();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Next.js
|
|
121
|
+
|
|
122
|
+
Server side, in `instrumentation.ts` at the project root (or in `src/`). This
|
|
123
|
+
runs in both the Node.js and the Edge runtime.
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import * as StatOSS from "@statoss/sdk";
|
|
127
|
+
import type { Instrumentation } from "next";
|
|
128
|
+
|
|
129
|
+
export function register() {
|
|
130
|
+
StatOSS.init({
|
|
131
|
+
dsn: process.env.STATOSS_DSN,
|
|
132
|
+
environment: process.env.NODE_ENV,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const onRequestError: Instrumentation.onRequestError = async (
|
|
137
|
+
error,
|
|
138
|
+
request,
|
|
139
|
+
context,
|
|
140
|
+
) => {
|
|
141
|
+
StatOSS.captureException(error, {
|
|
142
|
+
tags: {
|
|
143
|
+
route: context.routePath,
|
|
144
|
+
routeType: context.routeType,
|
|
145
|
+
method: request.method,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
// The Edge runtime may stop once the response is sent.
|
|
149
|
+
await StatOSS.flush(2000);
|
|
150
|
+
};
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Browser side, in `instrumentation-client.ts`:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import * as StatOSS from "@statoss/sdk";
|
|
157
|
+
|
|
158
|
+
StatOSS.init({ dsn: process.env.NEXT_PUBLIC_STATOSS_DSN });
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Errors that an error boundary catches never reach the browser's global
|
|
162
|
+
handler, so report them from `app/error.tsx` and `app/global-error.tsx`:
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
"use client";
|
|
166
|
+
|
|
167
|
+
import * as StatOSS from "@statoss/sdk";
|
|
168
|
+
import { useEffect } from "react";
|
|
169
|
+
|
|
170
|
+
export default function Error({
|
|
171
|
+
error,
|
|
172
|
+
}: {
|
|
173
|
+
error: Error & { digest?: string };
|
|
174
|
+
}) {
|
|
175
|
+
useEffect(() => {
|
|
176
|
+
StatOSS.captureException(error, { tags: { digest: error.digest } });
|
|
177
|
+
}, [error]);
|
|
178
|
+
return <p>Something went wrong.</p>;
|
|
179
|
+
}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Bun
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import * as StatOSS from "@statoss/sdk";
|
|
186
|
+
|
|
187
|
+
StatOSS.init({ dsn: Bun.env.STATOSS_DSN });
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Bun behaves as Node does above, including uncaught exceptions and unhandled
|
|
191
|
+
rejections.
|
|
192
|
+
|
|
193
|
+
## Deno
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import * as StatOSS from "npm:@statoss/sdk";
|
|
197
|
+
|
|
198
|
+
StatOSS.init({ dsn: Deno.env.get("STATOSS_DSN") });
|
|
199
|
+
|
|
200
|
+
Deno.serve((request) => {
|
|
201
|
+
return StatOSS.withScope((scope) => {
|
|
202
|
+
scope.setTag("path", new URL(request.url).pathname);
|
|
203
|
+
return handle(request);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Run with `--allow-net=ingest.statoss.com` (and `--allow-env` for the DSN).
|
|
209
|
+
An uncaught error or unhandled rejection is captured at level `fatal` and
|
|
210
|
+
sent, then Deno prints it and exits as it would have. A listener of yours that
|
|
211
|
+
calls `event.preventDefault()` first keeps the process running. `server_name`
|
|
212
|
+
is set only when the `sys` permission is already granted; the SDK never asks.
|
|
213
|
+
|
|
214
|
+
## Cloudflare Workers
|
|
215
|
+
|
|
216
|
+
Workers have no global error hook, so capture in your handler and hand the
|
|
217
|
+
sending to `waitUntil`, which keeps the Worker alive until it is done.
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
import * as StatOSS from "@statoss/sdk";
|
|
221
|
+
|
|
222
|
+
let started = false;
|
|
223
|
+
|
|
224
|
+
export default {
|
|
225
|
+
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
|
|
226
|
+
if (!started) {
|
|
227
|
+
StatOSS.init({ dsn: env.STATOSS_DSN, release: env.RELEASE });
|
|
228
|
+
started = true;
|
|
229
|
+
}
|
|
230
|
+
try {
|
|
231
|
+
return await handle(request, env);
|
|
232
|
+
} catch (error) {
|
|
233
|
+
StatOSS.captureException(error, {
|
|
234
|
+
tags: { path: new URL(request.url).pathname },
|
|
235
|
+
});
|
|
236
|
+
return new Response("Something went wrong", { status: 500 });
|
|
237
|
+
} finally {
|
|
238
|
+
ctx.waitUntil(StatOSS.flush(2000));
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
};
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
`init` does no I/O, timers or random numbers, so it may also run at the top of
|
|
245
|
+
the module, where Workers forbid those. Turn on the `nodejs_compat`
|
|
246
|
+
compatibility flag for `withScope` to keep requests apart across `await`s
|
|
247
|
+
(see [Scopes](#scopes)).
|
|
248
|
+
|
|
249
|
+
## Vercel Edge
|
|
250
|
+
|
|
251
|
+
The Edge runtime has no global error hook either: capture where you handle
|
|
252
|
+
errors and `await StatOSS.flush(2000)` (or pass it to `waitUntil`) before
|
|
253
|
+
returning. In Next.js, the `instrumentation.ts` above covers it.
|
|
254
|
+
|
|
255
|
+
## Custom levels and categories
|
|
256
|
+
|
|
257
|
+
Sentry has five levels: `fatal`, `error`, `warning`, `info` and `debug`.
|
|
258
|
+
StatOSS lets you use your own words as well, and adds a category. Both are
|
|
259
|
+
sent as tags next to the nearest Sentry level, so Sentry and any Sentry-based
|
|
260
|
+
tool still read the event normally.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
StatOSS.init({
|
|
264
|
+
dsn: "...",
|
|
265
|
+
levels: {
|
|
266
|
+
"payment-failed": "error",
|
|
267
|
+
"slow-query": "warning",
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
StatOSS.captureMessage("Card declined", "payment-failed");
|
|
272
|
+
// level "error", tags { "statoss.level": "payment-failed" }
|
|
273
|
+
|
|
274
|
+
StatOSS.captureException(error, {
|
|
275
|
+
level: "slow-query",
|
|
276
|
+
category: "db.postgres",
|
|
277
|
+
});
|
|
278
|
+
// level "warning", tags { "statoss.level": "slow-query", "statoss.category": "db.postgres" }
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
- `level` takes a Sentry level or any name of your own. A name that is not in
|
|
282
|
+
`levels` is sent as `error`.
|
|
283
|
+
- `category` sets the `statoss.category` tag.
|
|
284
|
+
- Names are 1 to 64 characters of lowercase letters, digits, dots, dashes and
|
|
285
|
+
underscores. Others are ignored; with `debug: true` the SDK logs each one.
|
|
286
|
+
- StatOSS filters and groups on these tags like any other.
|
|
287
|
+
|
|
288
|
+
## What is captured
|
|
289
|
+
|
|
290
|
+
| Integration | Runtimes | What it does |
|
|
291
|
+
| ---------------- | ------------------------ | ----------------------------------------------------- |
|
|
292
|
+
| `globalHandlers` | browser, Node, Bun, Deno | Uncaught errors and unhandled rejections |
|
|
293
|
+
| `console` | all | A breadcrumb per console call |
|
|
294
|
+
| `fetch` | all | A breadcrumb per request: method, URL, status |
|
|
295
|
+
| `navigation` | browser | A breadcrumb per pushState, replaceState and popstate |
|
|
296
|
+
| `clicks` | browser | A breadcrumb per click, with a short selector |
|
|
297
|
+
| `dedupe` | all | Drops an event identical to the one just before it |
|
|
298
|
+
|
|
299
|
+
All are on by default. Turn one off with
|
|
300
|
+
`integrations: { console: false }`.
|
|
301
|
+
|
|
302
|
+
Breadcrumbs keep the last 100. URLs in breadcrumbs and in the page request
|
|
303
|
+
lose their credentials, fragment, and the values of query parameters whose
|
|
304
|
+
names look secret (token, key, session, password and similar). The sender's
|
|
305
|
+
IP address is recorded only with `sendDefaultPii: true` (browsers send
|
|
306
|
+
`{{auto}}`, and the server reads the address).
|
|
307
|
+
|
|
308
|
+
Values in breadcrumbs, extra and contexts are copied only so far: 5 levels
|
|
309
|
+
deep, 1,000 entries per array or object, 8 KiB per string (1 KiB for a
|
|
310
|
+
console breadcrumb). Buffers, typed arrays, ArrayBuffers, Maps and Sets are
|
|
311
|
+
written as their type and size, such as `[Uint8Array(30000000)]`, without
|
|
312
|
+
reading them, so logging a large value costs the app next to nothing.
|
|
313
|
+
|
|
314
|
+
## Scopes
|
|
315
|
+
|
|
316
|
+
`setUser`, `setTag` and the other setters change the current scope, which
|
|
317
|
+
every later event carries. `withScope(callback)` gives the callback its own
|
|
318
|
+
copy, so what it sets stays with the events captured inside it.
|
|
319
|
+
|
|
320
|
+
Where the runtime has `AsyncLocalStorage` (Node 20.16+ and 22.3+, Bun, Deno,
|
|
321
|
+
Vercel Edge, Next.js, Workers with `nodejs_compat`), the copy follows the
|
|
322
|
+
callback through every `await`, and concurrent requests never see each other's
|
|
323
|
+
user or tags.
|
|
324
|
+
|
|
325
|
+
Elsewhere (browsers, Workers without `nodejs_compat`) the copy is current only
|
|
326
|
+
until the callback first awaits. It is never left current for other work to
|
|
327
|
+
pick up, so one request's user cannot end up on another's error, but after an
|
|
328
|
+
`await` the module-level functions see the outer scope. Capture through the
|
|
329
|
+
scope the callback gets instead; with `debug: true` the SDK warns once.
|
|
330
|
+
|
|
331
|
+
```ts
|
|
332
|
+
await StatOSS.withScope(async (scope) => {
|
|
333
|
+
scope.setUser({ id: user.id });
|
|
334
|
+
await loadCart();
|
|
335
|
+
scope.captureMessage("Cart was empty", "warning"); // carries the user anywhere
|
|
336
|
+
});
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
## API
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
init(options): void
|
|
343
|
+
captureException(error, context?): string // returns the event id
|
|
344
|
+
captureMessage(message, levelOrContext?): string
|
|
345
|
+
captureEvent(event, context?): string
|
|
346
|
+
setUser(user | null), setTag(key, value), setTags(tags)
|
|
347
|
+
setContext(name, context | null), setExtra(key, value)
|
|
348
|
+
addBreadcrumb(breadcrumb)
|
|
349
|
+
withScope(callback): result // callback gets an isolated scope
|
|
350
|
+
flush(timeoutMs = 2000): Promise<boolean> // true once nothing is left to send
|
|
351
|
+
close(timeoutMs = 2000): Promise<boolean> // flush, then stop and unpatch
|
|
352
|
+
lastEventId(): string | undefined
|
|
353
|
+
|
|
354
|
+
// on the scope withScope passes
|
|
355
|
+
scope.setUser, setTag, setTags, setExtra, setExtras, setContext, setLevel,
|
|
356
|
+
setFingerprint, addBreadcrumb, clearBreadcrumbs, clear
|
|
357
|
+
scope.captureException, captureMessage, captureEvent // with this scope
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
`context` is `{ level, tags, extra, contexts, user, fingerprint, category }`.
|
|
361
|
+
It wins over the scope for that one event.
|
|
362
|
+
|
|
363
|
+
### Options
|
|
364
|
+
|
|
365
|
+
| Option | Default | Meaning |
|
|
366
|
+
| ------------------ | -------------- | ------------------------------------------------------ |
|
|
367
|
+
| `dsn` | | Where to send. Without it the SDK stays off. |
|
|
368
|
+
| `environment` | `"production"` | |
|
|
369
|
+
| `release`, `dist` | | |
|
|
370
|
+
| `serverName` | host name | Node and Bun only, unless set |
|
|
371
|
+
| `sampleRate` | `1` | Share of events sent, 0 to 1 |
|
|
372
|
+
| `beforeSend` | | `(event, hint) => event \| null`, may be async |
|
|
373
|
+
| `beforeBreadcrumb` | | `(breadcrumb, hint) => breadcrumb \| null` |
|
|
374
|
+
| `maxBreadcrumbs` | `100` | 100 at most |
|
|
375
|
+
| `sendDefaultPii` | `false` | Record the sender's IP address |
|
|
376
|
+
| `tags` | | Tags on every event |
|
|
377
|
+
| `initialScope` | | `{ tags, extra, contexts, user, level, fingerprint }` |
|
|
378
|
+
| `levels` | | Your level names and the Sentry level each is sent as |
|
|
379
|
+
| `integrations` | all on | See above |
|
|
380
|
+
| `enabled` | `true` | |
|
|
381
|
+
| `debug` | `false` | Log what the SDK does |
|
|
382
|
+
| `transport` | fetch | `(request) => Promise<{ status, headers }>`, for tests |
|
|
383
|
+
|
|
384
|
+
## Sending to Sentry
|
|
385
|
+
|
|
386
|
+
Use a Sentry DSN instead. Events, client reports, rate limits (including a
|
|
387
|
+
used-up quota) and the `X-Sentry-Auth` header all follow Sentry's protocol.
|
|
388
|
+
Custom levels arrive as Sentry levels with the `statoss.level` tag.
|
|
389
|
+
|
|
390
|
+
## Network behaviour
|
|
391
|
+
|
|
392
|
+
Envelopes go to `https://<host>/api/<app number>/envelope/`, at most 5 at a
|
|
393
|
+
time and 30 queued or in flight; more are dropped and counted. When the server answers 429, the SDK holds back
|
|
394
|
+
the limited categories for as long as `Retry-After` or `X-Sentry-Rate-Limits`
|
|
395
|
+
says. Events it could not send (queue full, rate limited, network error,
|
|
396
|
+
sampled out, dropped by `beforeSend`) are counted and reported as Sentry
|
|
397
|
+
client reports, on `flush` and otherwise at most every 30 seconds.
|
|
398
|
+
|
|
399
|
+
## Building
|
|
400
|
+
|
|
401
|
+
From the repository root:
|
|
402
|
+
|
|
403
|
+
```sh
|
|
404
|
+
pnpm --dir sdk build # ESM in sdk/dist/esm, CommonJS in sdk/dist/cjs, .d.ts in both
|
|
405
|
+
pnpm test # runs sdk/test with the rest of the repository's tests
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
The build uses the repository's TypeScript; the package has no dependencies.
|
|
409
|
+
|
|
410
|
+
## License
|
|
411
|
+
|
|
412
|
+
MIT
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type Logger } from "./logger.js";
|
|
2
|
+
import { type RuntimeInfo } from "./runtime.js";
|
|
3
|
+
import { type Scope } from "./scope.js";
|
|
4
|
+
import type { Breadcrumb, BreadcrumbHint, CaptureContext, Event, InitOptions } from "./types.js";
|
|
5
|
+
export declare class Client {
|
|
6
|
+
readonly logger: Logger;
|
|
7
|
+
readonly runtime: RuntimeInfo;
|
|
8
|
+
readonly enabled: boolean;
|
|
9
|
+
readonly integrations: string[];
|
|
10
|
+
private readonly options;
|
|
11
|
+
private readonly dsn?;
|
|
12
|
+
private readonly endpoint?;
|
|
13
|
+
private readonly sender?;
|
|
14
|
+
private readonly levels;
|
|
15
|
+
private readonly sampleRate;
|
|
16
|
+
private readonly maxBreadcrumbs;
|
|
17
|
+
private readonly discarded;
|
|
18
|
+
private readonly pending;
|
|
19
|
+
private readonly teardowns;
|
|
20
|
+
private reportTimer?;
|
|
21
|
+
private previousSignature?;
|
|
22
|
+
private closed;
|
|
23
|
+
constructor(options: InitOptions);
|
|
24
|
+
/** Installs the integrations that apply to this runtime. */
|
|
25
|
+
setup(): void;
|
|
26
|
+
captureException(error: unknown, context: CaptureContext | undefined, scope: Scope): string;
|
|
27
|
+
captureMessage(message: unknown, context: CaptureContext | undefined, scope: Scope): string;
|
|
28
|
+
captureEvent(event: Event, context: CaptureContext | undefined, scope: Scope): string;
|
|
29
|
+
/** Applies beforeBreadcrumb and keeps the breadcrumb on the current scope. */
|
|
30
|
+
addBreadcrumb(breadcrumb: Breadcrumb, hint?: BreadcrumbHint): void;
|
|
31
|
+
/** True for a URL this client sends envelopes to, so the fetch breadcrumbs skip it. */
|
|
32
|
+
isOwnUrl(url: string): boolean;
|
|
33
|
+
/** Counts something not sent, for the next client report. */
|
|
34
|
+
recordLoss(reason: string, category: string, quantity?: number): void;
|
|
35
|
+
sendClientReport(): void;
|
|
36
|
+
/** Waits for captured events to be sent; false if timeoutMs passed first. */
|
|
37
|
+
flush(timeoutMs?: number): Promise<boolean>;
|
|
38
|
+
/** Flushes, then removes the integrations. Nothing is captured afterwards. */
|
|
39
|
+
close(timeoutMs?: number): Promise<boolean>;
|
|
40
|
+
/** Removes the integrations at once, without waiting. */
|
|
41
|
+
teardown(): void;
|
|
42
|
+
private flushReportsWhenHidden;
|
|
43
|
+
private capture;
|
|
44
|
+
/** Merges scope, context and options into the event, in Sentry's field order. */
|
|
45
|
+
private prepare;
|
|
46
|
+
private process;
|
|
47
|
+
/** Makes what beforeSend handed back valid again: a Sentry level, string tags, a sane size. */
|
|
48
|
+
private finish;
|
|
49
|
+
private send;
|
|
50
|
+
}
|