@@ -51,73 +51,102 @@ export type ValidatedAttributes<T> = {
5151 * Type-guard: The attribute object has the shape the official attribute object (value, type, unit).
5252 * https://develop.sentry.dev/sdk/telemetry/scopes/#setting-attributes
5353 */
54- export function isAttributeObject ( value : unknown ) : value is AttributeWithUnit {
55- if ( typeof value !== 'object' || value == null || Array . isArray ( value ) ) {
54+ export function isAttributeObject ( maybeObj : unknown ) : maybeObj is AttributeWithUnit {
55+ if ( typeof maybeObj !== 'object' || maybeObj == null || Array . isArray ( maybeObj ) ) {
5656 return false ;
5757 }
5858
59- // MUST have 'value' and 'unit' property
60- return Object . prototype . hasOwnProperty . call ( value , 'value' ) && Object . prototype . hasOwnProperty . call ( value , 'unit' ) ;
59+ // MUST have 'value' property
60+ // MAY have 'unit' property
61+ // MUST NOT have other properties
62+ const keys = Object . keys ( maybeObj ) ;
63+
64+ // MUST have 'value'
65+ if ( ! keys . includes ( 'value' ) ) {
66+ return false ;
67+ }
68+
69+ // ALLOWED keys: 'value', optionally 'unit'
70+ if ( keys . some ( k => k !== 'value' && k !== 'unit' ) ) {
71+ return false ;
72+ }
73+
74+ // All checks passed
75+ return true ;
6176}
6277
6378/**
6479 * Converts an attribute value to a typed attribute value.
6580 *
6681 * Does not allow mixed arrays. In case of a mixed array, the value is stringified and the type is 'string'.
82+ * All values besides the supported attribute types (see {@link AttributeTypeMap}) are stringified to a string attribute value.
6783 *
6884 * @param value - The value of the passed attribute.
6985 * @returns The typed attribute.
7086 */
7187export function attributeValueToTypedAttributeValue ( rawValue : unknown ) : TypedAttributeValue {
72- const unit = isAttributeObject ( rawValue ) ? rawValue . unit : undefined ;
73- const value = isAttributeObject ( rawValue ) ? rawValue . value : rawValue ;
88+ const { value, unit } = isAttributeObject ( rawValue ) ? rawValue : { value : rawValue , unit : undefined } ;
89+ return { ...getTypedAttributeValue ( value ) , ...( unit && { unit } ) } ;
90+ }
91+
92+ // Disallow NaN, differentiate between integer and double
93+ const getNumberType : ( num : number ) => 'integer' | 'double' | null = item =>
94+ Number . isNaN ( item ) ? null : Number . isInteger ( item ) ? 'integer' : 'double' ;
95+
96+ // Only allow string, boolean, or number types
97+ const getPrimitiveType : ( item : unknown ) => 'string' | 'boolean' | 'integer' | 'double' | null = item =>
98+ typeof item === 'string'
99+ ? 'string'
100+ : typeof item === 'boolean'
101+ ? 'boolean'
102+ : typeof item === 'number'
103+ ? getNumberType ( item )
104+ : null ;
74105
75- switch ( typeof value ) {
106+ function getTypedAttributeValue ( val : unknown ) : TypedAttributeValue {
107+ switch ( typeof val ) {
76108 case 'number' : {
77- const numberType = getNumberType ( value ) ;
109+ const numberType = getNumberType ( val ) ;
78110 if ( ! numberType ) {
79111 break ;
80112 }
81113 return {
82- value,
114+ value : val ,
83115 type : numberType ,
84- unit,
85116 } ;
86117 }
87118 case 'boolean' :
88119 return {
89- value,
120+ value : val ,
90121 type : 'boolean' ,
91- unit,
92122 } ;
93123 case 'string' :
94124 return {
95- value,
125+ value : val ,
96126 type : 'string' ,
97- unit,
98127 } ;
99128 }
100129
101- if ( Array . isArray ( value ) ) {
102- const coherentType = value . reduce ( ( acc : 'string' | 'boolean' | 'integer' | 'double' | null , item ) => {
130+ if ( Array . isArray ( val ) ) {
131+ const coherentType = val . reduce ( ( acc : 'string' | 'boolean' | 'integer' | 'double' | null , item ) => {
103132 if ( ! acc || getPrimitiveType ( item ) !== acc ) {
104133 return null ;
105134 }
106135 return acc ;
107- } , getPrimitiveType ( value [ 0 ] ) ) ;
136+ } , getPrimitiveType ( val [ 0 ] ) ) ;
108137
109138 if ( coherentType ) {
110- return { value, type : `${ coherentType } []` , unit } ;
139+ return { value : val , type : `${ coherentType } []` } ;
111140 }
112141 }
113142
114143 // Fallback: stringify the passed value
115144 let fallbackValue = '' ;
116145 try {
117- fallbackValue = JSON . stringify ( value ) ?? String ( value ) ;
146+ fallbackValue = JSON . stringify ( val ) ?? String ( val ) ;
118147 } catch {
119148 try {
120- fallbackValue = String ( value ) ;
149+ fallbackValue = String ( val ) ;
121150 } catch {
122151 // ignore
123152 }
@@ -126,20 +155,5 @@ export function attributeValueToTypedAttributeValue(rawValue: unknown): TypedAtt
126155 return {
127156 value : fallbackValue ,
128157 type : 'string' ,
129- unit,
130158 } ;
131159}
132-
133- // Disallow NaN, differentiate between integer and double
134- const getNumberType : ( num : number ) => 'integer' | 'double' | null = item =>
135- Number . isNaN ( item ) ? null : Number . isInteger ( item ) ? 'integer' : 'double' ;
136-
137- // Only allow string, boolean, or number types
138- const getPrimitiveType : ( item : unknown ) => 'string' | 'boolean' | 'integer' | 'double' | null = item =>
139- typeof item === 'string'
140- ? 'string'
141- : typeof item === 'boolean'
142- ? 'boolean'
143- : typeof item === 'number'
144- ? getNumberType ( item )
145- : null ;
0 commit comments