@flareapp/core 2.5.1 → 2.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/dist/index.cjs CHANGED
@@ -30,7 +30,7 @@ let error_stack_parser = require("error-stack-parser");
30
30
  error_stack_parser = __toESM(error_stack_parser);
31
31
 
32
32
  //#region src/env/index.ts
33
- const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.5.1" : "?";
33
+ const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.6.0" : "?";
34
34
  const KEY = typeof FLARE_JS_KEY === "undefined" ? "" : FLARE_JS_KEY;
35
35
  const SOURCEMAP_VERSION = typeof FLARE_SOURCEMAP_VERSION === "undefined" ? "" : FLARE_SOURCEMAP_VERSION;
36
36
 
@@ -171,6 +171,49 @@ function safeDecode(value) {
171
171
  }
172
172
  }
173
173
 
174
+ //#endregion
175
+ //#region src/util/rejection.ts
176
+ /** Best-effort human-readable description of an arbitrary rejection reason. */
177
+ function describeRejectionReason(reason) {
178
+ if (typeof reason === "string") return reason;
179
+ if (reason && typeof reason === "object") {
180
+ const message = reason.message;
181
+ if (typeof message === "string" && message) return message;
182
+ try {
183
+ return JSON.stringify(reason);
184
+ } catch {
185
+ return "Unhandled promise rejection (non-serializable reason)";
186
+ }
187
+ }
188
+ return String(reason);
189
+ }
190
+ function hasStack$1(reason) {
191
+ return !!reason && typeof reason === "object" && typeof reason.stack === "string";
192
+ }
193
+ /**
194
+ * Route a rejection reason to the reporter: an Error (or any stack-bearing
195
+ * object) goes to `reportSilently` so the STACK survives; only a stackless
196
+ * reason falls back to `reportUnhandledRejection` (string message, empty-stack
197
+ * `UnhandledRejection` class). Any rejection from `reportUnhandledRejection`'s
198
+ * returned promise is swallowed so a transport failure cannot itself surface as
199
+ * an unhandled rejection. `reportSilently` is assumed not to throw synchronously
200
+ * (core's does its work asynchronously); it is intentionally not wrapped, so a
201
+ * synchronous throw there would propagate.
202
+ */
203
+ function routeRejection(reporter, reason) {
204
+ if (reason instanceof Error) {
205
+ reporter.reportSilently(reason);
206
+ return;
207
+ }
208
+ if (hasStack$1(reason)) {
209
+ const error = new Error(describeRejectionReason(reason));
210
+ error.stack = reason.stack;
211
+ reporter.reportSilently(error);
212
+ return;
213
+ }
214
+ Promise.resolve(reporter.reportUnhandledRejection(describeRejectionReason(reason))).catch(() => {});
215
+ }
216
+
174
217
  //#endregion
175
218
  //#region src/api/Api.ts
