@flareapp/core 2.11.0 → 2.12.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/dist/index.cjs +29 -114
- package/dist/index.d.cts +22 -49
- package/dist/index.d.mts +22 -49
- package/dist/index.mjs +27 -113
- package/dist/{urlAttributes-Cinqzwjy.cjs → urlAttributes-CAhKmW6d.cjs} +22 -24
- package/dist/{urlAttributes-D0zqStcw.d.cts → urlAttributes-CU2Yr37w.d.cts} +20 -9
- package/dist/{urlAttributes-DmpMd_4O.mjs → urlAttributes-c-cPzjo7.mjs} +17 -25
- package/dist/{urlAttributes-DhkRD7pA.d.mts → urlAttributes-rWoVW31H.d.mts} +20 -9
- package/dist/util/index.cjs +2 -1
- package/dist/util/index.d.cts +2 -2
- package/dist/util/index.d.mts +2 -2
- package/dist/util/index.mjs +2 -2
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as
|
|
1
|
+
import { $ as StackFrame, A as AttributeValue, B as LogsEnvelope, C as createIdentityTagger, D as assertKey, F as EntryPointHandler, G as Report, H as OtelLogRecord, I as EntryPointType, J as Span, K as SamplingContext, L as Framework, M as BufferedLog, N as BufferedSpan, O as assert, P as Config, Q as SpanStatusCode, R as Glow, S as SdkTaggable, U as OtelSpan, V as MessageLevel, W as OverriddenGrouping, X as SpanOptions, Y as SpanEvent, Z as SpanStatus, at as SpanTypeName, b as extractCode, c as RejectionReporter, d as DEFAULT_URL_DENYLIST, et as TracesEnvelope, f as redactObjectValues, g as now, h as safeDecode, it as BrowserSpanType, j as Attributes, k as AnyValue, l as describeRejectionReason, m as resolveDenylist, n as urlAttributes, nt as User, o as SafeCloneOptions, ot as FrameworkName, p as redactUrlQuery, q as SdkInfo, r as toCustomContext, rt as BrowserSpanEventType, s as safeClone, tt as TracesSampler, u as routeRejection, v as glowsToEvents, w as convertToError, x as evictLruIfNew, y as flatJsonStringify, z as KeyValue } from "./urlAttributes-rWoVW31H.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/api/Api.d.ts
|
|
4
4
|
declare class Api {
|
|
@@ -6,8 +6,7 @@ declare class Api {
|
|
|
6
6
|
private pendingKeepaliveRequests;
|
|
7
7
|
/**
|
|
8
8
|
* How many keepalive bytes are still available. Logs and traces share one browser allowance and both
|
|
9
|
-
* flush on page hide, so whichever goes second
|
|
10
|
-
* whole budget, or it exceeds the gate in send() and silently degrades to a cancellable fetch.
|
|
9
|
+
* flush on page hide, so whichever goes second must pack against what is left, not the whole budget.
|
|
11
10
|
*/
|
|
12
11
|
keepaliveBudgetRemaining(): number;
|
|
13
12
|
report(report: Report, url: string, key: string | null, reportBrowserExtensionErrors: boolean, debug?: boolean): Promise<void>;
|
|
@@ -127,14 +126,13 @@ declare function readLinesFromFile(fileText: string, lineNumber: number, columnN
|
|
|
127
126
|
interface ActiveSpanHolder {
|
|
128
127
|
getActive(): Span | undefined;
|
|
129
128
|
/**
|
|
130
|
-
*
|
|
131
|
-
* holder can
|
|
129
|
+
* Runs `fn` with `span` active, then restores the previous active span. Takes a callback, not a
|
|
130
|
+
* setter, so a Node holder can implement it with `AsyncLocalStorage.run(...)`.
|
|
132
131
|
*/
|
|
133
132
|
withActive<T>(span: Span, fn: () => T): T;
|
|
134
133
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* omits it simply has no active-root support.
|
|
134
|
+
* Fallback span that getActive() returns when no withActive scope is active. Long-lived
|
|
135
|
+
* pageload/navigation roots use it so child spans auto-parent to them. Optional.
|
|
138
136
|
*/
|
|
139
137
|
setActiveRoot?(span: Span | undefined): void;
|
|
140
138
|
}
|
|
@@ -187,10 +185,9 @@ declare class Tracer {
|
|
|
187
185
|
getActiveSpan(): Span | undefined;
|
|
188
186
|
setActiveRoot(span?: Span): void;
|
|
189
187
|
/**
|
|
190
|
-
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
* Consumed by the matching `startSpan({ claimed: true })`.
|
|
188
|
+
* Claims a span slot before the span exists, for a caller that publishes a span id early (the
|
|
189
|
+
* component profilers do; their descendants record first). Returns false when the trace is full.
|
|
190
|
+
* Paired with `startSpan({ claimed: true })`.
|
|
194
191
|
*/
|
|
195
192
|
claimSpanSlot(traceId: string): boolean;
|
|
196
193
|
addSpanListener(fn: SpanLifecycleListener): () => void;
|
|
@@ -201,20 +198,20 @@ declare class Tracer {
|
|
|
201
198
|
clear(): void;
|
|
202
199
|
continueFromTraceparent(header: string): void;
|
|
203
200
|
/**
|
|
204
|
-
* Runs `fn` with the span active, so spans started inside auto-parent to it, then ends
|
|
201
|
+
* Runs `fn` with the span active, so spans started inside it auto-parent to it, then ends the span.
|
|
205
202
|
* Records an error status first if `fn` throws or its returned promise rejects.
|
|
206
203
|
*/
|
|
207
204
|
withSpan<T>(name: string, fn: (span: Span) => T, opts?: SpanOptions): T;
|
|
208
|
-
/**
|
|
209
|
-
*
|
|
205
|
+
/**
|
|
206
|
+
* Starts a span the caller must end. Unlike `withSpan`, it does not become the active span, so spans
|
|
207
|
+
* started after it do not auto-parent to it.
|
|
208
|
+
*/
|
|
210
209
|
startSpan(name: string, opts?: SpanOptions): Span;
|
|
211
|
-
/** A real Span handle that records nothing, so callers never have to branch on whether tracing is on. */
|
|
212
210
|
private startInertSpan;
|
|
213
211
|
private resolveTrace;
|
|
214
212
|
private getOrSeedState;
|
|
215
213
|
private createState;
|
|
216
214
|
private makeSpan;
|
|
217
|
-
/** Bounded, LRU by insertion order, like traceStates. Holds primitives only, never a span. */
|
|
218
215
|
private rememberClosed;
|
|
219
216
|
private onSpanEnd;
|
|
220
217
|
}
|
|
@@ -246,22 +243,13 @@ declare class Flare {
|
|
|
246
243
|
* default; a platform can back it with AsyncLocalStorage instead.
|
|
247
244
|
*/
|
|
248
245
|
constructor(api?: Api, contextCollector?: ContextCollector, fileReader?: FileReader, scopeProvider?: ScopeProvider, scheduler?: FlushScheduler, activeSpanHolder?: ActiveSpanHolder);
|
|
249
|
-
/**
|
|
250
|
-
* Register an in-flight report so `flush()` can wait for it. Every entry point wraps its whole async
|
|
251
|
-
* pipeline, from beforeEvaluate through api.report, so flush() waits on all of it.
|
|
252
|
-
*
|
|
253
|
-
* What goes in the Set is a shadow promise that mirrors `p`'s timing but cannot reject, so a failed
|
|
254
|
-
* report never surfaces as an unhandled rejection warning. `p` itself is returned untouched, so the
|
|
255
|
-
* caller still observes real success or failure.
|
|
256
|
-
*/
|
|
257
246
|
private track;
|
|
258
247
|
/**
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
* drain any other concurrent reports before `process.exit`.
|
|
248
|
+
* Waits until every in-flight report settles, or `timeoutMs` elapses. Always resolves, never rejects.
|
|
249
|
+
* Used by `@flareapp/node`'s fatal handler to drain other reports before `process.exit`.
|
|
262
250
|
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
251
|
+
* Only reports already in flight are awaited, so a handler still emitting during shutdown cannot block
|
|
252
|
+
* forever. Call flush again to catch those.
|
|
265
253
|
*/
|
|
266
254
|
flush(timeoutMs?: number): Promise<void>;
|
|
267
255
|
get config(): Readonly<Config>;
|
|
@@ -298,10 +286,6 @@ declare class Flare {
|
|
|
298
286
|
setEntryPoint(handler: EntryPointHandler): this;
|
|
299
287
|
setSdkInfo(info: SdkInfo): this;
|
|
300
288
|
setFramework(framework: Framework): this;
|
|
301
|
-
/**
|
|
302
|
-
* True when a report must not be captured: consent withdrawn, or dropped by sampling. Consent is
|
|
303
|
-
* checked first, so a blocked report never runs the sampler or assembles a report (no cookie read).
|
|
304
|
-
*/
|
|
305
289
|
private shouldSkipCapture;
|
|
306
290
|
report(error: Error, attributes?: Attributes): Promise<void>;
|
|
307
291
|
private reportInternal;
|
|
@@ -314,15 +298,6 @@ declare class Flare {
|
|
|
314
298
|
private buildBaseAttributes;
|
|
315
299
|
private assembleAttributes;
|
|
316
300
|
private buildLogAttributes;
|
|
317
|
-
/**
|
|
318
|
-
* Local roots only, snapshotted by the Tracer at span START so a long-lived root does not drift into
|
|
319
|
-
* the next page's scope. Children get none, and no span ever runs the DOM collector.
|
|
320
|
-
*
|
|
321
|
-
* Everything assembled is inherited except user identity (excluding the opaque `user.id`): a root span
|
|
322
|
-
* goes out for every page view, so email, full name, IP and `user.attributes` would turn normal
|
|
323
|
-
* browsing into PII traffic. The rest — `context.custom`, `addContextGroup` bags — stays in, because
|
|
324
|
-
* the trace viewer renders any span attribute whose key does not start with `flare.`.
|
|
325
|
-
*/
|
|
326
301
|
private getScopeAttributes;
|
|
327
302
|
private spanResourceAttributes;
|
|
328
303
|
private buildReport;
|
|
@@ -350,11 +325,9 @@ declare function spanId(): string;
|
|
|
350
325
|
//#endregion
|
|
351
326
|
//#region src/stacktrace/NullFileReader.d.ts
|
|
352
327
|
/**
|
|
353
|
-
* No-op `FileReader` returning `null` for every URL. Default for `Flare`'s `fileReader` param so
|
|
354
|
-
* reports without picking an environment; stack frames just omit source snippets.
|
|
355
|
-
*
|
|
356
|
-
* interface lets the stack-trace builder treat all three the same (render on text, skip on null), so core needs no
|
|
357
|
-
* environment checks.
|
|
328
|
+
* No-op `FileReader` returning `null` for every URL. Default for `Flare`'s `fileReader` param, so
|
|
329
|
+
* `new Flare()` builds reports without picking an environment; stack frames just omit source snippets.
|
|
330
|
+
* `@flareapp/js` and `@flareapp/node` inject a real fetch- or disk-based reader instead.
|
|
358
331
|
*/
|
|
359
332
|
declare class NullFileReader implements FileReader {
|
|
360
333
|
read(_url: string): Promise<string | null>;
|
|
@@ -422,4 +395,4 @@ declare function buildDeviceContextGroup(info: DeviceInfo): Record<string, Attri
|
|
|
422
395
|
//#region src/stacktrace/createStackTrace.d.ts
|
|
423
396
|
declare function createStackTrace(error: Error, debug: boolean, fileReader: FileReader): Promise<Array<StackFrame>>;
|
|
424
397
|
//#endregion
|
|
425
|
-
export { type ActiveSpanHolder, type AnyValue, Api, type AttributeValue, type Attributes, BrowserSpanEventType, BrowserSpanType, type BufferedLog, type BufferedSpan, type Config, type ContextCollector, DEFAULT_MAX_LIVE_TRACES, DEFAULT_URL_DENYLIST, type DeviceInfo, type DeviceInfoProvider, type EffectiveConnectionType, type EntryPointHandler, type EntryPointType, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, FrameworkName, GlobalScopeProvider, type Glow, InMemoryActiveSpanHolder, type KeyValue, Logger, type LoggerDeps, type LogsEnvelope, MAX_BREADCRUMB_URL_LENGTH, type MessageLevel, NoopFlushScheduler, NullDeviceInfoProvider, NullFileReader, type OtelLogRecord, type OtelSpan, type OverriddenGrouping, type RejectionReporter, type Report, type SafeCloneOptions, type SamplingContext, Scope, type ScopeProvider, type SdkInfo, type SdkTaggable, type Span, type SpanEvent, type SpanLifecycleEvent, type SpanLifecycleListener, type SpanOptions, type SpanPhase, type SpanStatus, SpanStatusCode, type SpanTypeName, type StackFrame, Tracer, type TracerDeps, type TracesEnvelope, type TracesSampler, USER_IDENTITY_KEYS, type User, assert, assertKey, breadcrumbUrl, buildDeviceContextGroup, buildTraceparent, buildTracesEnvelope, convertToError, createIdentityTagger, createStackTrace, defaultNowNano, describeRejectionReason, deviceInfoToAttributes, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, parseTraceparent, readLinesFromFile, recordBreadcrumb, redactObjectValues, redactUrlQuery, resolveDenylist, routeRejection, safeClone, safeDecode, spanId, toCustomContext, urlAttributes, userIdentityAttributes };
|
|
398
|
+
export { type ActiveSpanHolder, type AnyValue, Api, type AttributeValue, type Attributes, BrowserSpanEventType, BrowserSpanType, type BufferedLog, type BufferedSpan, type Config, type ContextCollector, DEFAULT_MAX_LIVE_TRACES, DEFAULT_URL_DENYLIST, type DeviceInfo, type DeviceInfoProvider, type EffectiveConnectionType, type EntryPointHandler, type EntryPointType, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, FrameworkName, GlobalScopeProvider, type Glow, InMemoryActiveSpanHolder, type KeyValue, Logger, type LoggerDeps, type LogsEnvelope, MAX_BREADCRUMB_URL_LENGTH, type MessageLevel, NoopFlushScheduler, NullDeviceInfoProvider, NullFileReader, type OtelLogRecord, type OtelSpan, type OverriddenGrouping, type RejectionReporter, type Report, type SafeCloneOptions, type SamplingContext, Scope, type ScopeProvider, type SdkInfo, type SdkTaggable, type Span, type SpanEvent, type SpanLifecycleEvent, type SpanLifecycleListener, type SpanOptions, type SpanPhase, type SpanStatus, SpanStatusCode, type SpanTypeName, type StackFrame, Tracer, type TracerDeps, type TracesEnvelope, type TracesSampler, USER_IDENTITY_KEYS, type User, assert, assertKey, breadcrumbUrl, buildDeviceContextGroup, buildTraceparent, buildTracesEnvelope, convertToError, createIdentityTagger, createStackTrace, defaultNowNano, describeRejectionReason, deviceInfoToAttributes, evictLruIfNew, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, parseTraceparent, readLinesFromFile, recordBreadcrumb, redactObjectValues, redactUrlQuery, resolveDenylist, routeRejection, safeClone, safeDecode, spanId, toCustomContext, urlAttributes, userIdentityAttributes };
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as KEY, C as createIdentityTagger, D as assertKey, O as assert, S as evictLruIfNew, _ as MAX_TRAVERSAL_DEPTH, a as describeRejectionReason, b as spendNode, c as redactObjectValues, d as safeDecode, f as now, g as safeClone, h as flatJsonStringify, i as setDefined, j as SOURCEMAP_VERSION, k as CLIENT_VERSION, l as redactUrlQuery, m as glowsToEvents, n as urlAttributes, o as routeRejection, p as timelineEvents, r as toCustomContext, s as DEFAULT_URL_DENYLIST, u as resolveDenylist, v as TRUNCATED, w as convertToError, x as extractCode, y as createTraversalBudget } from "./urlAttributes-c-cPzjo7.mjs";
|
|
2
2
|
import ErrorStackParser from "error-stack-parser";
|
|
3
3
|
|
|
4
4
|
//#region src/framework.ts
|
|
5
5
|
/**
|
|
6
|
-
* Framework names the Flare backend
|
|
7
|
-
* `flare.framework.name` and (lowercased) as `context.custom.framework`.
|
|
6
|
+
* Framework names the Flare backend recognizes. Wire format: these values never change, since they
|
|
7
|
+
* ship as `flare.framework.name` and (lowercased) as `context.custom.framework`.
|
|
8
8
|
*
|
|
9
|
-
* `Js` and `Node` are the base SDKs
|
|
10
|
-
* own name. `NodeElectron` is
|
|
9
|
+
* `Js` and `Node` are fallback claims from the base SDKs, overwritten when a framework package sets
|
|
10
|
+
* its own name. `NodeElectron` is the Electron main process; renderers report their own name.
|
|
11
11
|
*/
|
|
12
12
|
const FrameworkName = {
|
|
13
13
|
Js: "js",
|
|
@@ -23,7 +23,7 @@ const FrameworkName = {
|
|
|
23
23
|
//#endregion
|
|
24
24
|
//#region src/spanTypes.ts
|
|
25
25
|
/**
|
|
26
|
-
* Span types the Flare backend
|
|
26
|
+
* Span types the Flare backend recognizes. Wire format, so the values never change: they ship as the
|
|
27
27
|
* `flare.span_type` attribute and the backend groups performance data by them.
|
|
28
28
|
*
|
|
29
29
|
* These are the browser client's set. They live in core because core's `SpanOptions.spanType` needs
|
|
@@ -37,6 +37,10 @@ const BrowserSpanType = {
|
|
|
37
37
|
Component: "browser_component",
|
|
38
38
|
WebVital: "browser_web_vital"
|
|
39
39
|
};
|
|
40
|
+
/**
|
|
41
|
+
* Span event types on an error report. Kept apart from `BrowserSpanType`: these are points in time,
|
|
42
|
+
* not spans with a duration. That is also why a route change is not called `browser_navigation`.
|
|
43
|
+
*/
|
|
40
44
|
const BrowserSpanEventType = {
|
|
41
45
|
Click: "browser_click",
|
|
42
46
|
Input: "browser_input",
|
|
@@ -55,7 +59,6 @@ const SpanStatusCode = {
|
|
|
55
59
|
//#endregion
|
|
56
60
|
//#region src/util/utf8Bytes.ts
|
|
57
61
|
const textEncoder = new TextEncoder();
|
|
58
|
-
/** UTF-8 byte length of `value`. */
|
|
59
62
|
function utf8Bytes(value) {
|
|
60
63
|
return textEncoder.encode(value).length;
|
|
61
64
|
}
|
|
@@ -69,8 +72,7 @@ var Api = class {
|
|
|
69
72
|
pendingKeepaliveRequests = 0;
|
|
70
73
|
/**
|
|
71
74
|
* How many keepalive bytes are still available. Logs and traces share one browser allowance and both
|
|
72
|
-
* flush on page hide, so whichever goes second
|
|
73
|
-
* whole budget, or it exceeds the gate in send() and silently degrades to a cancellable fetch.
|
|
75
|
+
* flush on page hide, so whichever goes second must pack against what is left, not the whole budget.
|
|
74
76
|
*/
|
|
75
77
|
keepaliveBudgetRemaining() {
|
|
76
78
|
if (this.pendingKeepaliveRequests >= MAX_PENDING_KEEPALIVE_REQUESTS) return 0;
|
|
@@ -170,11 +172,6 @@ function recordBreadcrumb(scopeProvider, config, type, attributes, startTimeUnix
|
|
|
170
172
|
|
|
171
173
|
//#endregion
|
|
172
174
|
//#region src/telemetry/TelemetryBuffer.ts
|
|
173
|
-
/**
|
|
174
|
-
* The batching machine behind both telemetry signals: hold records, ship them when a size, weight or time
|
|
175
|
-
* trigger fires, and shed the oldest when nothing can drain. One instance owns one signal; what that signal
|
|
176
|
-
* is comes entirely from the policy.
|
|
177
|
-
*/
|
|
178
175
|
var TelemetryBuffer = class {
|
|
179
176
|
entries = [];
|
|
180
177
|
bufferedBytes = 0;
|
|
@@ -284,11 +281,6 @@ var TelemetryBuffer = class {
|
|
|
284
281
|
if (dropped) this.bufferedBytes -= dropped.bytes;
|
|
285
282
|
}
|
|
286
283
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Newest-wins. An over-budget record is skipped, not a stop signal, so a smaller older record behind a fat
|
|
289
|
-
* one still ships. Runs on visibilitychange:hidden, which fires on plain backgrounding too, so the tail this
|
|
290
|
-
* leaves behind is retained and re-armed rather than dropped (see flush).
|
|
291
|
-
*/
|
|
292
284
|
packForKeepalive(config, resource) {
|
|
293
285
|
const fixedBytes = this.policy.emptyEnvelopeBytes(resource);
|
|
294
286
|
const budget = Math.min(config.keepaliveMaxBytes, this.policy.keepaliveBudget?.(config) ?? config.keepaliveMaxBytes);
|
|
@@ -317,11 +309,6 @@ var TelemetryBuffer = class {
|
|
|
317
309
|
|
|
318
310
|
//#endregion
|
|
319
311
|
//#region src/telemetry/resourceIdentity.ts
|
|
320
|
-
/**
|
|
321
|
-
* Builds the attributes that go on every logs or traces envelope: the caller's own attributes in `base`, with
|
|
322
|
-
* our SDK, service and framework identity on top. Our keys win, so a user value cannot overwrite something
|
|
323
|
-
* like `telemetry.sdk.name`.
|
|
324
|
-
*/
|
|
325
312
|
function buildResourceIdentity(base, config, sdk, framework) {
|
|
326
313
|
const identity = {
|
|
327
314
|
"telemetry.sdk.language": "javascript",
|
|
@@ -342,18 +329,6 @@ function buildResourceIdentity(base, config, sdk, framework) {
|
|
|
342
329
|
|
|
343
330
|
//#endregion
|
|
344
331
|
//#region src/logging/otel.ts
|
|
345
|
-
/**
|
|
346
|
-
* Converts one attribute value into the OpenTelemetry `AnyValue` shape. Strings, numbers and booleans become
|
|
347
|
-
* leaves, arrays and objects are walked recursively. Anything OpenTelemetry cannot carry (null, undefined,
|
|
348
|
-
* NaN, functions) returns null and the caller drops that key.
|
|
349
|
-
*
|
|
350
|
-
* A value that contains itself becomes the string `[Circular]`. `inPath` only holds the parents of the value
|
|
351
|
-
* being converted right now, so the same object used twice side by side is converted twice instead of being
|
|
352
|
-
* wrongly called circular.
|
|
353
|
-
*
|
|
354
|
-
* The walk also stops at a maximum depth and a maximum number of nodes, see traversalBudget.ts. Pass `budget`
|
|
355
|
-
* to let several calls share one allowance, otherwise every call gets its own.
|
|
356
|
-
*/
|
|
357
332
|
function valueToOpenTelemetry(value, inPath = /* @__PURE__ */ new WeakSet(), budget = createTraversalBudget()) {
|
|
358
333
|
return convert(value, inPath, 0, budget);
|
|
359
334
|
}
|
|
@@ -438,17 +413,9 @@ function buildLogsEnvelope(records, resourceAttributes, scopeName, scopeVersion)
|
|
|
438
413
|
}]
|
|
439
414
|
}] };
|
|
440
415
|
}
|
|
441
|
-
/**
|
|
442
|
-
* How many UTF-8 bytes one record adds to an envelope. We measure the real toOtelLogRecord output instead of
|
|
443
|
-
* reusing the cached BufferedLog estimate, because keepaliveMaxBytes is a hard browser limit and an estimate
|
|
444
|
-
* is not good enough.
|
|
445
|
-
*
|
|
446
|
-
* Uses flatJsonStringify to match Api.logs, which sends the envelope through the same encoder.
|
|
447
|
-
*/
|
|
448
416
|
function otelLogRecordBytes(record) {
|
|
449
417
|
return utf8Bytes(flatJsonStringify(toOtelLogRecord(record)));
|
|
450
418
|
}
|
|
451
|
-
/** UTF-8 bytes of an empty envelope: the fixed overhead every batch has, before any records are added. */
|
|
452
419
|
function emptyLogsEnvelopeBytes(resourceAttributes, scopeName, scopeVersion) {
|
|
453
420
|
return utf8Bytes(flatJsonStringify(buildLogsEnvelope([], resourceAttributes, scopeName, scopeVersion)));
|
|
454
421
|
}
|
|
@@ -608,7 +575,6 @@ function partitionAttributes(attributes) {
|
|
|
608
575
|
|
|
609
576
|
//#endregion
|
|
610
577
|
//#region src/Scope.ts
|
|
611
|
-
/** `USER_IDENTITY_KEYS` derives from this, so adding a field here can never leave the clear pass stale. */
|
|
612
578
|
const USER_FIELD_KEYS = {
|
|
613
579
|
id: "user.id",
|
|
614
580
|
email: "user.email",
|
|
@@ -788,11 +754,9 @@ function isApplicationFrame(fileName) {
|
|
|
788
754
|
//#endregion
|
|
789
755
|
//#region src/stacktrace/NullFileReader.ts
|
|
790
756
|
/**
|
|
791
|
-
* No-op `FileReader` returning `null` for every URL. Default for `Flare`'s `fileReader` param so
|
|
792
|
-
* reports without picking an environment; stack frames just omit source snippets.
|
|
793
|
-
*
|
|
794
|
-
* interface lets the stack-trace builder treat all three the same (render on text, skip on null), so core needs no
|
|
795
|
-
* environment checks.
|
|
757
|
+
* No-op `FileReader` returning `null` for every URL. Default for `Flare`'s `fileReader` param, so
|
|
758
|
+
* `new Flare()` builds reports without picking an environment; stack frames just omit source snippets.
|
|
759
|
+
* `@flareapp/js` and `@flareapp/node` inject a real fetch- or disk-based reader instead.
|
|
796
760
|
*/
|
|
797
761
|
var NullFileReader = class {
|
|
798
762
|
read(_url) {
|
|
@@ -978,28 +942,15 @@ function buildTracesEnvelope(spans, resourceAttributes, scopeName, scopeVersion)
|
|
|
978
942
|
}]
|
|
979
943
|
}] };
|
|
980
944
|
}
|
|
981
|
-
/**
|
|
982
|
-
* How many UTF-8 bytes one span adds to an envelope. We measure the real toOtelSpan output instead of reusing
|
|
983
|
-
* the cached BufferedSpan estimate, because keepaliveMaxBytes is a hard browser limit and an estimate is not
|
|
984
|
-
* good enough.
|
|
985
|
-
*
|
|
986
|
-
* We use flatJsonStringify instead of JSON.stringify because a span keeps values the caller still owns, like
|
|
987
|
-
* status.message, and those can turn unserializable after the span ended. This runs from a visibilitychange
|
|
988
|
-
* listener with no try/catch around it, so a throw here loses the flush. flatJsonStringify handles the usual
|
|
989
|
-
* suspects (circular references, BigInt, a getter that throws on a plain object) but is not bulletproof: a
|
|
990
|
-
* class instance with a throwing getter goes through untouched and can still throw.
|
|
991
|
-
*/
|
|
992
945
|
function otelSpanBytes(span) {
|
|
993
946
|
return utf8Bytes(flatJsonStringify(toOtelSpan(span)));
|
|
994
947
|
}
|
|
995
|
-
/** UTF-8 bytes of an empty envelope: the fixed overhead every batch has, before any spans are added. */
|
|
996
948
|
function emptyTracesEnvelopeBytes(resourceAttributes, scopeName, scopeVersion) {
|
|
997
949
|
return utf8Bytes(JSON.stringify(buildTracesEnvelope([], resourceAttributes, scopeName, scopeVersion)));
|
|
998
950
|
}
|
|
999
951
|
|
|
1000
952
|
//#endregion
|
|
1001
953
|
//#region src/tracing/SpanBuffer.ts
|
|
1002
|
-
/** The span half of the shared telemetry buffer: names the config keys, the envelope and the ingest call. */
|
|
1003
954
|
var SpanBuffer = class {
|
|
1004
955
|
inner;
|
|
1005
956
|
constructor(deps) {
|
|
@@ -1084,11 +1035,9 @@ function parseTraceparent(header) {
|
|
|
1084
1035
|
function isPromiseLike(value) {
|
|
1085
1036
|
return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
|
|
1086
1037
|
}
|
|
1087
|
-
/** `SpanOptions.parent` is a structurally overlapping union; `isRecording` is what tells a real Span apart. */
|
|
1088
1038
|
function isSpan(parent) {
|
|
1089
1039
|
return "isRecording" in parent;
|
|
1090
1040
|
}
|
|
1091
|
-
/** A SpanImpl carries the epoch it was created under; a hand-stitched `{traceId, spanId}` parent does not. */
|
|
1092
1041
|
function hasEpoch(parent) {
|
|
1093
1042
|
return "epoch" in parent && typeof parent.epoch === "number";
|
|
1094
1043
|
}
|
|
@@ -1097,16 +1046,6 @@ function defaultNowNano() {
|
|
|
1097
1046
|
const ms = performanceApi && typeof performanceApi.now === "function" && typeof performanceApi.timeOrigin === "number" ? performanceApi.timeOrigin + performanceApi.now() : Date.now();
|
|
1098
1047
|
return Math.round(ms * 1e6);
|
|
1099
1048
|
}
|
|
1100
|
-
/**
|
|
1101
|
-
* Both trace maps cap their size the same way: insertion order is LRU, so the first key is the one to drop.
|
|
1102
|
-
* Only evicts when `key` is not already in the map. A set() that overwrites an existing key does not grow the
|
|
1103
|
-
* map, so it must not evict an unrelated entry to make room for it.
|
|
1104
|
-
*/
|
|
1105
|
-
function evictLruIfNew(map, key, cap) {
|
|
1106
|
-
if (map.has(key) || map.size < cap) return;
|
|
1107
|
-
const lru = map.keys().next().value;
|
|
1108
|
-
if (lru !== void 0) map.delete(lru);
|
|
1109
|
-
}
|
|
1110
1049
|
const MAX_CLOSED_TRACES = 100;
|
|
1111
1050
|
/** Bounded backstop for the live TraceState map: an app that never ends spans must not grow it forever. */
|
|
1112
1051
|
const DEFAULT_MAX_LIVE_TRACES = 1e3;
|
|
@@ -1145,10 +1084,9 @@ var Tracer = class {
|
|
|
1145
1084
|
this.holder.setActiveRoot?.(span);
|
|
1146
1085
|
}
|
|
1147
1086
|
/**
|
|
1148
|
-
*
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
* Consumed by the matching `startSpan({ claimed: true })`.
|
|
1087
|
+
* Claims a span slot before the span exists, for a caller that publishes a span id early (the
|
|
1088
|
+
* component profilers do; their descendants record first). Returns false when the trace is full.
|
|
1089
|
+
* Paired with `startSpan({ claimed: true })`.
|
|
1152
1090
|
*/
|
|
1153
1091
|
claimSpanSlot(traceId) {
|
|
1154
1092
|
const config = this.deps.getConfig();
|
|
@@ -1187,7 +1125,7 @@ var Tracer = class {
|
|
|
1187
1125
|
this.pendingContinuation = parseTraceparent(header);
|
|
1188
1126
|
}
|
|
1189
1127
|
/**
|
|
1190
|
-
* Runs `fn` with the span active, so spans started inside auto-parent to it, then ends
|
|
1128
|
+
* Runs `fn` with the span active, so spans started inside it auto-parent to it, then ends the span.
|
|
1191
1129
|
* Records an error status first if `fn` throws or its returned promise rejects.
|
|
1192
1130
|
*/
|
|
1193
1131
|
withSpan(name, fn, opts = {}) {
|
|
@@ -1217,8 +1155,10 @@ var Tracer = class {
|
|
|
1217
1155
|
}
|
|
1218
1156
|
});
|
|
1219
1157
|
}
|
|
1220
|
-
/**
|
|
1221
|
-
*
|
|
1158
|
+
/**
|
|
1159
|
+
* Starts a span the caller must end. Unlike `withSpan`, it does not become the active span, so spans
|
|
1160
|
+
* started after it do not auto-parent to it.
|
|
1161
|
+
*/
|
|
1222
1162
|
startSpan(name, opts = {}) {
|
|
1223
1163
|
const config = this.deps.getConfig();
|
|
1224
1164
|
const spanId$1 = opts.spanId ?? spanId();
|
|
@@ -1248,7 +1188,6 @@ var Tracer = class {
|
|
|
1248
1188
|
this.emitSpanEvent("start", span);
|
|
1249
1189
|
return span;
|
|
1250
1190
|
}
|
|
1251
|
-
/** A real Span handle that records nothing, so callers never have to branch on whether tracing is on. */
|
|
1252
1191
|
startInertSpan(name, spanId, opts, config) {
|
|
1253
1192
|
const span = this.makeSpan({
|
|
1254
1193
|
traceId: traceId(),
|
|
@@ -1355,7 +1294,6 @@ var Tracer = class {
|
|
|
1355
1294
|
if (opts.attributes) for (const [key, value] of Object.entries(opts.attributes)) span.setAttribute(key, value);
|
|
1356
1295
|
return span;
|
|
1357
1296
|
}
|
|
1358
|
-
/** Bounded, LRU by insertion order, like traceStates. Holds primitives only, never a span. */
|
|
1359
1297
|
rememberClosed(state) {
|
|
1360
1298
|
evictLruIfNew(this.closedTraces, state.traceId, MAX_CLOSED_TRACES);
|
|
1361
1299
|
this.closedTraces.set(state.traceId, {
|
|
@@ -1410,8 +1348,6 @@ var Tracer = class {
|
|
|
1410
1348
|
|
|
1411
1349
|
//#endregion
|
|
1412
1350
|
//#region src/Flare.ts
|
|
1413
|
-
/** Scope attributes a span never inherits. Derived from `USER_IDENTITY_KEYS` so a future user field is
|
|
1414
|
-
* excluded automatically, without anyone needing to remember to list it here. See `getScopeAttributes`. */
|
|
1415
1351
|
const SPAN_SCOPE_EXCLUDED_KEYS = USER_IDENTITY_KEYS.filter((key) => key !== USER_FIELD_KEYS.id);
|
|
1416
1352
|
const DEFAULT_SDK_NAME = "@flareapp/core";
|
|
1417
1353
|
var Flare = class {
|
|
@@ -1496,14 +1432,6 @@ var Flare = class {
|
|
|
1496
1432
|
activeSpanHolder
|
|
1497
1433
|
});
|
|
1498
1434
|
}
|
|
1499
|
-
/**
|
|
1500
|
-
* Register an in-flight report so `flush()` can wait for it. Every entry point wraps its whole async
|
|
1501
|
-
* pipeline, from beforeEvaluate through api.report, so flush() waits on all of it.
|
|
1502
|
-
*
|
|
1503
|
-
* What goes in the Set is a shadow promise that mirrors `p`'s timing but cannot reject, so a failed
|
|
1504
|
-
* report never surfaces as an unhandled rejection warning. `p` itself is returned untouched, so the
|
|
1505
|
-
* caller still observes real success or failure.
|
|
1506
|
-
*/
|
|
1507
1435
|
track(p) {
|
|
1508
1436
|
const tracked = p.then(() => void 0, () => void 0);
|
|
1509
1437
|
this.inflight.add(tracked);
|
|
@@ -1511,12 +1439,11 @@ var Flare = class {
|
|
|
1511
1439
|
return p;
|
|
1512
1440
|
}
|
|
1513
1441
|
/**
|
|
1514
|
-
*
|
|
1515
|
-
*
|
|
1516
|
-
* drain any other concurrent reports before `process.exit`.
|
|
1442
|
+
* Waits until every in-flight report settles, or `timeoutMs` elapses. Always resolves, never rejects.
|
|
1443
|
+
* Used by `@flareapp/node`'s fatal handler to drain other reports before `process.exit`.
|
|
1517
1444
|
*
|
|
1518
|
-
*
|
|
1519
|
-
*
|
|
1445
|
+
* Only reports already in flight are awaited, so a handler still emitting during shutdown cannot block
|
|
1446
|
+
* forever. Call flush again to catch those.
|
|
1520
1447
|
*/
|
|
1521
1448
|
flush(timeoutMs = 2e3) {
|
|
1522
1449
|
this._logger.flush();
|
|
@@ -1663,10 +1590,6 @@ var Flare = class {
|
|
|
1663
1590
|
this.framework = framework;
|
|
1664
1591
|
return this;
|
|
1665
1592
|
}
|
|
1666
|
-
/**
|
|
1667
|
-
* True when a report must not be captured: consent withdrawn, or dropped by sampling. Consent is
|
|
1668
|
-
* checked first, so a blocked report never runs the sampler or assembles a report (no cookie read).
|
|
1669
|
-
*/
|
|
1670
1593
|
shouldSkipCapture() {
|
|
1671
1594
|
if (this._config.hasConsent === false) return true;
|
|
1672
1595
|
return this._config.sampleRate < 1 && Math.random() >= this._config.sampleRate;
|
|
@@ -1791,15 +1714,6 @@ var Flare = class {
|
|
|
1791
1714
|
record: this.assembleAttributes(collectorRecord, userAttributes, false)
|
|
1792
1715
|
};
|
|
1793
1716
|
}
|
|
1794
|
-
/**
|
|
1795
|
-
* Local roots only, snapshotted by the Tracer at span START so a long-lived root does not drift into
|
|
1796
|
-
* the next page's scope. Children get none, and no span ever runs the DOM collector.
|
|
1797
|
-
*
|
|
1798
|
-
* Everything assembled is inherited except user identity (excluding the opaque `user.id`): a root span
|
|
1799
|
-
* goes out for every page view, so email, full name, IP and `user.attributes` would turn normal
|
|
1800
|
-
* browsing into PII traffic. The rest — `context.custom`, `addContextGroup` bags — stays in, because
|
|
1801
|
-
* the trace viewer renders any span attribute whose key does not start with `flare.`.
|
|
1802
|
-
*/
|
|
1803
1717
|
getScopeAttributes() {
|
|
1804
1718
|
const scoped = { ...this.assembleAttributes({}, {}, false) };
|
|
1805
1719
|
for (const key of SPAN_SCOPE_EXCLUDED_KEYS) delete scoped[key];
|
|
@@ -1902,4 +1816,4 @@ function hasNetworkSignal(network) {
|
|
|
1902
1816
|
}
|
|
1903
1817
|
|
|
1904
1818
|
//#endregion
|
|
1905
|
-
export { Api, BrowserSpanEventType, BrowserSpanType, DEFAULT_MAX_LIVE_TRACES, DEFAULT_URL_DENYLIST, Flare, FrameworkName, GlobalScopeProvider, InMemoryActiveSpanHolder, Logger, MAX_BREADCRUMB_URL_LENGTH, NoopFlushScheduler, NullDeviceInfoProvider, NullFileReader, Scope, SpanStatusCode, Tracer, USER_IDENTITY_KEYS, assert, assertKey, breadcrumbUrl, buildDeviceContextGroup, buildTraceparent, buildTracesEnvelope, convertToError, createIdentityTagger, createStackTrace, defaultNowNano, describeRejectionReason, deviceInfoToAttributes, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, parseTraceparent, readLinesFromFile, recordBreadcrumb, redactObjectValues, redactUrlQuery, resolveDenylist, routeRejection, safeClone, safeDecode, spanId, toCustomContext, urlAttributes, userIdentityAttributes };
|
|
1819
|
+
export { Api, BrowserSpanEventType, BrowserSpanType, DEFAULT_MAX_LIVE_TRACES, DEFAULT_URL_DENYLIST, Flare, FrameworkName, GlobalScopeProvider, InMemoryActiveSpanHolder, Logger, MAX_BREADCRUMB_URL_LENGTH, NoopFlushScheduler, NullDeviceInfoProvider, NullFileReader, Scope, SpanStatusCode, Tracer, USER_IDENTITY_KEYS, assert, assertKey, breadcrumbUrl, buildDeviceContextGroup, buildTraceparent, buildTracesEnvelope, convertToError, createIdentityTagger, createStackTrace, defaultNowNano, describeRejectionReason, deviceInfoToAttributes, evictLruIfNew, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, parseTraceparent, readLinesFromFile, recordBreadcrumb, redactObjectValues, redactUrlQuery, resolveDenylist, routeRejection, safeClone, safeDecode, spanId, toCustomContext, urlAttributes, userIdentityAttributes };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
//#region src/env/index.ts
|
|
3
|
-
const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.
|
|
3
|
+
const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.12.1" : "?";
|
|
4
4
|
const KEY = typeof FLARE_JS_KEY === "undefined" ? "" : FLARE_JS_KEY;
|
|
5
5
|
const SOURCEMAP_VERSION = typeof FLARE_SOURCEMAP_VERSION === "undefined" ? "" : FLARE_SOURCEMAP_VERSION;
|
|
6
6
|
|
|
@@ -87,6 +87,14 @@ function createIdentityTagger(config) {
|
|
|
87
87
|
};
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/util/evictLruIfNew.ts
|
|
92
|
+
function evictLruIfNew(map, key, cap) {
|
|
93
|
+
if (map.has(key) || map.size < cap) return;
|
|
94
|
+
const lru = map.keys().next().value;
|
|
95
|
+
if (lru !== void 0) map.delete(lru);
|
|
96
|
+
}
|
|
97
|
+
|
|
90
98
|
//#endregion
|
|
91
99
|
//#region src/util/extractCode.ts
|
|
92
100
|
const MAX_CODE_LENGTH = 64;
|
|
@@ -98,28 +106,16 @@ function extractCode(error) {
|
|
|
98
106
|
|
|
99
107
|
//#endregion
|
|
100
108
|
//#region src/util/traversalBudget.ts
|
|
101
|
-
/**
|
|
102
|
-
* Cycle detection only tracks the ancestor path, so a value holding the same child under two keys is not
|
|
103
|
-
* a cycle but still costs 2^depth to walk. Host data (glows, addContext, span attributes) reaches that
|
|
104
|
-
* shape through any object graph shared by reference.
|
|
105
|
-
*
|
|
106
|
-
* The node cap only bounds that walk if EVERY visited node is charged, primitive leaves included. It used
|
|
107
|
-
* to charge containers only, so 15 shared objects over an array of 1000 strings walked ~17M uncharged
|
|
108
|
-
* strings before the cap fired: 1.2s and 1GB. Callers must spend before their leaf branches return, not
|
|
109
|
-
* after. We do not memoize instead: a value shared under two keys is not a cycle and must not read as one.
|
|
110
|
-
*/
|
|
111
109
|
const MAX_TRAVERSAL_DEPTH = 24;
|
|
112
110
|
const MAX_TRAVERSAL_NODES = 5e4;
|
|
113
111
|
function createTraversalBudget(nodes = MAX_TRAVERSAL_NODES) {
|
|
114
112
|
return { remaining: nodes };
|
|
115
113
|
}
|
|
116
|
-
/** Consumes one node. False once the budget is spent: stop descending. */
|
|
117
114
|
function spendNode(budget) {
|
|
118
115
|
if (budget.remaining <= 0) return false;
|
|
119
116
|
budget.remaining--;
|
|
120
117
|
return true;
|
|
121
118
|
}
|
|
122
|
-
/** Marks where a walk stopped, so a truncated payload does not read as a complete one. */
|
|
123
119
|
const TRUNCATED = "[truncated: too large]";
|
|
124
120
|
|
|
125
121
|
//#endregion
|
|
@@ -182,10 +178,6 @@ function truncate(value, max) {
|
|
|
182
178
|
if (value.length <= max) return value;
|
|
183
179
|
return `${value.slice(0, max)}…[truncated ${value.length - max} chars]`;
|
|
184
180
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Literal object / null prototypes only. Class instances may have side-effecting getters or
|
|
187
|
-
* non-enumerable internals we should not traverse, so they are left to the caller's mode policy.
|
|
188
|
-
*/
|
|
189
181
|
function isPlainObject(value) {
|
|
190
182
|
if (value === null || typeof value !== "object") return false;
|
|
191
183
|
const proto = Object.getPrototypeOf(value);
|
|
@@ -235,6 +227,10 @@ function now() {
|
|
|
235
227
|
|
|
236
228
|
//#endregion
|
|
237
229
|
//#region src/util/redactUrl.ts
|
|
230
|
+
/**
|
|
231
|
+
* Matched against query-string keys, cookie names, and (in framework SDKs) prop/route-param keys. Values for
|
|
232
|
+
* matching keys are replaced with [redacted] before sending so credentials/PII don't leak in error reports.
|
|
233
|
+
*/
|
|
238
234
|
const DEFAULT_URL_DENYLIST = /password|passwd|pwd|token|secret|authorization|\bauth\b|bearer|oauth|credentials?|cookie|api[-_]?key|private[-_]?key|session|csrf|xsrf|\bpin\b|\bssn\b|card[-_]?number|\bcvv\b/i;
|
|
239
235
|
function resolveDenylist(custom, replaceDefault = false, defaultDenylist = DEFAULT_URL_DENYLIST) {
|
|
240
236
|
if (!custom) return defaultDenylist;
|
|
@@ -284,10 +280,6 @@ function redactObjectValues(obj, denylist = DEFAULT_URL_DENYLIST) {
|
|
|
284
280
|
for (const key of Object.keys(obj)) result[key] = denylist.test(key) ? "[redacted]" : obj[key];
|
|
285
281
|
return result;
|
|
286
282
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Removes userinfo (`user:pass@`) from an absolute URL's authority only. A path or query can legally
|
|
289
|
-
* contain `@`, so only the authority (after `scheme://`, up to the first `/`, `?`, `#`) is inspected.
|
|
290
|
-
*/
|
|
291
283
|
function stripUserinfo(url) {
|
|
292
284
|
const schemeMatch = /^[a-z][a-z0-9+.-]*:\/\//i.exec(url);
|
|
293
285
|
if (!schemeMatch) return url;
|
|
@@ -333,9 +325,9 @@ function hasStack(reason) {
|
|
|
333
325
|
}
|
|
334
326
|
/**
|
|
335
327
|
* Routes by whether `reason` carries a stack: stack-bearing reasons go to `reportSilently`, stackless
|
|
336
|
-
* ones to `reportUnhandledRejection`.
|
|
337
|
-
*
|
|
338
|
-
*
|
|
328
|
+
* ones to `reportUnhandledRejection`. The `.catch` stops a transport failure from surfacing as a second
|
|
329
|
+
* unhandled rejection. `reportSilently` is assumed async and left unwrapped, so a synchronous throw there
|
|
330
|
+
* still propagates.
|
|
339
331
|
*/
|
|
340
332
|
function routeRejection(reporter, reason) {
|
|
341
333
|
if (reason instanceof Error) {
|
|
@@ -481,6 +473,12 @@ Object.defineProperty(exports, 'describeRejectionReason', {
|
|
|
481
473
|
return describeRejectionReason;
|
|
482
474
|
}
|
|
483
475
|
});
|
|
476
|
+
Object.defineProperty(exports, 'evictLruIfNew', {
|
|
477
|
+
enumerable: true,
|
|
478
|
+
get: function () {
|
|
479
|
+
return evictLruIfNew;
|
|
480
|
+
}
|
|
481
|
+
});
|
|
484
482
|
Object.defineProperty(exports, 'extractCode', {
|
|
485
483
|
enumerable: true,
|
|
486
484
|
get: function () {
|