@edraj/sauron-node 1.4.0 → 1.6.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/CHANGELOG.md +50 -0
- package/COPYING +674 -0
- package/LICENSE +160 -656
- package/README.md +154 -12
- package/dist/client.js +15 -3
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/transaction-extra.d.ts +36 -0
- package/dist/transaction-extra.js +53 -0
- package/dist/transport.js +1 -1
- package/dist/types.d.ts +37 -2
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -56,13 +56,13 @@ adding `Content-Encoding: gzip` once the body crosses the gzip threshold.
|
|
|
56
56
|
|
|
57
57
|
## Configuration
|
|
58
58
|
|
|
59
|
-
`init(options)` takes a single `InitOptions` object. Every field except `dsn`
|
|
60
|
-
optional.
|
|
59
|
+
`init(options)` takes a single `InitOptions` object. Every field except `dsn`
|
|
60
|
+
and `release` is optional.
|
|
61
61
|
|
|
62
62
|
| Option | Type | Default | Description |
|
|
63
63
|
| --- | --- | --- | --- |
|
|
64
|
-
| `dsn` | `string` | — (**required**) | `https://<public_key>@<host>/<environment_id>`. A non-string throws `Error`; a malformed value throws `DsnError`. |
|
|
65
|
-
| `release` | `string
|
|
64
|
+
| `dsn` | `string` | — (**required**) | `https://<public_key>@<host>/<environment_id>`. A non-string or empty value throws `Error`; a malformed value throws `DsnError`. |
|
|
65
|
+
| `release` | `string` | — (**required**) | The app version this build reports as, written to `header.release`. Missing or whitespace-only throws `Error`. Trimmed before it is sent. |
|
|
66
66
|
| `tags` | `Record<string, string>` | `{}` | Default tags seeded into the global scope at init. |
|
|
67
67
|
| `contexts` | `Record<string, unknown>` | `{}` | Default named dev context blocks seeded into the global scope. Distinct from the machine `context` (device/os/app/runtime). |
|
|
68
68
|
| `extra` | `Record<string, unknown>` | `{}` | Default free-form extra values seeded into the global scope. |
|
|
@@ -140,11 +140,12 @@ function init(options: InitOptions): SauronClient
|
|
|
140
140
|
| `options` | `InitOptions` | — (required) | See [Configuration](#configuration). |
|
|
141
141
|
|
|
142
142
|
Returns the `SauronClient` it created, and installs it as the active client.
|
|
143
|
-
Throws `Error` when `options.dsn` is not a string,
|
|
144
|
-
is
|
|
143
|
+
Throws `Error` when `options.dsn` is missing/empty/not a string, when
|
|
144
|
+
`options.release` is missing or blank (**required as of v1.6.0**), and
|
|
145
|
+
`DsnError` when the DSN is malformed.
|
|
145
146
|
|
|
146
147
|
```ts
|
|
147
|
-
const client = init({ dsn: 'https://pk@ingest.example.com/42' });
|
|
148
|
+
const client = init({ dsn: 'https://pk@ingest.example.com/42', release: 'api@1.4.2' });
|
|
148
149
|
```
|
|
149
150
|
|
|
150
151
|
### `getClient()`
|
|
@@ -275,10 +276,28 @@ function trackTransaction(input: TransactionInput): void
|
|
|
275
276
|
| `input.http_status` | `number` | omitted | e.g. `200`. |
|
|
276
277
|
| `input.url` | `string` | omitted | Request URL/path. |
|
|
277
278
|
| `input.distinct_id` | `string` | scope user's `id`, else omitted | Explicit value wins over the scope. |
|
|
279
|
+
| `input.tags` | `Record<string, string>` | omitted | Indexed string→string labels. Filter with `@tag.key:value` on the Transactions page. |
|
|
280
|
+
| `input.extra` | `Record<string, unknown>` | omitted | Freeform JSON — request body, response body, SQL text, row counts. Searchable with `extra.key:value`. |
|
|
278
281
|
|
|
279
282
|
Emits a `transaction` item. Absent optional fields are omitted from the wire JSON
|
|
280
283
|
rather than serialized as `null`. Returns `void`.
|
|
281
284
|
|
|
285
|
+
**`tags` and `extra` are per-call only.** Unlike `track()` and
|
|
286
|
+
`captureException()`, a transaction does **not** inherit the scope:
|
|
287
|
+
`setTag()` / `setExtra()` defaults are not merged in. Transactions are the
|
|
288
|
+
highest-volume signal a service emits — one per request and per query — so
|
|
289
|
+
inheriting a global blob would write it onto every row.
|
|
290
|
+
|
|
291
|
+
`extra` is serialized and capped at **16 KB**
|
|
292
|
+
(`MAX_TRANSACTION_EXTRA_BYTES`, exported from `transaction-extra`). Past that
|
|
293
|
+
the whole map is replaced with `{ _truncated: true, _bytes: N }` and the
|
|
294
|
+
dashboard says so on the row. The cap is not cosmetic: envelopes are batched,
|
|
295
|
+
and one oversized body would push the whole envelope past the ingest limit and
|
|
296
|
+
drop every unrelated span sent with it.
|
|
297
|
+
|
|
298
|
+
Nothing in `extra` is scrubbed. Use `beforeSend` for redaction, and think twice
|
|
299
|
+
before attaching a body that can carry tokens, passwords or personal data.
|
|
300
|
+
|
|
282
301
|
```ts
|
|
283
302
|
const started = Date.now();
|
|
284
303
|
await handler(req, res);
|
|
@@ -293,6 +312,128 @@ trackTransaction({
|
|
|
293
312
|
});
|
|
294
313
|
```
|
|
295
314
|
|
|
315
|
+
#### Example: an Express route, with request and response bodies
|
|
316
|
+
|
|
317
|
+
`res.json` is wrapped rather than read afterwards, because by the time the
|
|
318
|
+
`finish` event fires the payload is already on the wire and gone.
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
import express from 'express';
|
|
322
|
+
import { trackTransaction } from '@edraj/sauron-node';
|
|
323
|
+
|
|
324
|
+
export function tracedRoute(routeLabel: string, handler: express.RequestHandler) {
|
|
325
|
+
return async (req: express.Request, res: express.Response, next: express.NextFunction) => {
|
|
326
|
+
const started = Date.now();
|
|
327
|
+
let responseBody: unknown;
|
|
328
|
+
|
|
329
|
+
// Capture on the way out. Reading it in `finish` is too late — the body
|
|
330
|
+
// has already been serialized and released by then.
|
|
331
|
+
const json = res.json.bind(res);
|
|
332
|
+
res.json = (body: unknown) => {
|
|
333
|
+
responseBody = body;
|
|
334
|
+
return json(body);
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
res.on('finish', () => {
|
|
338
|
+
trackTransaction({
|
|
339
|
+
name: routeLabel, // 'POST /orders', NOT '/orders/8412'
|
|
340
|
+
op: 'http',
|
|
341
|
+
duration_ms: Date.now() - started,
|
|
342
|
+
http_method: req.method,
|
|
343
|
+
http_status: res.statusCode,
|
|
344
|
+
url: req.originalUrl,
|
|
345
|
+
status: res.statusCode < 400 ? 'ok' : 'error',
|
|
346
|
+
distinct_id: (req as { userId?: string }).userId,
|
|
347
|
+
tags: { route: routeLabel, tier: (req as { plan?: string }).plan ?? 'free' },
|
|
348
|
+
extra: {
|
|
349
|
+
request: req.body,
|
|
350
|
+
response: responseBody,
|
|
351
|
+
query: req.query,
|
|
352
|
+
// Header VALUES are omitted on purpose — `authorization` and
|
|
353
|
+
// `cookie` live there.
|
|
354
|
+
request_headers: Object.keys(req.headers),
|
|
355
|
+
},
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
try {
|
|
360
|
+
await handler(req, res, next);
|
|
361
|
+
} catch (err) {
|
|
362
|
+
next(err);
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const app = express();
|
|
368
|
+
app.post('/orders', tracedRoute('POST /orders', createOrder));
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
On the dashboard: **Transactions → the row → expand**. Both bodies render as a
|
|
372
|
+
JSON tree, and every one of these finds it:
|
|
373
|
+
|
|
374
|
+
```text
|
|
375
|
+
extra.response:~9001 # substring, inside the stored response body
|
|
376
|
+
@tag.route:"POST /orders" # indexed tag
|
|
377
|
+
op:http http.status:>=500 # the failures
|
|
378
|
+
duration:>2s # the slow ones
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
#### Example: a SQL query (`pg`)
|
|
382
|
+
|
|
383
|
+
Put the **statement** in `extra` and keep `name` a stable label — a query with
|
|
384
|
+
literals baked in would mint a new dashboard row per execution.
|
|
385
|
+
|
|
386
|
+
```ts
|
|
387
|
+
import { Pool } from 'pg';
|
|
388
|
+
import { trackTransaction } from '@edraj/sauron-node';
|
|
389
|
+
|
|
390
|
+
const pool = new Pool();
|
|
391
|
+
|
|
392
|
+
export async function tracedQuery<T>(label: string, sql: string, params: unknown[] = []) {
|
|
393
|
+
const started = Date.now();
|
|
394
|
+
try {
|
|
395
|
+
const result = await pool.query<T>(sql, params);
|
|
396
|
+
trackTransaction({
|
|
397
|
+
// The LABEL, not the statement. `op` is free-form on this SDK, so a
|
|
398
|
+
// 'db' op is stored as-is — but note the browser SDK coerces anything
|
|
399
|
+
// outside navigation|http|resource|screen_load|custom to 'custom', so
|
|
400
|
+
// use a tag if you need the two to agree.
|
|
401
|
+
name: label,
|
|
402
|
+
op: 'db',
|
|
403
|
+
duration_ms: Date.now() - started,
|
|
404
|
+
status: 'ok',
|
|
405
|
+
tags: { db: 'postgres', table: 'orders' },
|
|
406
|
+
extra: {
|
|
407
|
+
statement: sql,
|
|
408
|
+
row_count: result.rowCount,
|
|
409
|
+
// Bind PARAMETERS are user data. Log them only if you have decided
|
|
410
|
+
// that is acceptable, or log their shape instead.
|
|
411
|
+
params,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
return result;
|
|
415
|
+
} catch (err) {
|
|
416
|
+
trackTransaction({
|
|
417
|
+
name: label,
|
|
418
|
+
op: 'db',
|
|
419
|
+
duration_ms: Date.now() - started,
|
|
420
|
+
status: 'error',
|
|
421
|
+
tags: { db: 'postgres', table: 'orders' },
|
|
422
|
+
extra: { statement: sql, error: String(err) },
|
|
423
|
+
});
|
|
424
|
+
throw err;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
await tracedQuery(
|
|
429
|
+
'SELECT orders',
|
|
430
|
+
'SELECT id, total FROM orders WHERE user_id = $1 ORDER BY created_at DESC LIMIT 20',
|
|
431
|
+
[userId],
|
|
432
|
+
);
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
Then `@tag.table:orders duration:>500ms` is your slow-query list.
|
|
436
|
+
|
|
296
437
|
### `addBreadcrumb(crumb)`
|
|
297
438
|
|
|
298
439
|
```ts
|
|
@@ -683,7 +824,7 @@ Re-entrancy is guarded, so a throw inside the capture path cannot loop. Prefer
|
|
|
683
824
|
down on `close()`.
|
|
684
825
|
|
|
685
826
|
```ts
|
|
686
|
-
const client = init({ dsn: DSN });
|
|
827
|
+
const client = init({ dsn: DSN, release: 'api@1.4.2' });
|
|
687
828
|
const uninstall = installAutoCapture(client);
|
|
688
829
|
// later
|
|
689
830
|
uninstall();
|
|
@@ -715,7 +856,7 @@ signal fires — if you need to drain HTTP connections first, leave `autoShutdow
|
|
|
715
856
|
off and call `close()` yourself from your own handler.
|
|
716
857
|
|
|
717
858
|
```ts
|
|
718
|
-
const client = init({ dsn: DSN });
|
|
859
|
+
const client = init({ dsn: DSN, release: 'api@1.4.2' });
|
|
719
860
|
const uninstall = installShutdownHooks(client);
|
|
720
861
|
```
|
|
721
862
|
|
|
@@ -948,7 +1089,7 @@ Emit conventions on the wire:
|
|
|
948
1089
|
`distinct_id` fallback from the scope's user id.
|
|
949
1090
|
|
|
950
1091
|
```ts
|
|
951
|
-
init({ dsn: DSN, tags: { service: 'checkout' } }); // layer 1
|
|
1092
|
+
init({ dsn: DSN, release: 'api@1.4.2', tags: { service: 'checkout' } }); // layer 1
|
|
952
1093
|
|
|
953
1094
|
setTag('region', 'eu-west-1'); // layer 2 (global)
|
|
954
1095
|
|
|
@@ -1054,7 +1195,7 @@ import {
|
|
|
1054
1195
|
init, withScope, addBreadcrumb, captureException, trackTransaction, close,
|
|
1055
1196
|
} from '@edraj/sauron-node';
|
|
1056
1197
|
|
|
1057
|
-
init({ dsn: process.env.SAURON_DSN!, autoCaptureUnhandled: true });
|
|
1198
|
+
init({ dsn: process.env.SAURON_DSN!, release: process.env.GIT_SHA, autoCaptureUnhandled: true });
|
|
1058
1199
|
|
|
1059
1200
|
const fastify = Fastify();
|
|
1060
1201
|
const startedAt = new WeakMap<object, number>();
|
|
@@ -1195,4 +1336,5 @@ npm run typecheck # tsc --noEmit
|
|
|
1195
1336
|
|
|
1196
1337
|
## License
|
|
1197
1338
|
|
|
1198
|
-
|
|
1339
|
+
LGPL-3.0-only — GNU Lesser General Public License v3.0. LGPLv3 applies on top of
|
|
1340
|
+
the GNU GPL v3, whose text ships alongside it in `COPYING`.
|
package/dist/client.js
CHANGED
|
@@ -6,8 +6,8 @@ import { parseError } from './stacktrace.js';
|
|
|
6
6
|
import { installAutoCapture, installShutdownHooks } from './autocapture.js';
|
|
7
7
|
import { getCurrentScope, getGlobalScope, normalizeBreadcrumb, } from './scope.js';
|
|
8
8
|
import { normalizeReason, normalizeWorkflowName } from './workflow.js';
|
|
9
|
+
import { capTransactionExtra } from './transaction-extra.js';
|
|
9
10
|
const DEFAULTS = {
|
|
10
|
-
release: null,
|
|
11
11
|
sampleRate: 1,
|
|
12
12
|
flushInterval: 5000,
|
|
13
13
|
maxBatch: 30,
|
|
@@ -18,13 +18,16 @@ const DEFAULTS = {
|
|
|
18
18
|
debug: false,
|
|
19
19
|
};
|
|
20
20
|
function resolveOptions(options) {
|
|
21
|
-
if (!options || typeof options.dsn !== 'string') {
|
|
21
|
+
if (!options || typeof options.dsn !== 'string' || options.dsn.length === 0) {
|
|
22
22
|
throw new Error('[sauron] init requires a { dsn } option');
|
|
23
23
|
}
|
|
24
|
+
if (typeof options.release !== 'string' || options.release.trim().length === 0) {
|
|
25
|
+
throw new Error('[sauron] init requires a { release } option (the app version this build reports as)');
|
|
26
|
+
}
|
|
24
27
|
const sampleRate = typeof options.sampleRate === 'number' ? options.sampleRate : DEFAULTS.sampleRate;
|
|
25
28
|
return {
|
|
26
29
|
dsn: options.dsn,
|
|
27
|
-
release: options.release
|
|
30
|
+
release: options.release.trim(),
|
|
28
31
|
tags: options.tags ?? {},
|
|
29
32
|
contexts: options.contexts ?? {},
|
|
30
33
|
extra: options.extra ?? {},
|
|
@@ -210,6 +213,15 @@ export class SauronClient {
|
|
|
210
213
|
item.url = input.url;
|
|
211
214
|
if (distinctId != null)
|
|
212
215
|
item.distinct_id = distinctId;
|
|
216
|
+
// Per-call only — deliberately no `globalScope.tags`/`.extra` merge here,
|
|
217
|
+
// unlike `track()` and `captureException` below. See TransactionInput.
|
|
218
|
+
// Omitted when empty so an app that never sets them is byte-identical on
|
|
219
|
+
// the wire to before these fields existed.
|
|
220
|
+
if (input.tags && Object.keys(input.tags).length > 0)
|
|
221
|
+
item.tags = { ...input.tags };
|
|
222
|
+
if (input.extra && Object.keys(input.extra).length > 0) {
|
|
223
|
+
item.extra = capTransactionExtra({ ...input.extra });
|
|
224
|
+
}
|
|
213
225
|
this.dispatch(item);
|
|
214
226
|
}
|
|
215
227
|
/** Capture a product-analytics event. `distinctId` is required. */
|
package/dist/index.d.ts
CHANGED
|
@@ -77,3 +77,4 @@ export declare function setExtra(key: string, value: unknown): void;
|
|
|
77
77
|
export declare function flush(): Promise<void>;
|
|
78
78
|
/** Flush and stop the background timer, then clear the active client. */
|
|
79
79
|
export declare function close(): Promise<void>;
|
|
80
|
+
export { MAX_TRANSACTION_EXTRA_BYTES, capTransactionExtra, } from './transaction-extra.js';
|
package/dist/index.js
CHANGED
|
@@ -112,3 +112,6 @@ export async function close() {
|
|
|
112
112
|
activeClient = null;
|
|
113
113
|
await client.close();
|
|
114
114
|
}
|
|
115
|
+
// Exported so a caller can size a payload BEFORE attaching it — the cap is
|
|
116
|
+
// otherwise invisible until the dashboard shows a truncation marker.
|
|
117
|
+
export { MAX_TRANSACTION_EXTRA_BYTES, capTransactionExtra, } from './transaction-extra.js';
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The size cap on a transaction's developer-supplied `extra`.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than a private helper inside `client.ts` so the limit
|
|
5
|
+
* and its behaviour are directly testable — the failure this guards against
|
|
6
|
+
* (an oversized payload taking a whole batched envelope down with it) is not
|
|
7
|
+
* visible from the outside once it happens.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Largest serialized `extra` a single transaction may carry, in bytes.
|
|
11
|
+
*
|
|
12
|
+
* Transactions are the highest-volume signal and they ship in BATCHED
|
|
13
|
+
* envelopes, so one oversized payload does not fail alone — ingest rejects the
|
|
14
|
+
* whole envelope past `INGEST_MAX_BODY_BYTES` (1 MiB by default) and every
|
|
15
|
+
* unrelated span batched with it is lost. Since the motivating use of
|
|
16
|
+
* transaction `extra` is request and response bodies, that is not a remote
|
|
17
|
+
* hazard.
|
|
18
|
+
*
|
|
19
|
+
* Kept identical across all five SDKs. If it moves, it moves everywhere.
|
|
20
|
+
*/
|
|
21
|
+
export declare const MAX_TRANSACTION_EXTRA_BYTES: number;
|
|
22
|
+
/**
|
|
23
|
+
* Cap a transaction's `extra`, substituting a marker when it is too large.
|
|
24
|
+
*
|
|
25
|
+
* Replaces the WHOLE map rather than trimming keys: a half-written JSON value
|
|
26
|
+
* is worse than an honest marker, and per-key trimming would make the result
|
|
27
|
+
* depend on key iteration order, which differs across the five SDKs. The marker
|
|
28
|
+
* is deliberately readable on the dashboard — `_truncated` says data was
|
|
29
|
+
* dropped rather than silently serving a short object that looks complete.
|
|
30
|
+
*
|
|
31
|
+
* A value that cannot be serialized at all (a cycle, a BigInt) becomes the same
|
|
32
|
+
* marker with `_bytes: -1`, because the alternative is throwing from inside
|
|
33
|
+
* `trackTransaction` — and an SDK that crashes the app it is measuring is worse
|
|
34
|
+
* than one that drops a payload.
|
|
35
|
+
*/
|
|
36
|
+
export declare function capTransactionExtra(extra: Record<string, unknown>, maxBytes?: number): Record<string, unknown>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The size cap on a transaction's developer-supplied `extra`.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than a private helper inside `client.ts` so the limit
|
|
5
|
+
* and its behaviour are directly testable — the failure this guards against
|
|
6
|
+
* (an oversized payload taking a whole batched envelope down with it) is not
|
|
7
|
+
* visible from the outside once it happens.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Largest serialized `extra` a single transaction may carry, in bytes.
|
|
11
|
+
*
|
|
12
|
+
* Transactions are the highest-volume signal and they ship in BATCHED
|
|
13
|
+
* envelopes, so one oversized payload does not fail alone — ingest rejects the
|
|
14
|
+
* whole envelope past `INGEST_MAX_BODY_BYTES` (1 MiB by default) and every
|
|
15
|
+
* unrelated span batched with it is lost. Since the motivating use of
|
|
16
|
+
* transaction `extra` is request and response bodies, that is not a remote
|
|
17
|
+
* hazard.
|
|
18
|
+
*
|
|
19
|
+
* Kept identical across all five SDKs. If it moves, it moves everywhere.
|
|
20
|
+
*/
|
|
21
|
+
export const MAX_TRANSACTION_EXTRA_BYTES = 16 * 1024;
|
|
22
|
+
/**
|
|
23
|
+
* Cap a transaction's `extra`, substituting a marker when it is too large.
|
|
24
|
+
*
|
|
25
|
+
* Replaces the WHOLE map rather than trimming keys: a half-written JSON value
|
|
26
|
+
* is worse than an honest marker, and per-key trimming would make the result
|
|
27
|
+
* depend on key iteration order, which differs across the five SDKs. The marker
|
|
28
|
+
* is deliberately readable on the dashboard — `_truncated` says data was
|
|
29
|
+
* dropped rather than silently serving a short object that looks complete.
|
|
30
|
+
*
|
|
31
|
+
* A value that cannot be serialized at all (a cycle, a BigInt) becomes the same
|
|
32
|
+
* marker with `_bytes: -1`, because the alternative is throwing from inside
|
|
33
|
+
* `trackTransaction` — and an SDK that crashes the app it is measuring is worse
|
|
34
|
+
* than one that drops a payload.
|
|
35
|
+
*/
|
|
36
|
+
export function capTransactionExtra(extra, maxBytes = MAX_TRANSACTION_EXTRA_BYTES) {
|
|
37
|
+
let bytes;
|
|
38
|
+
try {
|
|
39
|
+
const json = JSON.stringify(extra);
|
|
40
|
+
if (json === undefined)
|
|
41
|
+
return { _truncated: true, _bytes: -1 };
|
|
42
|
+
// UTF-8 byte length, not `json.length`: the latter undercounts every
|
|
43
|
+
// non-ASCII byte, which is exactly what a response body full of user text
|
|
44
|
+
// is made of.
|
|
45
|
+
bytes = Buffer.byteLength(json, 'utf8');
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { _truncated: true, _bytes: -1 };
|
|
49
|
+
}
|
|
50
|
+
if (bytes <= maxBytes)
|
|
51
|
+
return extra;
|
|
52
|
+
return { _truncated: true, _bytes: bytes };
|
|
53
|
+
}
|
package/dist/transport.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { maybeGzip } from './gzip.js';
|
|
2
2
|
import { BoundedQueue } from './queue.js';
|
|
3
3
|
const SDK_NAME = 'sauron-node';
|
|
4
|
-
const SDK_VERSION = '1.
|
|
4
|
+
const SDK_VERSION = '1.6.0';
|
|
5
5
|
/** Default exponential-backoff base (ms) for the first retry. */
|
|
6
6
|
const DEFAULT_RETRY_BASE_MS = 200;
|
|
7
7
|
/** Hard cap on any single backoff delay (ms). */
|
package/dist/types.d.ts
CHANGED
|
@@ -161,6 +161,17 @@ export interface TransactionItem {
|
|
|
161
161
|
workflow_id?: string;
|
|
162
162
|
workflow_name?: string;
|
|
163
163
|
timestamp: string;
|
|
164
|
+
/**
|
|
165
|
+
* Developer-supplied flat string tags for THIS transaction. NOT merged with
|
|
166
|
+
* the scope — see {@link TransactionInput.tags}. Omitted when empty.
|
|
167
|
+
*/
|
|
168
|
+
tags?: Record<string, string>;
|
|
169
|
+
/**
|
|
170
|
+
* Developer-supplied freeform JSON for THIS transaction. Capped and replaced
|
|
171
|
+
* with a truncation marker past `MAX_TRANSACTION_EXTRA_BYTES`. Omitted when
|
|
172
|
+
* empty.
|
|
173
|
+
*/
|
|
174
|
+
extra?: Record<string, unknown>;
|
|
164
175
|
}
|
|
165
176
|
/** Caller input for {@link TransactionItem} via `trackTransaction`. */
|
|
166
177
|
export interface TransactionInput {
|
|
@@ -187,6 +198,29 @@ export interface TransactionInput {
|
|
|
187
198
|
url?: string;
|
|
188
199
|
/** Falls back to the scoped user's id when omitted. */
|
|
189
200
|
distinct_id?: string;
|
|
201
|
+
/**
|
|
202
|
+
* Flat string tags for this transaction.
|
|
203
|
+
*
|
|
204
|
+
* **Per-call only — the scope is NOT merged in**, which is the one place
|
|
205
|
+
* transactions differ from `track()` and `captureException()`. Those two
|
|
206
|
+
* merge `setTag`/`setExtra` defaults; a transaction carries only what its own
|
|
207
|
+
* call site attached. Transactions are the highest-volume signal (one per
|
|
208
|
+
* navigation and per HTTP call), so inheriting a global blob would write it
|
|
209
|
+
* onto every row.
|
|
210
|
+
*/
|
|
211
|
+
tags?: Record<string, string>;
|
|
212
|
+
/**
|
|
213
|
+
* Freeform JSON for this transaction — the request body, the response body,
|
|
214
|
+
* an order id, a retry count.
|
|
215
|
+
*
|
|
216
|
+
* Per-call only, for the reason on {@link TransactionInput.tags}. Serialized
|
|
217
|
+
* and capped at `MAX_TRANSACTION_EXTRA_BYTES`; past that the whole map is
|
|
218
|
+
* replaced with `{ _truncated: true, _bytes: N }` so one large body cannot
|
|
219
|
+
* take a batched envelope over the ingest limit and drop every span in it.
|
|
220
|
+
*
|
|
221
|
+
* Nothing here is scrubbed. `beforeSend` is the redaction seam.
|
|
222
|
+
*/
|
|
223
|
+
extra?: Record<string, unknown>;
|
|
190
224
|
}
|
|
191
225
|
/** Any item that can appear in an envelope's `items` array. */
|
|
192
226
|
export type EnvelopeItem = ErrorItem | EventItem | IdentifyItem | TransactionItem;
|
|
@@ -273,7 +307,8 @@ export interface TransportOptions {
|
|
|
273
307
|
export interface InitOptions {
|
|
274
308
|
/** `https://<public_key>@<host>/<project_id>` */
|
|
275
309
|
dsn: string;
|
|
276
|
-
|
|
310
|
+
/** Required. The app version every event is attributed to. */
|
|
311
|
+
release: string;
|
|
277
312
|
/** Default tags seeded into the global scope at init. */
|
|
278
313
|
tags?: Record<string, string>;
|
|
279
314
|
/** Default named dev context blocks seeded into the global scope at init. Distinct from the machine `context`. */
|
|
@@ -318,7 +353,7 @@ export interface InitOptions {
|
|
|
318
353
|
/** Fully-resolved options with all defaults applied. */
|
|
319
354
|
export interface ResolvedOptions {
|
|
320
355
|
dsn: string;
|
|
321
|
-
release: string
|
|
356
|
+
release: string;
|
|
322
357
|
tags: Record<string, string>;
|
|
323
358
|
contexts: Record<string, unknown>;
|
|
324
359
|
extra: Record<string, unknown>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edraj/sauron-node",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Sauron server-side Node/TypeScript SDK: product-analytics events + exception capture for Node backends.",
|
|
5
5
|
"homepage": "https://github.com/edraj/sauron/tree/main/sdks/node#readme",
|
|
6
6
|
"repository": {
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
"dist",
|
|
26
26
|
"README.md",
|
|
27
27
|
"CHANGELOG.md",
|
|
28
|
-
"LICENSE"
|
|
28
|
+
"LICENSE",
|
|
29
|
+
"COPYING"
|
|
29
30
|
],
|
|
30
31
|
"scripts": {
|
|
31
32
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -52,7 +53,7 @@
|
|
|
52
53
|
"node",
|
|
53
54
|
"server"
|
|
54
55
|
],
|
|
55
|
-
"license": "
|
|
56
|
+
"license": "LGPL-3.0-only",
|
|
56
57
|
"publishConfig": {
|
|
57
58
|
"access": "public"
|
|
58
59
|
}
|