@flareapp/js 2.1.0 → 2.2.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 +12 -0
- package/README.md +5 -0
- package/dist/index.cjs +115 -556
- package/dist/index.d.cts +6 -123
- package/dist/index.d.mts +6 -123
- package/dist/index.mjs +68 -525
- package/package.json +3 -2
- package/tsconfig.json +5 -0
- package/vitest.config.ts +17 -0
package/dist/index.cjs
CHANGED
|
@@ -1,33 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __copyProps = (to, from, except, desc) => {
|
|
10
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
-
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
-
key = keys[i];
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
14
|
-
__defProp(to, key, {
|
|
15
|
-
get: ((k) => from[k]).bind(null, key),
|
|
16
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return to;
|
|
22
|
-
};
|
|
23
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
24
|
-
value: mod,
|
|
25
|
-
enumerable: true
|
|
26
|
-
}) : target, mod));
|
|
27
|
-
|
|
28
|
-
//#endregion
|
|
29
|
-
let error_stack_parser = require("error-stack-parser");
|
|
30
|
-
error_stack_parser = __toESM(error_stack_parser);
|
|
2
|
+
let _flareapp_core = require("@flareapp/core");
|
|
31
3
|
|
|
32
4
|
//#region src/browser/catchWindowErrors.ts
|
|
33
5
|
function catchWindowErrors() {
|
|
@@ -45,7 +17,7 @@ function catchWindowErrors() {
|
|
|
45
17
|
flare.reportSilently(reason);
|
|
46
18
|
return;
|
|
47
19
|
}
|
|
48
|
-
if (hasStack
|
|
20
|
+
if (hasStack(reason)) {
|
|
49
21
|
const error = new Error(describeRejectionReason(reason));
|
|
50
22
|
error.stack = reason.stack;
|
|
51
23
|
flare.reportSilently(error);
|
|
@@ -67,177 +39,12 @@ function describeRejectionReason(reason) {
|
|
|
67
39
|
}
|
|
68
40
|
return String(reason);
|
|
69
41
|
}
|
|
70
|
-
function hasStack
|
|
42
|
+
function hasStack(value) {
|
|
71
43
|
return typeof value === "object" && value !== null && "stack" in value && typeof value.stack === "string";
|
|
72
44
|
}
|
|
73
45
|
|
|
74
46
|
//#endregion
|
|
75
|
-
//#region src/
|
|
76
|
-
const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.1.0" : "?";
|
|
77
|
-
const KEY = typeof FLARE_JS_KEY === "undefined" ? "" : FLARE_JS_KEY;
|
|
78
|
-
const SOURCEMAP_VERSION = typeof FLARE_SOURCEMAP_VERSION === "undefined" ? "" : FLARE_SOURCEMAP_VERSION;
|
|
79
|
-
|
|
80
|
-
//#endregion
|
|
81
|
-
//#region src/util/assert.ts
|
|
82
|
-
function assert(value, message, debug) {
|
|
83
|
-
if (debug && !value) console.error(`Flare JavaScript client v${CLIENT_VERSION}: ${message}`);
|
|
84
|
-
return !!value;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region src/util/convertToError.ts
|
|
89
|
-
function convertToError(error) {
|
|
90
|
-
if (error instanceof Error) return error;
|
|
91
|
-
if (typeof error === "string") return new Error(error);
|
|
92
|
-
if (typeof error === "object" && error !== null) {
|
|
93
|
-
const obj = error;
|
|
94
|
-
const message = typeof obj.message === "string" ? obj.message : String(error);
|
|
95
|
-
const converted = new Error(message);
|
|
96
|
-
if (typeof obj.stack === "string") converted.stack = obj.stack;
|
|
97
|
-
if (typeof obj.name === "string") converted.name = obj.name;
|
|
98
|
-
return converted;
|
|
99
|
-
}
|
|
100
|
-
return new Error(String(error));
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
//#endregion
|
|
104
|
-
//#region src/util/assertKey.ts
|
|
105
|
-
function assertKey(key, debug) {
|
|
106
|
-
return assert(key, "The client was not yet initialised with an API key. Run client.light('<flare-project-key>') when you initialise your app. If you are running in dev mode and didn't run the light command on purpose, you can ignore this error.", debug);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
//#endregion
|
|
110
|
-
//#region src/util/extractCode.ts
|
|
111
|
-
const MAX_CODE_LENGTH = 64;
|
|
112
|
-
function extractCode(error) {
|
|
113
|
-
const code = error.code;
|
|
114
|
-
if (typeof code !== "string" || code.length === 0) return;
|
|
115
|
-
return code.slice(0, MAX_CODE_LENGTH);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
//#endregion
|
|
119
|
-
//#region src/util/flatJsonStringify.ts
|
|
120
|
-
function flatJsonStringify(json) {
|
|
121
|
-
return JSON.stringify(decycle(json));
|
|
122
|
-
}
|
|
123
|
-
function isPlainObject(value) {
|
|
124
|
-
if (typeof value !== "object" || value === null) return false;
|
|
125
|
-
const proto = Object.getPrototypeOf(value);
|
|
126
|
-
return proto === Object.prototype || proto === null;
|
|
127
|
-
}
|
|
128
|
-
function decycle(root) {
|
|
129
|
-
const inPath = /* @__PURE__ */ new WeakSet();
|
|
130
|
-
function clone(node) {
|
|
131
|
-
if (Array.isArray(node)) {
|
|
132
|
-
if (inPath.has(node)) return "[Circular]";
|
|
133
|
-
inPath.add(node);
|
|
134
|
-
const result = node.map(clone);
|
|
135
|
-
inPath.delete(node);
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
if (isPlainObject(node)) {
|
|
139
|
-
if (inPath.has(node)) return "[Circular]";
|
|
140
|
-
inPath.add(node);
|
|
141
|
-
const result = {};
|
|
142
|
-
for (const [k, v] of Object.entries(node)) result[k] = clone(v);
|
|
143
|
-
inPath.delete(node);
|
|
144
|
-
return result;
|
|
145
|
-
}
|
|
146
|
-
return node;
|
|
147
|
-
}
|
|
148
|
-
return clone(root);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
//#endregion
|
|
152
|
-
//#region src/util/glowsToEvents.ts
|
|
153
|
-
function glowsToEvents(glows) {
|
|
154
|
-
return glows.map((glow) => ({
|
|
155
|
-
type: "php_glow",
|
|
156
|
-
startTimeUnixNano: Math.round(glow.microtime * 1e9),
|
|
157
|
-
endTimeUnixNano: null,
|
|
158
|
-
attributes: {
|
|
159
|
-
"glow.name": String(glow.name),
|
|
160
|
-
"glow.level": glow.messageLevel,
|
|
161
|
-
"glow.context": glow.metaData ?? {}
|
|
162
|
-
}
|
|
163
|
-
}));
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
//#endregion
|
|
167
|
-
//#region src/util/now.ts
|
|
168
|
-
function now() {
|
|
169
|
-
return Math.round(Date.now() / 1e3);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
//#endregion
|
|
173
|
-
//#region src/util/redactUrl.ts
|
|
174
|
-
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;
|
|
175
|
-
function resolveDenylist(custom, replaceDefault = false, defaultDenylist = DEFAULT_URL_DENYLIST) {
|
|
176
|
-
if (!custom) return defaultDenylist;
|
|
177
|
-
if (replaceDefault) {
|
|
178
|
-
const safeFlags = custom.flags.replace(/[gy]/g, "");
|
|
179
|
-
return new RegExp(custom.source, safeFlags);
|
|
180
|
-
}
|
|
181
|
-
const flags = unionFlags(defaultDenylist.flags, custom.flags);
|
|
182
|
-
return new RegExp(`(?:${defaultDenylist.source})|(?:${custom.source})`, flags);
|
|
183
|
-
}
|
|
184
|
-
function unionFlags(a, b) {
|
|
185
|
-
const merged = /* @__PURE__ */ new Set();
|
|
186
|
-
for (const flag of a + b) {
|
|
187
|
-
if (flag === "g" || flag === "y") continue;
|
|
188
|
-
merged.add(flag);
|
|
189
|
-
}
|
|
190
|
-
return [...merged].join("");
|
|
191
|
-
}
|
|
192
|
-
function redactFullPath(fullPath, denylist = DEFAULT_URL_DENYLIST) {
|
|
193
|
-
const queryStart = fullPath.indexOf("?");
|
|
194
|
-
if (queryStart === -1) return fullPath;
|
|
195
|
-
const hashStart = fullPath.indexOf("#", queryStart);
|
|
196
|
-
const queryEnd = hashStart === -1 ? fullPath.length : hashStart;
|
|
197
|
-
const prefix = fullPath.slice(0, queryStart + 1);
|
|
198
|
-
const queryString = fullPath.slice(queryStart + 1, queryEnd);
|
|
199
|
-
const suffix = fullPath.slice(queryEnd);
|
|
200
|
-
return `${prefix}${queryString.split("&").map((pair) => {
|
|
201
|
-
if (pair === "") return pair;
|
|
202
|
-
const eq = pair.indexOf("=");
|
|
203
|
-
const rawKey = eq === -1 ? pair : pair.slice(0, eq);
|
|
204
|
-
const decodedKey = safeDecode(rawKey);
|
|
205
|
-
if (!denylist.test(decodedKey)) return pair;
|
|
206
|
-
return eq === -1 ? rawKey : `${rawKey}=[redacted]`;
|
|
207
|
-
}).join("&")}${suffix}`;
|
|
208
|
-
}
|
|
209
|
-
function safeDecode(value) {
|
|
210
|
-
try {
|
|
211
|
-
return decodeURIComponent(value);
|
|
212
|
-
} catch {
|
|
213
|
-
return value;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
//#endregion
|
|
218
|
-
//#region src/api/Api.ts
|
|
219
|
-
var Api = class {
|
|
220
|
-
report(report, url, key, reportBrowserExtensionErrors, debug = false) {
|
|
221
|
-
return fetch(url, {
|
|
222
|
-
method: "POST",
|
|
223
|
-
headers: {
|
|
224
|
-
"Accept": "application/json",
|
|
225
|
-
"Content-Type": "application/json",
|
|
226
|
-
"X-Api-Token": key ?? "",
|
|
227
|
-
"X-Report-Browser-Extension-Errors": JSON.stringify(reportBrowserExtensionErrors),
|
|
228
|
-
"X-Flare-Client-Version": "2"
|
|
229
|
-
},
|
|
230
|
-
body: flatJsonStringify(report)
|
|
231
|
-
}).then((response) => {
|
|
232
|
-
if (debug && response.status !== 201) console.error(`Received response with status ${response.status} from Flare`);
|
|
233
|
-
}, (error) => {
|
|
234
|
-
if (debug) console.error(error);
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
//#endregion
|
|
240
|
-
//#region src/context/cookie.ts
|
|
47
|
+
//#region src/browser/context/cookie.ts
|
|
241
48
|
function cookie() {
|
|
242
49
|
if (!window.document.cookie) return {};
|
|
243
50
|
const cookies = {};
|
|
@@ -254,384 +61,92 @@ function cookie() {
|
|
|
254
61
|
}
|
|
255
62
|
|
|
256
63
|
//#endregion
|
|
257
|
-
//#region src/context/request.ts
|
|
64
|
+
//#region src/browser/context/request.ts
|
|
258
65
|
function request(urlDenylist) {
|
|
259
66
|
return {
|
|
260
|
-
"url.full":
|
|
67
|
+
"url.full": (0, _flareapp_core.redactUrlQuery)(window.location.href, urlDenylist),
|
|
261
68
|
"user_agent.original": window.navigator.userAgent,
|
|
262
|
-
"http.request.referrer":
|
|
69
|
+
"http.request.referrer": (0, _flareapp_core.redactUrlQuery)(window.document.referrer, urlDenylist),
|
|
263
70
|
"document.ready_state": window.document.readyState
|
|
264
71
|
};
|
|
265
72
|
}
|
|
266
73
|
|
|
267
74
|
//#endregion
|
|
268
|
-
//#region src/context/requestData.ts
|
|
75
|
+
//#region src/browser/context/requestData.ts
|
|
269
76
|
function requestData(urlDenylist) {
|
|
270
77
|
if (!window.location.search) return {};
|
|
271
|
-
return { "url.query":
|
|
78
|
+
return { "url.query": (0, _flareapp_core.redactUrlQuery)(window.location.search, urlDenylist).replace(/^\?/, "") };
|
|
272
79
|
}
|
|
273
80
|
|
|
274
81
|
//#endregion
|
|
275
|
-
//#region src/context/
|
|
276
|
-
|
|
277
|
-
if (typeof window === "undefined") return {};
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
82
|
+
//#region src/browser/context/collectBrowser.ts
|
|
83
|
+
const collectBrowser = (config) => {
|
|
84
|
+
if (typeof window === "undefined") return { "flare.entry_point.type": "server" };
|
|
85
|
+
const attrs = { "flare.entry_point.type": "web" };
|
|
86
|
+
if (window?.location?.href) {
|
|
87
|
+
attrs["flare.entry_point.value"] = (0, _flareapp_core.redactUrlQuery)(window.location.href, config.urlDenylist);
|
|
88
|
+
if (window.location.pathname) {
|
|
89
|
+
attrs["flare.entry_point.handler.identifier"] = window.location.pathname;
|
|
90
|
+
attrs["flare.entry_point.handler.type"] = "browser";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
Object.assign(attrs, request(config.urlDenylist));
|
|
94
|
+
Object.assign(attrs, requestData(config.urlDenylist));
|
|
95
|
+
Object.assign(attrs, cookie());
|
|
96
|
+
return attrs;
|
|
97
|
+
};
|
|
292
98
|
|
|
293
99
|
//#endregion
|
|
294
|
-
//#region src/
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
return (isNode() && isLocalFileUrl(url) ? readFileFromDisk(url) : readFileWithFetch(url)).then((text) => {
|
|
327
|
-
if (text !== null) cachedFiles[url] = text;
|
|
328
|
-
return text;
|
|
329
|
-
});
|
|
330
|
-
}
|
|
331
|
-
function readFileWithFetch(url) {
|
|
332
|
-
return fetch(url).then((response) => {
|
|
333
|
-
if (response.status !== 200) return null;
|
|
334
|
-
return response.text();
|
|
335
|
-
}).catch(() => null);
|
|
336
|
-
}
|
|
337
|
-
function readFileFromDisk(url) {
|
|
338
|
-
return nativeImport("node:url").then(({ fileURLToPath }) => {
|
|
339
|
-
const path = /^file:\/\//i.test(url) ? fileURLToPath(url) : url;
|
|
340
|
-
return nativeImport("node:fs/promises").then(({ readFile: readFileAsync }) => readFileAsync(path, "utf-8"));
|
|
341
|
-
}).catch(() => null);
|
|
342
|
-
}
|
|
343
|
-
function readLinesFromFile(fileText, lineNumber, columnNumber, maxSnippetLineLength = 1e3, maxSnippetLines = 40) {
|
|
344
|
-
const codeSnippet = {};
|
|
345
|
-
let trimmedColumnNumber = null;
|
|
346
|
-
const lines = fileText.split("\n");
|
|
347
|
-
const errorLineIndex = lineNumber - 1;
|
|
348
|
-
const half = Math.floor(maxSnippetLines / 2);
|
|
349
|
-
for (let i = -half; i <= half; i++) {
|
|
350
|
-
const currentLineIndex = errorLineIndex + i;
|
|
351
|
-
if (currentLineIndex < 0 || !lines[currentLineIndex]) continue;
|
|
352
|
-
const displayLine = currentLineIndex + 1;
|
|
353
|
-
const line = lines[currentLineIndex];
|
|
354
|
-
if (line.length > maxSnippetLineLength) {
|
|
355
|
-
if (columnNumber && columnNumber > maxSnippetLineLength / 2) {
|
|
356
|
-
const start = columnNumber - Math.round(maxSnippetLineLength / 2);
|
|
357
|
-
codeSnippet[displayLine] = line.slice(start, start + maxSnippetLineLength);
|
|
358
|
-
if (displayLine === lineNumber) trimmedColumnNumber = Math.round(maxSnippetLineLength / 2);
|
|
359
|
-
continue;
|
|
360
|
-
}
|
|
361
|
-
codeSnippet[displayLine] = line.slice(0, maxSnippetLineLength) + "…";
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
codeSnippet[displayLine] = line;
|
|
100
|
+
//#region src/browser/FetchFileReader.ts
|
|
101
|
+
/**
|
|
102
|
+
* Browser `FileReader` implementation that fetches source files over HTTP(S).
|
|
103
|
+
*
|
|
104
|
+
* Wired into `@flareapp/js`'s singleton so the stack-trace builder can pull
|
|
105
|
+
* the original source for each frame and render a code snippet around the
|
|
106
|
+
* offending line. The source URL comes from the frame (usually the URL of
|
|
107
|
+
* the JS bundle that produced the error, post-sourcemap resolution).
|
|
108
|
+
*
|
|
109
|
+
* Three safety gates:
|
|
110
|
+
*
|
|
111
|
+
* 1. **Scheme allowlist.** Only `http:` and `https:` URLs are fetched. Other
|
|
112
|
+
* schemes (`chrome-extension://`, `file://`, `blob:`, `data:`) return
|
|
113
|
+
* `null` immediately. This avoids surprise privilege boundaries (e.g.,
|
|
114
|
+
* pages should not read extension-internal files) and dodges CORS/CSP
|
|
115
|
+
* walls that would error noisily.
|
|
116
|
+
* 2. **Status check.** Only `200 OK` responses are used. 3xx redirects are
|
|
117
|
+
* handled transparently by `fetch`; 4xx/5xx fall back to `null` so the
|
|
118
|
+
* snippet is simply omitted from the report.
|
|
119
|
+
* 3. **Catch-all.** Network failures, CORS errors, and aborted requests
|
|
120
|
+
* return `null`. We never let a source-fetch failure leak as an error
|
|
121
|
+
* that the consumer page would see.
|
|
122
|
+
*
|
|
123
|
+
* The `read()` contract returns `null` on any failure path, never throws.
|
|
124
|
+
*/
|
|
125
|
+
var FetchFileReader = class {
|
|
126
|
+
read(url) {
|
|
127
|
+
if (!/^https?:\/\//i.test(url)) return Promise.resolve(null);
|
|
128
|
+
return fetch(url).then((response) => {
|
|
129
|
+
if (response.status !== 200) return null;
|
|
130
|
+
return response.text();
|
|
131
|
+
}).catch(() => null);
|
|
365
132
|
}
|
|
366
|
-
|
|
367
|
-
codeSnippet,
|
|
368
|
-
trimmedColumnNumber
|
|
369
|
-
};
|
|
370
|
-
}
|
|
133
|
+
};
|
|
371
134
|
|
|
372
135
|
//#endregion
|
|
373
|
-
//#region src/
|
|
374
|
-
|
|
375
|
-
return new Promise((resolve) => {
|
|
376
|
-
if (!hasStack(error)) return resolve([fallbackFrame("stacktrace missing")]);
|
|
377
|
-
let parsedFrames;
|
|
378
|
-
try {
|
|
379
|
-
parsedFrames = error_stack_parser.default.parse(error);
|
|
380
|
-
} catch (parseError) {
|
|
381
|
-
assert(false, "Couldn't parse stacktrace of below error:", debug);
|
|
382
|
-
if (debug) {
|
|
383
|
-
console.error(parseError);
|
|
384
|
-
console.error(error);
|
|
385
|
-
}
|
|
386
|
-
return resolve([fallbackFrame("stacktrace could not be parsed")]);
|
|
387
|
-
}
|
|
388
|
-
Promise.all(parsedFrames.map((frame) => {
|
|
389
|
-
return getCodeSnippet(frame.fileName, frame.lineNumber, frame.columnNumber).then((snippet) => ({
|
|
390
|
-
lineNumber: frame.lineNumber || 1,
|
|
391
|
-
columnNumber: frame.columnNumber || 1,
|
|
392
|
-
method: frame.functionName || "Anonymous or unknown function",
|
|
393
|
-
file: frame.fileName || "Unknown file",
|
|
394
|
-
codeSnippet: snippet.codeSnippet,
|
|
395
|
-
class: "",
|
|
396
|
-
isApplicationFrame: isApplicationFrame(frame.fileName)
|
|
397
|
-
}));
|
|
398
|
-
})).then(resolve);
|
|
399
|
-
});
|
|
400
|
-
}
|
|
401
|
-
function fallbackFrame(reason) {
|
|
402
|
-
return {
|
|
403
|
-
lineNumber: 0,
|
|
404
|
-
columnNumber: 0,
|
|
405
|
-
method: "unknown",
|
|
406
|
-
file: "unknown",
|
|
407
|
-
codeSnippet: { 0: `Could not read from file: ${reason}` },
|
|
408
|
-
class: "unknown"
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
function hasStack(err) {
|
|
412
|
-
if (!err || typeof err !== "object") return false;
|
|
413
|
-
const e = err;
|
|
414
|
-
const stack = e.stack ?? e.stacktrace ?? e["opera#sourceloc"];
|
|
415
|
-
return typeof stack === "string" && stack !== `${e.name}: ${e.message}`;
|
|
416
|
-
}
|
|
417
|
-
function isApplicationFrame(fileName) {
|
|
418
|
-
if (!fileName) return true;
|
|
419
|
-
if (/[/\\]node_modules[/\\]/.test(fileName)) return false;
|
|
420
|
-
if (/(^|[/\\])(vendor|vendors)[.~-][^/\\]*\.js/i.test(fileName)) return false;
|
|
421
|
-
return true;
|
|
422
|
-
}
|
|
136
|
+
//#region src/env/index.ts
|
|
137
|
+
const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.2.0" : "?";
|
|
423
138
|
|
|
424
139
|
//#endregion
|
|
425
|
-
//#region src/
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
stage: "",
|
|
433
|
-
maxGlowsPerReport: 30,
|
|
434
|
-
ingestUrl: "https://ingress.flareapp.io/v1/errors",
|
|
435
|
-
reportBrowserExtensionErrors: false,
|
|
436
|
-
debug: false,
|
|
437
|
-
urlDenylist: DEFAULT_URL_DENYLIST,
|
|
438
|
-
replaceDefaultUrlDenylist: false,
|
|
439
|
-
sampleRate: 1,
|
|
440
|
-
beforeEvaluate: (error) => error,
|
|
441
|
-
beforeSubmit: (report) => report
|
|
442
|
-
};
|
|
443
|
-
_glows = [];
|
|
444
|
-
pendingAttributes = {};
|
|
445
|
-
entryPoint = null;
|
|
446
|
-
sdkInfo = {
|
|
447
|
-
name: DEFAULT_SDK_NAME,
|
|
448
|
-
version: CLIENT_VERSION
|
|
449
|
-
};
|
|
450
|
-
framework = null;
|
|
451
|
-
constructor(api = new Api()) {
|
|
452
|
-
this.api = api;
|
|
453
|
-
}
|
|
454
|
-
get config() {
|
|
455
|
-
return this._config;
|
|
456
|
-
}
|
|
457
|
-
get glows() {
|
|
458
|
-
return this._glows;
|
|
459
|
-
}
|
|
460
|
-
light(key = KEY, debug) {
|
|
461
|
-
this._config.key = key;
|
|
462
|
-
if (debug !== void 0) this._config.debug = debug;
|
|
463
|
-
return this;
|
|
464
|
-
}
|
|
465
|
-
configure(config) {
|
|
466
|
-
this._config = {
|
|
467
|
-
...this._config,
|
|
468
|
-
...config
|
|
469
|
-
};
|
|
470
|
-
if (config.sampleRate !== void 0) this._config.sampleRate = Math.max(0, Math.min(1, config.sampleRate));
|
|
471
|
-
this._config.urlDenylist = resolveDenylist(config.urlDenylist, config.replaceDefaultUrlDenylist ?? this._config.replaceDefaultUrlDenylist);
|
|
472
|
-
return this;
|
|
473
|
-
}
|
|
474
|
-
async test() {
|
|
475
|
-
const report = await this.createReportFromError(/* @__PURE__ */ new Error("The Flare client is set up correctly!"));
|
|
476
|
-
if (!report) return;
|
|
477
|
-
return this.sendReport(report);
|
|
478
|
-
}
|
|
479
|
-
glow(name, level = "info", data = []) {
|
|
480
|
-
const time = now();
|
|
481
|
-
this._glows.push({
|
|
482
|
-
name,
|
|
483
|
-
messageLevel: level,
|
|
484
|
-
metaData: data,
|
|
485
|
-
time,
|
|
486
|
-
microtime: time
|
|
487
|
-
});
|
|
488
|
-
if (this._glows.length > this._config.maxGlowsPerReport) this._glows = this._glows.slice(this._glows.length - this._config.maxGlowsPerReport);
|
|
489
|
-
return this;
|
|
490
|
-
}
|
|
491
|
-
clearGlows() {
|
|
492
|
-
this._glows = [];
|
|
493
|
-
return this;
|
|
494
|
-
}
|
|
495
|
-
addContext(name, value) {
|
|
496
|
-
const existing = this.pendingAttributes["context.custom"] ?? {};
|
|
497
|
-
this.pendingAttributes["context.custom"] = {
|
|
498
|
-
...existing,
|
|
499
|
-
[name]: value
|
|
500
|
-
};
|
|
501
|
-
return this;
|
|
502
|
-
}
|
|
503
|
-
addContextGroup(groupName, value) {
|
|
504
|
-
this.pendingAttributes[`context.${groupName}`] = value;
|
|
505
|
-
return this;
|
|
506
|
-
}
|
|
507
|
-
setEntryPoint(handler) {
|
|
508
|
-
this.entryPoint = handler;
|
|
509
|
-
return this;
|
|
510
|
-
}
|
|
511
|
-
setSdkInfo(info) {
|
|
512
|
-
this.sdkInfo = info;
|
|
513
|
-
return this;
|
|
514
|
-
}
|
|
515
|
-
setFramework(framework) {
|
|
516
|
-
this.framework = framework;
|
|
517
|
-
this.addContext("framework", framework.name.toLowerCase());
|
|
518
|
-
return this;
|
|
519
|
-
}
|
|
520
|
-
async report(error, attributes = {}) {
|
|
521
|
-
if (this._config.sampleRate < 1 && Math.random() >= this._config.sampleRate) return;
|
|
522
|
-
const seenAtUnixNano = Date.now() * 1e6;
|
|
523
|
-
const coerced = error instanceof Error ? error : new Error(typeof error === "string" ? error : String(error));
|
|
524
|
-
const errorToReport = await this._config.beforeEvaluate(coerced);
|
|
525
|
-
if (!errorToReport) return;
|
|
526
|
-
const report = await this.createReportFromError(errorToReport, attributes, seenAtUnixNano);
|
|
527
|
-
if (!report) return;
|
|
528
|
-
return this.sendReport(report);
|
|
529
|
-
}
|
|
530
|
-
reportSilently(error, attributes = {}) {
|
|
531
|
-
Promise.resolve(this.report(error, attributes)).catch(() => {});
|
|
532
|
-
}
|
|
533
|
-
async reportUnhandledRejection(message, attributes = {}) {
|
|
534
|
-
if (Math.random() >= this._config.sampleRate) return;
|
|
535
|
-
const seenAtUnixNano = Date.now() * 1e6;
|
|
536
|
-
const report = this.buildReport({
|
|
537
|
-
exceptionClass: "UnhandledRejection",
|
|
538
|
-
message,
|
|
539
|
-
stacktrace: [],
|
|
540
|
-
isLog: false,
|
|
541
|
-
level: void 0,
|
|
542
|
-
extraAttributes: attributes,
|
|
543
|
-
code: void 0,
|
|
544
|
-
seenAtUnixNano
|
|
545
|
-
});
|
|
546
|
-
return this.sendReport(report);
|
|
547
|
-
}
|
|
548
|
-
async reportMessage(message, level, attributes = {}) {
|
|
549
|
-
if (this._config.sampleRate < 1 && Math.random() >= this._config.sampleRate) return;
|
|
550
|
-
const seenAtUnixNano = Date.now() * 1e6;
|
|
551
|
-
const stackTrace = await createStackTrace(/* @__PURE__ */ new Error(), this._config.debug);
|
|
552
|
-
stackTrace.shift();
|
|
553
|
-
const report = this.buildReport({
|
|
554
|
-
exceptionClass: "Log",
|
|
555
|
-
message,
|
|
556
|
-
stacktrace: stackTrace,
|
|
557
|
-
isLog: true,
|
|
558
|
-
level,
|
|
559
|
-
extraAttributes: attributes,
|
|
560
|
-
code: void 0,
|
|
561
|
-
seenAtUnixNano
|
|
562
|
-
});
|
|
563
|
-
return this.sendReport(report);
|
|
564
|
-
}
|
|
565
|
-
async createReportFromError(error, attributes = {}, seenAtUnixNano = Date.now() * 1e6) {
|
|
566
|
-
if (!assert(error, "No error provided.", this._config.debug)) return false;
|
|
567
|
-
const stacktrace = await createStackTrace(error, this._config.debug);
|
|
568
|
-
assert(stacktrace.length, "Couldn't generate stacktrace of this error: " + error, this._config.debug);
|
|
569
|
-
const exceptionClass = error.constructor && error.constructor.name ? error.constructor.name : "undefined";
|
|
570
|
-
return this.buildReport({
|
|
571
|
-
exceptionClass,
|
|
572
|
-
message: error.message,
|
|
573
|
-
stacktrace,
|
|
574
|
-
isLog: false,
|
|
575
|
-
level: void 0,
|
|
576
|
-
extraAttributes: attributes,
|
|
577
|
-
code: extractCode(error),
|
|
578
|
-
seenAtUnixNano
|
|
140
|
+
//#region src/index.ts
|
|
141
|
+
var Flare = class extends _flareapp_core.Flare {
|
|
142
|
+
constructor(api = new _flareapp_core.Api(), contextCollector = collectBrowser, fileReader = new FetchFileReader(), scopeProvider = new _flareapp_core.GlobalScopeProvider()) {
|
|
143
|
+
super(api, contextCollector, fileReader, scopeProvider);
|
|
144
|
+
this.setSdkInfo({
|
|
145
|
+
name: "@flareapp/js",
|
|
146
|
+
version: CLIENT_VERSION
|
|
579
147
|
});
|
|
580
148
|
}
|
|
581
|
-
buildReport(input) {
|
|
582
|
-
const baseAttributes = {
|
|
583
|
-
"telemetry.sdk.language": "javascript",
|
|
584
|
-
"telemetry.sdk.name": this.sdkInfo.name,
|
|
585
|
-
"telemetry.sdk.version": this.sdkInfo.version,
|
|
586
|
-
"flare.language.name": "javascript",
|
|
587
|
-
"flare.entry_point.type": "web"
|
|
588
|
-
};
|
|
589
|
-
if (typeof window !== "undefined" && window?.location?.href) baseAttributes["flare.entry_point.value"] = redactFullPath(window.location.href, this._config.urlDenylist);
|
|
590
|
-
const handlerIdentifier = this.entryPoint?.identifier ?? (typeof window !== "undefined" && window?.location?.pathname ? window.location.pathname : void 0);
|
|
591
|
-
const handlerType = this.entryPoint?.type ?? (typeof window !== "undefined" && window ? "browser" : void 0);
|
|
592
|
-
if (handlerIdentifier !== void 0) baseAttributes["flare.entry_point.handler.identifier"] = handlerIdentifier;
|
|
593
|
-
if (handlerType !== void 0) baseAttributes["flare.entry_point.handler.type"] = handlerType;
|
|
594
|
-
if (this.entryPoint?.name !== void 0) baseAttributes["flare.entry_point.handler.name"] = this.entryPoint.name;
|
|
595
|
-
if (this.framework?.name) baseAttributes["flare.framework.name"] = this.framework.name;
|
|
596
|
-
if (this.framework?.version) baseAttributes["flare.framework.version"] = this.framework.version;
|
|
597
|
-
if (this._config.stage) baseAttributes["service.stage"] = this._config.stage;
|
|
598
|
-
if (this._config.version) baseAttributes["service.version"] = this._config.version;
|
|
599
|
-
const attributes = {
|
|
600
|
-
...baseAttributes,
|
|
601
|
-
...collectAttributes(this._config.urlDenylist),
|
|
602
|
-
...this.pendingAttributes,
|
|
603
|
-
...input.extraAttributes
|
|
604
|
-
};
|
|
605
|
-
const pendingCustom = this.pendingAttributes["context.custom"];
|
|
606
|
-
const extraCustom = input.extraAttributes["context.custom"];
|
|
607
|
-
if (pendingCustom && extraCustom && typeof pendingCustom === "object" && typeof extraCustom === "object" && !Array.isArray(pendingCustom) && !Array.isArray(extraCustom)) attributes["context.custom"] = {
|
|
608
|
-
...pendingCustom,
|
|
609
|
-
...extraCustom
|
|
610
|
-
};
|
|
611
|
-
const report = {
|
|
612
|
-
exceptionClass: input.exceptionClass,
|
|
613
|
-
message: input.message,
|
|
614
|
-
seenAtUnixNano: input.seenAtUnixNano,
|
|
615
|
-
stacktrace: input.stacktrace,
|
|
616
|
-
events: glowsToEvents(this._glows),
|
|
617
|
-
attributes
|
|
618
|
-
};
|
|
619
|
-
if (input.isLog) report.isLog = true;
|
|
620
|
-
if (input.level !== void 0) report.level = input.level;
|
|
621
|
-
if (this._config.sourcemapVersionId) report.sourcemapVersionId = this._config.sourcemapVersionId;
|
|
622
|
-
if (input.code !== void 0) report.code = input.code;
|
|
623
|
-
return report;
|
|
624
|
-
}
|
|
625
|
-
async sendReport(report) {
|
|
626
|
-
if (!assertKey(this._config.key, this._config.debug)) return;
|
|
627
|
-
const reportToSubmit = await this._config.beforeSubmit(report);
|
|
628
|
-
if (!reportToSubmit) return;
|
|
629
|
-
return this.api.report(reportToSubmit, this._config.ingestUrl, this._config.key, this._config.reportBrowserExtensionErrors, this._config.debug);
|
|
630
|
-
}
|
|
631
149
|
};
|
|
632
|
-
|
|
633
|
-
//#endregion
|
|
634
|
-
//#region src/index.ts
|
|
635
150
|
const flare = new Flare();
|
|
636
151
|
if (typeof window !== "undefined" && window) {
|
|
637
152
|
window.flare = flare;
|
|
@@ -639,9 +154,53 @@ if (typeof window !== "undefined" && window) {
|
|
|
639
154
|
}
|
|
640
155
|
|
|
641
156
|
//#endregion
|
|
642
|
-
exports
|
|
157
|
+
Object.defineProperty(exports, 'DEFAULT_URL_DENYLIST', {
|
|
158
|
+
enumerable: true,
|
|
159
|
+
get: function () {
|
|
160
|
+
return _flareapp_core.DEFAULT_URL_DENYLIST;
|
|
161
|
+
}
|
|
162
|
+
});
|
|
643
163
|
exports.Flare = Flare;
|
|
644
|
-
exports
|
|
164
|
+
Object.defineProperty(exports, 'GlobalScopeProvider', {
|
|
165
|
+
enumerable: true,
|
|
166
|
+
get: function () {
|
|
167
|
+
return _flareapp_core.GlobalScopeProvider;
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
Object.defineProperty(exports, 'NullFileReader', {
|
|
171
|
+
enumerable: true,
|
|
172
|
+
get: function () {
|
|
173
|
+
return _flareapp_core.NullFileReader;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
Object.defineProperty(exports, 'Scope', {
|
|
177
|
+
enumerable: true,
|
|
178
|
+
get: function () {
|
|
179
|
+
return _flareapp_core.Scope;
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
Object.defineProperty(exports, 'convertToError', {
|
|
183
|
+
enumerable: true,
|
|
184
|
+
get: function () {
|
|
185
|
+
return _flareapp_core.convertToError;
|
|
186
|
+
}
|
|
187
|
+
});
|
|
645
188
|
exports.flare = flare;
|
|
646
|
-
exports
|
|
647
|
-
|
|
189
|
+
Object.defineProperty(exports, 'redactFullPath', {
|
|
190
|
+
enumerable: true,
|
|
191
|
+
get: function () {
|
|
192
|
+
return _flareapp_core.redactUrlQuery;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
Object.defineProperty(exports, 'redactUrlQuery', {
|
|
196
|
+
enumerable: true,
|
|
197
|
+
get: function () {
|
|
198
|
+
return _flareapp_core.redactUrlQuery;
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
Object.defineProperty(exports, 'resolveDenylist', {
|
|
202
|
+
enumerable: true,
|
|
203
|
+
get: function () {
|
|
204
|
+
return _flareapp_core.resolveDenylist;
|
|
205
|
+
}
|
|
206
|
+
});
|