{"version":3,"file":"attributes.js","sources":["../../src/attributes.ts"],"sourcesContent":["import type { DurationUnit, FractionUnit, InformationUnit } from './types/measurement';\nimport type { Primitive } from './types/misc';\nimport { isPrimitive } from './utils/is';\n\nexport type RawAttributes = T & ValidatedAttributes;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport type RawAttribute = T extends { value: any } | { unit: any } ? AttributeObject : T;\n\nexport type Attributes = Record;\n\nexport type AttributeValueType = string | number | boolean | Array | Array | Array;\n\ntype AttributeTypeMap = {\n string: string;\n integer: number;\n double: number;\n boolean: boolean;\n array: Array | Array | Array;\n};\n\n/* Generates a type from the AttributeTypeMap like:\n | { value: string; type: 'string' }\n | { value: number; type: 'integer' }\n | { value: number; type: 'double' }\n */\ntype AttributeUnion = {\n [K in keyof AttributeTypeMap]: {\n value: AttributeTypeMap[K];\n type: K;\n };\n}[keyof AttributeTypeMap];\n\nexport type TypedAttributeValue = AttributeUnion & { unit?: AttributeUnit };\n\nexport type AttributeObject = {\n value: unknown;\n unit?: AttributeUnit;\n};\n\n// Unfortunately, we loose type safety if we did something like Exclude\n// so therefore we unionize between the three supported unit categories.\ntype AttributeUnit = DurationUnit | InformationUnit | FractionUnit;\n\n/* If an attribute has either a 'value' or 'unit' property, we use the ValidAttributeObject type. */\nexport type ValidatedAttributes = {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n [K in keyof T]: T[K] extends { value: any } | { unit: any } ? AttributeObject : unknown;\n};\n\n/**\n * Type-guard: The attribute object has the shape the official attribute object (value, type, unit).\n * https://develop.sentry.dev/sdk/telemetry/scopes/#setting-attributes\n */\nexport function isAttributeObject(maybeObj: unknown): maybeObj is AttributeObject {\n return (\n typeof maybeObj === 'object' &&\n maybeObj != null &&\n !Array.isArray(maybeObj) &&\n Object.keys(maybeObj).includes('value')\n );\n}\n\n/**\n * Converts an attribute value to a typed attribute value.\n *\n * For now, we support primitive values and arrays, either raw or inside attribute objects.\n * If @param useFallback is true, we stringify other non-primitive values to a string attribute\n * value. Otherwise we return `undefined` for unsupported values.\n *\n * @param value - The value of the passed attribute.\n * @param useFallback - If true, unsupported values will be stringified to a string attribute value.\n * Defaults to false. In this case, `undefined` is returned for unsupported values.\n * @returns The typed attribute.\n */\nexport function attributeValueToTypedAttributeValue(\n rawValue: unknown,\n useFallback?: boolean | 'skip-undefined',\n): TypedAttributeValue | void {\n const { value, unit } = isAttributeObject(rawValue) ? rawValue : { value: rawValue, unit: undefined };\n const attributeValue = getTypedAttributeValue(value);\n const checkedUnit = unit && typeof unit === 'string' ? { unit } : {};\n if (attributeValue) {\n return { ...attributeValue, ...checkedUnit };\n }\n\n if (!useFallback || (useFallback === 'skip-undefined' && value === undefined)) {\n return;\n }\n\n // Fallback: stringify the value\n // TODO(v11): be smarter here and use String constructor if stringify fails\n // (this is a breaking change for already existing attribute values)\n let stringValue = '';\n try {\n stringValue = JSON.stringify(value) ?? '';\n } catch {\n // Do nothing\n }\n return {\n value: stringValue,\n type: 'string',\n ...checkedUnit,\n };\n}\n\n/**\n * Serializes raw attributes to typed attributes as expected in our envelopes.\n *\n * @param attributes The raw attributes to serialize.\n * @param fallback If true, unsupported values will be stringified to a string attribute value.\n * Defaults to false. In this case, `undefined` is returned for unsupported values.\n *\n * @returns The serialized attributes.\n */\nexport function serializeAttributes(\n attributes: RawAttributes | undefined,\n fallback: boolean | 'skip-undefined' = false,\n): Attributes {\n const serializedAttributes: Attributes = {};\n for (const [key, value] of Object.entries(attributes ?? {})) {\n const typedValue = attributeValueToTypedAttributeValue(value, fallback);\n if (typedValue) {\n serializedAttributes[key] = typedValue;\n }\n }\n return serializedAttributes;\n}\n\n/**\n * Estimates the serialized byte size of {@link Attributes},\n * with a couple of heuristics for performance.\n */\nexport function estimateTypedAttributesSizeInBytes(attributes: Attributes | undefined): number {\n if (!attributes) {\n return 0;\n }\n let weight = 0;\n for (const [key, attr] of Object.entries(attributes)) {\n weight += key.length * 2;\n weight += attr.type.length * 2;\n weight += (attr.unit?.length ?? 0) * 2;\n const val = attr.value;\n\n if (Array.isArray(val)) {\n // Assumption: Individual array items have the same type and roughly the same size\n // probably not always true but allows us to cut down on runtime\n weight += estimatePrimitiveSizeInBytes(val[0]) * val.length;\n } else if (isPrimitive(val)) {\n weight += estimatePrimitiveSizeInBytes(val);\n } else {\n // default fallback for anything else (objects)\n weight += 100;\n }\n }\n return weight;\n}\n\nfunction estimatePrimitiveSizeInBytes(value: Primitive): number {\n if (typeof value === 'string') {\n return value.length * 2;\n } else if (typeof value === 'boolean') {\n return 4;\n } else if (typeof value === 'number') {\n return 8;\n }\n return 0;\n}\n\n/**\n * NOTE: We return typed attributes for primitives and arrays:\n * - Relay currently only supports arrays consisting of primitive values. Attributes with non-conforming arrays are dropped by Relay, so runtime type validation in the SDK is unnecessary.\n * - Objects are not supported yet and product support is still TBD.\n */\nfunction getTypedAttributeValue(value: unknown): TypedAttributeValue | void {\n if (Array.isArray(value)) {\n return { value, type: 'array' };\n }\n\n const primitiveType =\n typeof value === 'string'\n ? 'string'\n : typeof value === 'boolean'\n ? 'boolean'\n : typeof value === 'number' && !Number.isNaN(value)\n ? Number.isInteger(value)\n ? 'integer'\n : 'double'\n : null;\n if (primitiveType) {\n // @ts-expect-error - TS complains because {@link TypedAttributeValue} is strictly typed to\n // avoid setting the wrong `type` on the attribute value.\n // In this case, getPrimitiveType already does the check but TS doesn't know that.\n // The \"clean\" alternative is to return an object per `typeof value` case\n // but that would require more bundle size\n // Therefore, we ignore it.\n return { value, type: primitiveType };\n }\n}\n"],"names":[],"mappings":"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"}