11/* eslint-disable max-lines */
2+ import { Attributes , attributeValueToTypedAttributeValue , AttributeValueType , TypedAttributeValue } from './attributes' ;
23import type { Client } from './client' ;
34import { DEBUG_BUILD } from './debug-build' ;
45import { updateSession } from './session' ;
@@ -46,6 +47,7 @@ export interface ScopeContext {
4647 extra : Extras ;
4748 contexts : Contexts ;
4849 tags : { [ key : string ] : Primitive } ;
50+ attributes ?: Attributes ;
4951 fingerprint : string [ ] ;
5052 propagationContext : PropagationContext ;
5153}
@@ -71,6 +73,8 @@ export interface ScopeData {
7173 breadcrumbs : Breadcrumb [ ] ;
7274 user : User ;
7375 tags : { [ key : string ] : Primitive } ;
76+ // TODO(v11): Make this a required field (could be subtly breaking if we did it today)
77+ attributes ?: Attributes ;
7478 extra : Extras ;
7579 contexts : Contexts ;
7680 attachments : Attachment [ ] ;
@@ -104,6 +108,9 @@ export class Scope {
104108 /** Tags */
105109 protected _tags : { [ key : string ] : Primitive } ;
106110
111+ /** Attributes */
112+ protected _attributes : Attributes ;
113+
107114 /** Extra */
108115 protected _extra : Extras ;
109116
@@ -155,6 +162,7 @@ export class Scope {
155162 this . _attachments = [ ] ;
156163 this . _user = { } ;
157164 this . _tags = { } ;
165+ this . _attributes = { } ;
158166 this . _extra = { } ;
159167 this . _contexts = { } ;
160168 this . _sdkProcessingMetadata = { } ;
@@ -171,6 +179,7 @@ export class Scope {
171179 const newScope = new Scope ( ) ;
172180 newScope . _breadcrumbs = [ ...this . _breadcrumbs ] ;
173181 newScope . _tags = { ...this . _tags } ;
182+ newScope . _attributes = { ...this . _attributes } ;
174183 newScope . _extra = { ...this . _extra } ;
175184 newScope . _contexts = { ...this . _contexts } ;
176185 if ( this . _contexts . flags ) {
@@ -296,6 +305,59 @@ export class Scope {
296305 return this ;
297306 }
298307
308+ /**
309+ * Sets attributes onto the scope.
310+ *
311+ * TODO:
312+ * Currently, these attributes are not applied to any telemetry data but they will be in the future.
313+ *
314+ * @param newAttributes - The attributes to set on the scope. You can either pass in key-value pairs, or
315+ * an object with a concrete type declaration and an optional unit (if applicable to your attribute).
316+ * You can only pass in primitive values or arrays of primitive values.
317+ *
318+ * @example
319+ * ```typescript
320+ * scope.setAttributes({
321+ * is_admin: true,
322+ * payment_selection: 'credit_card',
323+ * clicked_products: [130, 554, 292],
324+ * render_duration: { value: 'render_duration', type: 'float', unit: 'ms' },
325+ * });
326+ * ```
327+ */
328+ public setAttributes ( newAttributes : Record < string , AttributeValueType | TypedAttributeValue > ) : this {
329+ Object . entries ( newAttributes ) . forEach ( ( [ key , value ] ) => {
330+ if ( typeof value === 'object' && ! Array . isArray ( value ) ) {
331+ this . _attributes [ key ] = value ;
332+ } else {
333+ this . _attributes [ key ] = attributeValueToTypedAttributeValue ( value ) ;
334+ }
335+ } ) ;
336+ this . _notifyScopeListeners ( ) ;
337+ return this ;
338+ }
339+
340+ /**
341+ * Sets an attribute onto the scope.
342+ *
343+ * TODO:
344+ * Currently, these attributes are not applied to any telemetry data but they will be in the future.
345+ *
346+ * @param key - The attribute key.
347+ * @param value - the attribute value. You can either pass in a raw value (primitive or array of primitives), or
348+ * a typed attribute value object with a concrete type declaration and an optional unit (if applicable to your attribute).
349+ *
350+ * @example
351+ * ```typescript
352+ * scope.setAttribute('is_admin', true);
353+ * scope.setAttribute('clicked_products', [130, 554, 292]);
354+ * scope.setAttribute('render_duration', { value: 'render_duration', type: 'float', unit: 'ms' });
355+ * ```
356+ */
357+ public setAttribute ( key : string , value : AttributeValueType | TypedAttributeValue ) : this {
358+ return this . setAttributes ( { [ key ] : value } ) ;
359+ }
360+
299361 /**
300362 * Set an object that will be merged into existing extra on the scope,
301363 * and will be sent as extra data with the event.
@@ -411,9 +473,19 @@ export class Scope {
411473 ? ( captureContext as ScopeContext )
412474 : undefined ;
413475
414- const { tags, extra, user, contexts, level, fingerprint = [ ] , propagationContext } = scopeInstance || { } ;
476+ const {
477+ tags,
478+ attributes,
479+ extra,
480+ user,
481+ contexts,
482+ level,
483+ fingerprint = [ ] ,
484+ propagationContext,
485+ } = scopeInstance || { } ;
415486
416487 this . _tags = { ...this . _tags , ...tags } ;
488+ this . _attributes = { ...this . _attributes , ...attributes } ;
417489 this . _extra = { ...this . _extra , ...extra } ;
418490 this . _contexts = { ...this . _contexts , ...contexts } ;
419491
@@ -444,6 +516,7 @@ export class Scope {
444516 // client is not cleared here on purpose!
445517 this . _breadcrumbs = [ ] ;
446518 this . _tags = { } ;
519+ this . _attributes = { } ;
447520 this . _extra = { } ;
448521 this . _user = { } ;
449522 this . _contexts = { } ;
@@ -530,6 +603,7 @@ export class Scope {
530603 attachments : this . _attachments ,
531604 contexts : this . _contexts ,
532605 tags : this . _tags ,
606+ attributes : this . _attributes ,
533607 extra : this . _extra ,
534608 user : this . _user ,
535609 level : this . _level ,
0 commit comments