176
219
  var Api = class {
@@ -674,18 +717,24 @@ function createStackTrace(error, debug, fileReader) {
674
717
  return resolve([fallbackFrame("stacktrace could not be parsed")]);
675
718
  }
676
719
  Promise.all(parsedFrames.map((frame) => {
677
- return getCodeSnippet(fileReader, frame.fileName, frame.lineNumber, frame.columnNumber).then((snippet) => ({
720
+ const fileName = normalizeFileName(frame.fileName);
721
+ return getCodeSnippet(fileReader, fileName, frame.lineNumber, frame.columnNumber).then((snippet) => ({
678
722
  lineNumber: frame.lineNumber || 1,
679
723
  columnNumber: frame.columnNumber || 1,
680
724
  method: frame.functionName || "Anonymous or unknown function",
681
- file: frame.fileName || "Unknown file",
725
+ file: fileName || "Unknown file",
682
726
  codeSnippet: snippet.codeSnippet,
683
727
  class: "",
684
- isApplicationFrame: isApplicationFrame(frame.fileName)
728
+ isApplicationFrame: isApplicationFrame(fileName)
685
729
  }));
686
730
  })).then(resolve);
687
731
  });
688
732
  }
733
+ const HERMES_ADDRESS_PREFIX = "address at ";
734
+ function normalizeFileName(fileName) {
735
+ if (fileName?.startsWith(HERMES_ADDRESS_PREFIX)) return fileName.slice(11);
736
+ return fileName;
737
+ }
689
738
  function fallbackFrame(reason) {
690
739
  return {
691
740
  lineNumber: 0,
@@ -1196,6 +1245,7 @@ exports.assert = assert;
1196
1245
  exports.assertKey = assertKey;
1197
1246
  exports.convertToError = convertToError;
1198
1247
  exports.createStackTrace = createStackTrace;
1248
+ exports.describeRejectionReason = describeRejectionReason;
1199
1249
  exports.extractCode = extractCode;
1200
1250
  exports.flatJsonStringify = flatJsonStringify;
1201
1251
  exports.getCodeSnippet = getCodeSnippet;
@@ -1204,4 +1254,5 @@ exports.now = now;
1204
1254
  exports.readLinesFromFile = readLinesFromFile;
1205
1255
  exports.redactUrlQuery = redactUrlQuery;
1206
1256
  exports.resolveDenylist = resolveDenylist;
1257
+ exports.routeRejection = routeRejection;
1207
1258
  exports.userIdentityAttributes = userIdentityAttributes;
package/dist/index.d.cts CHANGED
@@ -185,6 +185,34 @@ declare const DEFAULT_URL_DENYLIST: RegExp;
185
185
  declare function resolveDenylist(custom?: RegExp, replaceDefault?: boolean, defaultDenylist?: RegExp): RegExp;
186
186
  declare function redactUrlQuery(fullPath: string, denylist?: RegExp): string;
187
187
  //#endregion
188
+ //#region src/util/rejection.d.ts
189
+ /**
190
+ * Shared unhandled-rejection routing. A rejection "reason" can be anything a
191
+ * promise was rejected with: an Error, an Error-like object carrying a `.stack`,
192
+ * a string, or a plain object. The browser `unhandledrejection` listener
193
+ * (`@flareapp/js`) and the React Native engine rejection tracker
194
+ * (`@flareapp/react-native`) need the SAME routing so a report looks identical
195
+ * across SDKs, so it lives here instead of being copy-pasted (and drifting) per
196
+ * client.
197
+ */
198
+ type RejectionReporter = {
199
+ reportSilently: (error: Error) => void;
200
+ reportUnhandledRejection: (message: string) => unknown;
201
+ };
202
+ /** Best-effort human-readable description of an arbitrary rejection reason. */
203
+ declare function describeRejectionReason(reason: unknown): string;
204
+ /**
205
+ * Route a rejection reason to the reporter: an Error (or any stack-bearing
206
+ * object) goes to `reportSilently` so the STACK survives; only a stackless
207
+ * reason falls back to `reportUnhandledRejection` (string message, empty-stack
208
+ * `UnhandledRejection` class). Any rejection from `reportUnhandledRejection`'s
209
+ * returned promise is swallowed so a transport failure cannot itself surface as
210
+ * an unhandled rejection. `reportSilently` is assumed not to throw synchronously
211
+ * (core's does its work asynchronously); it is intentionally not wrapped, so a
212
+ * synchronous throw there would propagate.
213
+ */
214
+ declare function routeRejection(reporter: RejectionReporter, reason: unknown): void;
215
+ //#endregion
188
216
  //#region src/api/Api.d.ts
189
217
  declare class Api {
190
218
  report(report: Report, url: string, key: string | null, reportBrowserExtensionErrors: boolean, debug?: boolean): Promise<void>;
@@ -580,4 +608,4 @@ declare class NullFileReader implements FileReader {
580
608
  //#region src/stacktrace/createStackTrace.d.ts
581
609
  declare function createStackTrace(error: Error, debug: boolean, fileReader: FileReader): Promise<Array<StackFrame>>;
582
610
  //#endregion
583
- export { type AnyValue, Api, type AttributeValue, type Attributes, type BufferedLog, type Config, type ContextCollector, DEFAULT_URL_DENYLIST, type EntryPointHandler, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, GlobalScopeProvider, type Glow, type KeyValue, Logger, type LoggerDeps, type LogsEnvelope, type MessageLevel, NoopFlushScheduler, NullFileReader, type OtelLogRecord, type OverriddenGrouping, type Report, Scope, type ScopeProvider, type SdkInfo, type SpanEvent, type StackFrame, USER_IDENTITY_KEYS, type User, assert, assertKey, convertToError, createStackTrace, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist, userIdentityAttributes };
611
+ export { type AnyValue, Api, type AttributeValue, type Attributes, type BufferedLog, type Config, type ContextCollector, DEFAULT_URL_DENYLIST, type EntryPointHandler, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, GlobalScopeProvider, type Glow, type KeyValue, Logger, type LoggerDeps, type LogsEnvelope, type MessageLevel, NoopFlushScheduler, NullFileReader, type OtelLogRecord, type OverriddenGrouping, type RejectionReporter, type Report, Scope, type ScopeProvider, type SdkInfo, type SpanEvent, type StackFrame, USER_IDENTITY_KEYS, type User, assert, assertKey, convertToError, createStackTrace, describeRejectionReason, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist, routeRejection, userIdentityAttributes };
package/dist/index.d.mts CHANGED
@@ -185,6 +185,34 @@ declare const DEFAULT_URL_DENYLIST: RegExp;
185
185
  declare function resolveDenylist(custom?: RegExp, replaceDefault?: boolean, defaultDenylist?: RegExp): RegExp;
186
186
  declare function redactUrlQuery(fullPath: string, denylist?: RegExp): string;
187
187
  //#endregion
188
+ //#region src/util/rejection.d.ts
189
+ /**
190
+ * Shared unhandled-rejection routing. A rejection "reason" can be anything a
191
+ * promise was rejected with: an Error, an Error-like object carrying a `.stack`,
192
+ * a string, or a plain object. The browser `unhandledrejection` listener
193
+ * (`@flareapp/js`) and the React Native engine rejection tracker
194
+ * (`@flareapp/react-native`) need the SAME routing so a report looks identical
195
+ * across SDKs, so it lives here instead of being copy-pasted (and drifting) per
196
+ * client.
197
+ */
198
+ type RejectionReporter = {
199
+ reportSilently: (error: Error) => void;
200
+ reportUnhandledRejection: (message: string) => unknown;
201
+ };
202
+ /** Best-effort human-readable description of an arbitrary rejection reason. */
203
+ declare function describeRejectionReason(reason: unknown): string;
204
+ /**
205
+ * Route a rejection reason to the reporter: an Error (or any stack-bearing
206
+ * object) goes to `reportSilently` so the STACK survives; only a stackless
207
+ * reason falls back to `reportUnhandledRejection` (string message, empty-stack
208
+ * `UnhandledRejection` class). Any rejection from `reportUnhandledRejection`'s
209
+ * returned promise is swallowed so a transport failure cannot itself surface as
210
+ * an unhandled rejection. `reportSilently` is assumed not to throw synchronously
211
+ * (core's does its work asynchronously); it is intentionally not wrapped, so a
212
+ * synchronous throw there would propagate.
213
+ */
214
+ declare function routeRejection(reporter: RejectionReporter, reason: unknown): void;
215
+ //#endregion
188
216
  //#region src/api/Api.d.ts
189
217
  declare class Api {
190
218
  report(report: Report, url: string, key: string | null, reportBrowserExtensionErrors: boolean, debug?: boolean): Promise<void>;
@@ -580,4 +608,4 @@ declare class NullFileReader implements FileReader {
580
608
  //#region src/stacktrace/createStackTrace.d.ts
581
609
  declare function createStackTrace(error: Error, debug: boolean, fileReader: FileReader): Promise<Array<StackFrame>>;
582
610
  //#endregion
583
- export { type AnyValue, Api, type AttributeValue, type Attributes, type BufferedLog, type Config, type ContextCollector, DEFAULT_URL_DENYLIST, type EntryPointHandler, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, GlobalScopeProvider, type Glow, type KeyValue, Logger, type LoggerDeps, type LogsEnvelope, type MessageLevel, NoopFlushScheduler, NullFileReader, type OtelLogRecord, type OverriddenGrouping, type Report, Scope, type ScopeProvider, type SdkInfo, type SpanEvent, type StackFrame, USER_IDENTITY_KEYS, type User, assert, assertKey, convertToError, createStackTrace, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist, userIdentityAttributes };
611
+ export { type AnyValue, Api, type AttributeValue, type Attributes, type BufferedLog, type Config, type ContextCollector, DEFAULT_URL_DENYLIST, type EntryPointHandler, type FileReader, Flare, type FlushFn, type FlushScheduler, type Framework, GlobalScopeProvider, type Glow, type KeyValue, Logger, type LoggerDeps, type LogsEnvelope, type MessageLevel, NoopFlushScheduler, NullFileReader, type OtelLogRecord, type OverriddenGrouping, type RejectionReporter, type Report, Scope, type ScopeProvider, type SdkInfo, type SpanEvent, type StackFrame, USER_IDENTITY_KEYS, type User, assert, assertKey, convertToError, createStackTrace, describeRejectionReason, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist, routeRejection, userIdentityAttributes };
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  import ErrorStackParser from "error-stack-parser";
2
2
 
3
3
  //#region src/env/index.ts
4
- const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.5.1" : "?";
4
+ const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.6.0" : "?";
5
5
  const KEY = typeof FLARE_JS_KEY === "undefined" ? "" : FLARE_JS_KEY;
6
6
  const SOURCEMAP_VERSION = typeof FLARE_SOURCEMAP_VERSION === "undefined" ? "" : FLARE_SOURCEMAP_VERSION;
7
7
 
@@ -142,6 +142,49 @@ function safeDecode(value) {
142
142
  }
143
143
  }
144
144
 
145
+ //#endregion
146
+ //#region src/util/rejection.ts
147
+ /** Best-effort human-readable description of an arbitrary rejection reason. */
148
+ function describeRejectionReason(reason) {
149
+ if (typeof reason === "string") return reason;
150
+ if (reason && typeof reason === "object") {
151
+ const message = reason.message;
152
+ if (typeof message === "string" && message) return message;
153
+ try {
154
+ return JSON.stringify(reason);
155
+ } catch {
156
+ return "Unhandled promise rejection (non-serializable reason)";
157
+ }
158
+ }
159
+ return String(reason);
160
+ }
161
+ function hasStack$1(reason) {
162
+ return !!reason && typeof reason === "object" && typeof reason.stack === "string";
163
+ }
164
+ /**
165
+ * Route a rejection reason to the reporter: an Error (or any stack-bearing
166
+ * object) goes to `reportSilently` so the STACK survives; only a stackless
167
+ * reason falls back to `reportUnhandledRejection` (string message, empty-stack
168
+ * `UnhandledRejection` class). Any rejection from `reportUnhandledRejection`'s
169
+ * returned promise is swallowed so a transport failure cannot itself surface as
170
+ * an unhandled rejection. `reportSilently` is assumed not to throw synchronously
171
+ * (core's does its work asynchronously); it is intentionally not wrapped, so a
172
+ * synchronous throw there would propagate.
173
+ */
174
+ function routeRejection(reporter, reason) {
175
+ if (reason instanceof Error) {
176
+ reporter.reportSilently(reason);
177
+ return;
178
+ }
179
+ if (hasStack$1(reason)) {
180
+ const error = new Error(describeRejectionReason(reason));
181
+ error.stack = reason.stack;
182
+ reporter.reportSilently(error);
183
+ return;
184
+ }
185
+ Promise.resolve(reporter.reportUnhandledRejection(describeRejectionReason(reason))).catch(() => {});
186
+ }
187
+
145
188
  //#endregion
146
189
  //#region src/api/Api.ts
147
190
  var Api = class {
@@ -645,18 +688,24 @@ function createStackTrace(error, debug, fileReader) {
645
688
  return resolve([fallbackFrame("stacktrace could not be parsed")]);
646
689
  }
647
690
  Promise.all(parsedFrames.map((frame) => {
648
- return getCodeSnippet(fileReader, frame.fileName, frame.lineNumber, frame.columnNumber).then((snippet) => ({
691
+ const fileName = normalizeFileName(frame.fileName);
692
+ return getCodeSnippet(fileReader, fileName, frame.lineNumber, frame.columnNumber).then((snippet) => ({
649
693
  lineNumber: frame.lineNumber || 1,
650
694
  columnNumber: frame.columnNumber || 1,
651
695
  method: frame.functionName || "Anonymous or unknown function",
652
- file: frame.fileName || "Unknown file",
696
+ file: fileName || "Unknown file",
653
697
  codeSnippet: snippet.codeSnippet,
654
698
  class: "",
655
- isApplicationFrame: isApplicationFrame(frame.fileName)
699
+ isApplicationFrame: isApplicationFrame(fileName)
656
700
  }));
657
701
  })).then(resolve);
658
702
  });
659
703
  }
704
+ const HERMES_ADDRESS_PREFIX = "address at ";
705
+ function normalizeFileName(fileName) {
706
+ if (fileName?.startsWith(HERMES_ADDRESS_PREFIX)) return fileName.slice(11);
707
+ return fileName;
708
+ }
660
709
  function fallbackFrame(reason) {
661
710
  return {
662
711
  lineNumber: 0,
@@ -1154,4 +1203,4 @@ var Flare = class {
1154
1203
  };
1155
1204
 
1156
1205
  //#endregion
1157
- export { Api, DEFAULT_URL_DENYLIST, Flare, GlobalScopeProvider, Logger, NoopFlushScheduler, NullFileReader, Scope, USER_IDENTITY_KEYS, assert, assertKey, convertToError, createStackTrace, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist, userIdentityAttributes };
1206
+ export { Api, DEFAULT_URL_DENYLIST, Flare, GlobalScopeProvider, Logger, NoopFlushScheduler, NullFileReader, Scope, USER_IDENTITY_KEYS, assert, assertKey, convertToError, createStackTrace, describeRejectionReason, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist, routeRejection, userIdentityAttributes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flareapp/core",
3
- "version": "2.5.1",
3
+ "version": "2.6.0",
4
4
  "description": "Environment-agnostic core for the Flare JS SDK",
5
5
  "homepage": "https://flareapp.io",
6
6
  "bugs": {