@flareapp/core 2.5.0 → 2.5.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 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.0" : "?";
33
+ const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.5.1" : "?";
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
 
@@ -500,6 +500,39 @@ function partitionAttributes(attributes) {
500
500
  //#endregion
501
501
  //#region src/Scope.ts
502
502
  /**
503
+ * Maps each `User` identity field to the flat report attribute key it projects to.
504
+ * `Flare.setUser`'s set pass writes through these so the literal key strings live in
505
+ * exactly one place; `USER_IDENTITY_KEYS` (the clear pass) derives from them, so adding
506
+ * a field here can never silently leave the clear pass out of date.
507
+ */
508
+ const USER_FIELD_KEYS = {
509
+ id: "user.id",
510
+ email: "user.email",
511
+ fullName: "user.full_name",
512
+ ipAddress: "client.address"
513
+ };
514
+ /**
515
+ * The report attribute keys that `Flare.setUser` owns: the four projected identity
516
+ * fields plus the `user.attributes` bag for extras. Single source of truth so the
517
+ * clear pass and the set pass in `setUser` cannot drift, and so consumers that must
518
+ * stamp identity outside core's report pipeline (Electron's forwarded-renderer path)
519
+ * pick up the exact same set instead of re-hardcoding it.
520
+ */
521
+ const USER_IDENTITY_KEYS = [...Object.values(USER_FIELD_KEYS), "user.attributes"];
522
+ /**
523
+ * Pick the user-identity attributes currently set on a scope. Used where identity must
524
+ * be copied onto a report that does not flow through `Flare.report()` (which would
525
+ * otherwise spread `pendingAttributes` automatically).
526
+ */
527
+ function userIdentityAttributes(scope) {
528
+ const attrs = {};
529
+ for (const key of USER_IDENTITY_KEYS) {
530
+ const value = scope.pendingAttributes[key];
531
+ if (value !== void 0) attrs[key] = value;
532
+ }
533
+ return attrs;
534
+ }
535
+ /**
503
536
  * Holds the per-call mutable state that used to live on the `Flare` instance:
504
537
  * breadcrumbs (`glows`), custom attributes (`pendingAttributes`), and the
505
538
  * current entry-point handler.
@@ -516,9 +549,9 @@ function partitionAttributes(attributes) {
516
549
  * holding the state directly, so the per-request behavior comes from the
517
550
  * provider, not from the class itself.
518
551
  *
519
- * `NodeScope` (in `@flareapp/node`) extends this with two more buckets:
520
- * `request` (HTTP method, path, headers) and `user` (id, email, ...). Browser
521
- * does not need those.
552
+ * `NodeScope` (in `@flareapp/node`) extends this with a `request` bucket
553
+ * (HTTP method, path, headers). User identity is written to `pendingAttributes`
554
+ * by `Flare.setUser`, so it needs no dedicated field. Browser does not need `request`.
522
555
  */
523
556
  var Scope = class {
524
557
  glows = [];
@@ -975,6 +1008,26 @@ var Flare = class {
975
1008
  this.scopeProvider.active().setAttribute(`context.${groupName}`, value);
976
1009
  return this;
977
1010
  }
1011
+ /**
1012
+ * Attach an identified user to the active scope. Fields are projected to the
1013
+ * keys the Flare backend reads: `user.id`, `user.email`, `user.full_name`,
1014
+ * and `client.address`. Any extra keys are bundled into `user.attributes`.
1015
+ * Pass `null` to clear the user. Scope-aware: in Node this targets the
1016
+ * per-request scope via the scope provider.
1017
+ */
1018
+ setUser(user) {
1019
+ const scope = this.scopeProvider.active();
1020
+ for (const key of USER_IDENTITY_KEYS) delete scope.pendingAttributes[key];
1021
+ if (!user) return this;
1022
+ const { id, email, fullName, ipAddress, ...rest } = user;
1023
+ if (id !== void 0 && id !== null) scope.setAttribute(USER_FIELD_KEYS.id, String(id));
1024
+ if (email !== void 0) scope.setAttribute(USER_FIELD_KEYS.email, email);
1025
+ if (fullName !== void 0) scope.setAttribute(USER_FIELD_KEYS.fullName, fullName);
1026
+ if (ipAddress !== void 0) scope.setAttribute(USER_FIELD_KEYS.ipAddress, ipAddress);
1027
+ const extras = Object.fromEntries(Object.entries(rest).filter(([, value]) => value !== void 0));
1028
+ if (Object.keys(extras).length > 0) scope.setAttribute("user.attributes", extras);
1029
+ return this;
1030
+ }
978
1031
  setEntryPoint(handler) {
979
1032
  this.scopeProvider.active().entryPoint = handler;
980
1033
  return this;
@@ -1138,6 +1191,7 @@ exports.Logger = Logger;
1138
1191
  exports.NoopFlushScheduler = NoopFlushScheduler;
1139
1192
  exports.NullFileReader = NullFileReader;
1140
1193
  exports.Scope = Scope;
1194
+ exports.USER_IDENTITY_KEYS = USER_IDENTITY_KEYS;
1141
1195
  exports.assert = assert;
1142
1196
  exports.assertKey = assertKey;
1143
1197
  exports.convertToError = convertToError;
@@ -1149,4 +1203,5 @@ exports.glowsToEvents = glowsToEvents;
1149
1203
  exports.now = now;
1150
1204
  exports.readLinesFromFile = readLinesFromFile;
1151
1205
  exports.redactUrlQuery = redactUrlQuery;
1152
- exports.resolveDenylist = resolveDenylist;
1206
+ exports.resolveDenylist = resolveDenylist;
1207
+ exports.userIdentityAttributes = userIdentityAttributes;
package/dist/index.d.cts CHANGED
@@ -4,6 +4,23 @@ type AttributeValue = string | number | boolean | null | AttributeValue[] | {
4
4
  [key: string]: AttributeValue;
5
5
  };
6
6
  type Attributes = Record<string, AttributeValue>;
7
+ /**
8
+ * An identified user passed to `Flare.setUser`. The four known fields project to the
9
+ * report keys the Flare backend reads: `id`→`user.id`, `email`→`user.email`,
10
+ * `fullName`→`user.full_name`, `ipAddress`→`client.address`. Any OTHER key is bundled
11
+ * into `user.attributes`.
12
+ *
13
+ * Caveat: the open index signature means a misspelled known field (e.g. `fullname` or
14
+ * `full_name` instead of `fullName`) does NOT raise a type error — it silently lands in
15
+ * `user.attributes` rather than the identity key. Spell the four known fields exactly.
16
+ */
17
+ type User = {
18
+ id?: string | number;
19
+ email?: string;
20
+ fullName?: string;
21
+ ipAddress?: string;
22
+ [key: string]: AttributeValue | undefined;
23
+ };
7
24
  type Config = {
8
25
  key: string | null;
9
26
  version: string;
@@ -236,6 +253,20 @@ declare class Logger {
236
253
  }
237
254
  //#endregion
238
255
  //#region src/Scope.d.ts
256
+ /**
257
+ * The report attribute keys that `Flare.setUser` owns: the four projected identity
258
+ * fields plus the `user.attributes` bag for extras. Single source of truth so the
259
+ * clear pass and the set pass in `setUser` cannot drift, and so consumers that must
260
+ * stamp identity outside core's report pipeline (Electron's forwarded-renderer path)
261
+ * pick up the exact same set instead of re-hardcoding it.
262
+ */
263
+ declare const USER_IDENTITY_KEYS: readonly [...("user.id" | "user.email" | "user.full_name" | "client.address")[], "user.attributes"];
264
+ /**
265
+ * Pick the user-identity attributes currently set on a scope. Used where identity must
266
+ * be copied onto a report that does not flow through `Flare.report()` (which would
267
+ * otherwise spread `pendingAttributes` automatically).
268
+ */
269
+ declare function userIdentityAttributes(scope: Scope): Attributes;
239
270
  /**
240
271
  * Holds the per-call mutable state that used to live on the `Flare` instance:
241
272
  * breadcrumbs (`glows`), custom attributes (`pendingAttributes`), and the
@@ -253,9 +284,9 @@ declare class Logger {
253
284
  * holding the state directly, so the per-request behavior comes from the
254
285
  * provider, not from the class itself.
255
286
  *
256
- * `NodeScope` (in `@flareapp/node`) extends this with two more buckets:
257
- * `request` (HTTP method, path, headers) and `user` (id, email, ...). Browser
258
- * does not need those.
287
+ * `NodeScope` (in `@flareapp/node`) extends this with a `request` bucket
288
+ * (HTTP method, path, headers). User identity is written to `pendingAttributes`
289
+ * by `Flare.setUser`, so it needs no dedicated field. Browser does not need `request`.
259
290
  */
260
291
  declare class Scope {
261
292
  glows: Glow[];
@@ -494,6 +525,14 @@ declare class Flare {
494
525
  clearGlows(): this;
495
526
  addContext(name: string, value: AttributeValue): this;
496
527
  addContextGroup(groupName: string, value: Record<string, AttributeValue>): this;
528
+ /**
529
+ * Attach an identified user to the active scope. Fields are projected to the
530
+ * keys the Flare backend reads: `user.id`, `user.email`, `user.full_name`,
531
+ * and `client.address`. Any extra keys are bundled into `user.attributes`.
532
+ * Pass `null` to clear the user. Scope-aware: in Node this targets the
533
+ * per-request scope via the scope provider.
534
+ */
535
+ setUser(user: User | null): this;
497
536
  setEntryPoint(handler: EntryPointHandler): this;
498
537
  setSdkInfo(info: SdkInfo): this;
499
538
  setFramework(framework: Framework): this;
@@ -541,4 +580,4 @@ declare class NullFileReader implements FileReader {
541
580
  //#region src/stacktrace/createStackTrace.d.ts
542
581
  declare function createStackTrace(error: Error, debug: boolean, fileReader: FileReader): Promise<Array<StackFrame>>;
543
582
  //#endregion
544
- 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, assert, assertKey, convertToError, createStackTrace, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist };
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 };
package/dist/index.d.mts CHANGED
@@ -4,6 +4,23 @@ type AttributeValue = string | number | boolean | null | AttributeValue[] | {
4
4
  [key: string]: AttributeValue;
5
5
  };
6
6
  type Attributes = Record<string, AttributeValue>;
7
+ /**
8
+ * An identified user passed to `Flare.setUser`. The four known fields project to the
9
+ * report keys the Flare backend reads: `id`→`user.id`, `email`→`user.email`,
10
+ * `fullName`→`user.full_name`, `ipAddress`→`client.address`. Any OTHER key is bundled
11
+ * into `user.attributes`.
12
+ *
13
+ * Caveat: the open index signature means a misspelled known field (e.g. `fullname` or
14
+ * `full_name` instead of `fullName`) does NOT raise a type error — it silently lands in
15
+ * `user.attributes` rather than the identity key. Spell the four known fields exactly.
16
+ */
17
+ type User = {
18
+ id?: string | number;
19
+ email?: string;
20
+ fullName?: string;
21
+ ipAddress?: string;
22
+ [key: string]: AttributeValue | undefined;
23
+ };
7
24
  type Config = {
8
25
  key: string | null;
9
26
  version: string;
@@ -236,6 +253,20 @@ declare class Logger {
236
253
  }
237
254
  //#endregion
238
255
  //#region src/Scope.d.ts
256
+ /**
257
+ * The report attribute keys that `Flare.setUser` owns: the four projected identity
258
+ * fields plus the `user.attributes` bag for extras. Single source of truth so the
259
+ * clear pass and the set pass in `setUser` cannot drift, and so consumers that must
260
+ * stamp identity outside core's report pipeline (Electron's forwarded-renderer path)
261
+ * pick up the exact same set instead of re-hardcoding it.
262
+ */
263
+ declare const USER_IDENTITY_KEYS: readonly [...("user.id" | "user.email" | "user.full_name" | "client.address")[], "user.attributes"];
264
+ /**
265
+ * Pick the user-identity attributes currently set on a scope. Used where identity must
266
+ * be copied onto a report that does not flow through `Flare.report()` (which would
267
+ * otherwise spread `pendingAttributes` automatically).
268
+ */
269
+ declare function userIdentityAttributes(scope: Scope): Attributes;
239
270
  /**
240
271
  * Holds the per-call mutable state that used to live on the `Flare` instance:
241
272
  * breadcrumbs (`glows`), custom attributes (`pendingAttributes`), and the
@@ -253,9 +284,9 @@ declare class Logger {
253
284
  * holding the state directly, so the per-request behavior comes from the
254
285
  * provider, not from the class itself.
255
286
  *
256
- * `NodeScope` (in `@flareapp/node`) extends this with two more buckets:
257
- * `request` (HTTP method, path, headers) and `user` (id, email, ...). Browser
258
- * does not need those.
287
+ * `NodeScope` (in `@flareapp/node`) extends this with a `request` bucket
288
+ * (HTTP method, path, headers). User identity is written to `pendingAttributes`
289
+ * by `Flare.setUser`, so it needs no dedicated field. Browser does not need `request`.
259
290
  */
260
291
  declare class Scope {
261
292
  glows: Glow[];
@@ -494,6 +525,14 @@ declare class Flare {
494
525
  clearGlows(): this;
495
526
  addContext(name: string, value: AttributeValue): this;
496
527
  addContextGroup(groupName: string, value: Record<string, AttributeValue>): this;
528
+ /**
529
+ * Attach an identified user to the active scope. Fields are projected to the
530
+ * keys the Flare backend reads: `user.id`, `user.email`, `user.full_name`,
531
+ * and `client.address`. Any extra keys are bundled into `user.attributes`.
532
+ * Pass `null` to clear the user. Scope-aware: in Node this targets the
533
+ * per-request scope via the scope provider.
534
+ */
535
+ setUser(user: User | null): this;
497
536
  setEntryPoint(handler: EntryPointHandler): this;
498
537
  setSdkInfo(info: SdkInfo): this;
499
538
  setFramework(framework: Framework): this;
@@ -541,4 +580,4 @@ declare class NullFileReader implements FileReader {
541
580
  //#region src/stacktrace/createStackTrace.d.ts
542
581
  declare function createStackTrace(error: Error, debug: boolean, fileReader: FileReader): Promise<Array<StackFrame>>;
543
582
  //#endregion
544
- 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, assert, assertKey, convertToError, createStackTrace, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist };
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 };
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.0" : "?";
4
+ const CLIENT_VERSION = typeof process !== "undefined" && true ? "2.5.1" : "?";
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
 
@@ -471,6 +471,39 @@ function partitionAttributes(attributes) {
471
471
  //#endregion
472
472
  //#region src/Scope.ts
473
473
  /**
474
+ * Maps each `User` identity field to the flat report attribute key it projects to.
475
+ * `Flare.setUser`'s set pass writes through these so the literal key strings live in
476
+ * exactly one place; `USER_IDENTITY_KEYS` (the clear pass) derives from them, so adding
477
+ * a field here can never silently leave the clear pass out of date.
478
+ */
479
+ const USER_FIELD_KEYS = {
480
+ id: "user.id",
481
+ email: "user.email",
482
+ fullName: "user.full_name",
483
+ ipAddress: "client.address"
484
+ };
485
+ /**
486
+ * The report attribute keys that `Flare.setUser` owns: the four projected identity
487
+ * fields plus the `user.attributes` bag for extras. Single source of truth so the
488
+ * clear pass and the set pass in `setUser` cannot drift, and so consumers that must
489
+ * stamp identity outside core's report pipeline (Electron's forwarded-renderer path)
490
+ * pick up the exact same set instead of re-hardcoding it.
491
+ */
492
+ const USER_IDENTITY_KEYS = [...Object.values(USER_FIELD_KEYS), "user.attributes"];
493
+ /**
494
+ * Pick the user-identity attributes currently set on a scope. Used where identity must
495
+ * be copied onto a report that does not flow through `Flare.report()` (which would
496
+ * otherwise spread `pendingAttributes` automatically).
497
+ */
498
+ function userIdentityAttributes(scope) {
499
+ const attrs = {};
500
+ for (const key of USER_IDENTITY_KEYS) {
501
+ const value = scope.pendingAttributes[key];
502
+ if (value !== void 0) attrs[key] = value;
503
+ }
504
+ return attrs;
505
+ }
506
+ /**
474
507
  * Holds the per-call mutable state that used to live on the `Flare` instance:
475
508
  * breadcrumbs (`glows`), custom attributes (`pendingAttributes`), and the
476
509
  * current entry-point handler.
@@ -487,9 +520,9 @@ function partitionAttributes(attributes) {
487
520
  * holding the state directly, so the per-request behavior comes from the
488
521
  * provider, not from the class itself.
489
522
  *
490
- * `NodeScope` (in `@flareapp/node`) extends this with two more buckets:
491
- * `request` (HTTP method, path, headers) and `user` (id, email, ...). Browser
492
- * does not need those.
523
+ * `NodeScope` (in `@flareapp/node`) extends this with a `request` bucket
524
+ * (HTTP method, path, headers). User identity is written to `pendingAttributes`
525
+ * by `Flare.setUser`, so it needs no dedicated field. Browser does not need `request`.
493
526
  */
494
527
  var Scope = class {
495
528
  glows = [];
@@ -946,6 +979,26 @@ var Flare = class {
946
979
  this.scopeProvider.active().setAttribute(`context.${groupName}`, value);
947
980
  return this;
948
981
  }
982
+ /**
983
+ * Attach an identified user to the active scope. Fields are projected to the
984
+ * keys the Flare backend reads: `user.id`, `user.email`, `user.full_name`,
985
+ * and `client.address`. Any extra keys are bundled into `user.attributes`.
986
+ * Pass `null` to clear the user. Scope-aware: in Node this targets the
987
+ * per-request scope via the scope provider.
988
+ */
989
+ setUser(user) {
990
+ const scope = this.scopeProvider.active();
991
+ for (const key of USER_IDENTITY_KEYS) delete scope.pendingAttributes[key];
992
+ if (!user) return this;
993
+ const { id, email, fullName, ipAddress, ...rest } = user;
994
+ if (id !== void 0 && id !== null) scope.setAttribute(USER_FIELD_KEYS.id, String(id));
995
+ if (email !== void 0) scope.setAttribute(USER_FIELD_KEYS.email, email);
996
+ if (fullName !== void 0) scope.setAttribute(USER_FIELD_KEYS.fullName, fullName);
997
+ if (ipAddress !== void 0) scope.setAttribute(USER_FIELD_KEYS.ipAddress, ipAddress);
998
+ const extras = Object.fromEntries(Object.entries(rest).filter(([, value]) => value !== void 0));
999
+ if (Object.keys(extras).length > 0) scope.setAttribute("user.attributes", extras);
1000
+ return this;
1001
+ }
949
1002
  setEntryPoint(handler) {
950
1003
  this.scopeProvider.active().entryPoint = handler;
951
1004
  return this;
@@ -1101,4 +1154,4 @@ var Flare = class {
1101
1154
  };
1102
1155
 
1103
1156
  //#endregion
1104
- export { Api, DEFAULT_URL_DENYLIST, Flare, GlobalScopeProvider, Logger, NoopFlushScheduler, NullFileReader, Scope, assert, assertKey, convertToError, createStackTrace, extractCode, flatJsonStringify, getCodeSnippet, glowsToEvents, now, readLinesFromFile, redactUrlQuery, resolveDenylist };
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 };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flareapp/core",
3
- "version": "2.5.0",
3
+ "version": "2.5.1",
4
4
  "description": "Environment-agnostic core for the Flare JS SDK",
5
5
  "homepage": "https://flareapp.io",
6
6
  "bugs": {