From 760903aafc4010b93bd5daf5cdd3a1df25ec5cb5 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Thu, 18 Dec 2025 16:29:54 +0000 Subject: [PATCH 001/168] Add documentation for Session Replay's CLI --- packages/react-native-session-replay/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/packages/react-native-session-replay/README.md b/packages/react-native-session-replay/README.md index da1101745..4b019a86e 100644 --- a/packages/react-native-session-replay/README.md +++ b/packages/react-native-session-replay/README.md @@ -127,6 +127,21 @@ yarn android The `datadog-generate-sr-assets` CLI utility scans your codebase for SVG elements and pre-generates optimized assets that will be included in your native builds. +#### CLI Options + +The `datadog-generate-sr-assets` command supports the following options: + +```sh +npx datadog-generate-sr-assets [options] +``` + +| Option | Alias | Description | Example | +|--------|-------|-------------|---------| +| `--ignore ` | `-i` | Additional glob patterns to ignore during scanning. Can be specified multiple times. | `npx datadog-generate-sr-assets --ignore "**/legacy/**" --ignore "**/vendor/**"` | +| `--verbose` | `-v` | Enable verbose output for debugging. | `npx datadog-generate-sr-assets --verbose` | +| `--path ` | `-p` | Path to the root directory to scan. Defaults to the current working directory. | `npx datadog-generate-sr-assets --path ./src` | +| `--followSymlinks` | | Follow symbolic links during directory traversal. Default: false (symlinks are ignored). | `npx datadog-generate-sr-assets --followSymlinks` | + **Note for CI/CD**: If you use continuous integration for your builds, make sure to include these steps in your CI pipeline. The workflow should be: `yarn install` → `npx datadog-generate-sr-assets` → `pod install` (for iOS) → build your app. This ensures SVG assets are properly generated before the native build process. ### Development Workflow From 516f352610a384267e8930bd80c77afec1df8c63 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Tue, 6 Jan 2026 16:30:03 +0000 Subject: [PATCH 002/168] Fix inconsistency in babel plugin's README.md - svgTracking configuration option's default value should be `false` in order to be in conformity with the code --- packages/react-native-babel-plugin/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-native-babel-plugin/README.md b/packages/react-native-babel-plugin/README.md index af4d09e60..e47cbafd8 100644 --- a/packages/react-native-babel-plugin/README.md +++ b/packages/react-native-babel-plugin/README.md @@ -49,7 +49,7 @@ You can configure the plugin to adjust how it processes your code, giving you co | Option | Type | Default | Description | |-----------------|---------|---------|-------------| -| `svgTracking` | boolean | true | Whether to track SVG assets in the context of Session Replay. | +| `svgTracking` | boolean | false | Whether to track SVG assets in the context of Session Replay. | --- From f7dcf5c311318a47f970d11488a4bb986f7081a0 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Mon, 5 Jan 2026 12:53:17 +0000 Subject: [PATCH 003/168] Fix incorrect babel's component handler override operations - Prevent overriding handlers on custom components without proper configuration - Prevent overriding handlers when spread props are present - Prevent adding 'Mandatory' handlers on custom components --- .../src/actions/rum/index.ts | 40 ++- .../test/plugin.test.ts | 307 +++++++++++++++++- 2 files changed, 336 insertions(+), 11 deletions(-) diff --git a/packages/react-native-babel-plugin/src/actions/rum/index.ts b/packages/react-native-babel-plugin/src/actions/rum/index.ts index f5ec2c700..b7eab2bce 100644 --- a/packages/react-native-babel-plugin/src/actions/rum/index.ts +++ b/packages/react-native-babel-plugin/src/actions/rum/index.ts @@ -92,14 +92,25 @@ export function handleJSXElementActionPaths( ? Object.keys(state.trackedComponents) : []; - // Some components need specific handlers present (inject no-op handlers if missing) - ensureMandatoryAttributes( - path, - componentName, - actionPathList, - actionPathNames + // Only inject mandatory attributes for native components, NOT options tracked components + // Options tracked components define their own handlers and should not have additional ones injected + const isOptionsTrackedComponent = options.components.tracked.find( + x => x.name === componentName ); + if ( + !isOptionsTrackedComponent && + componentNameList.includes(componentName) + ) { + // Some native components need specific handlers present (inject no-op handlers if missing) + ensureMandatoryAttributes( + path, + componentName, + actionPathList, + actionPathNames + ); + } + // Optionally compute a content getter (children + label props) setContentAttribute(componentName, t, path, state, ddValues); @@ -115,13 +126,17 @@ export function handleJSXElementActionPaths( /** * Ensures that all mandatory handler attributes exist on the element so that - * they can be wrapped by RUM even if the user didn’t specify them. + * they can be wrapped by RUM even if the user didn't specify them. * * Example: * Some inputs require `onFocus`/`onBlur` for reliable action boundaries. * If missing, we inject `() => {}` as a placeholder and mark those paths * as actionable so they get wrapped downstream. * + * IMPORTANT: If the element has spread attributes (e.g., {...props}), we cannot + * safely inject handlers because we don't know at build time what props are being + * spread. In such cases, we skip injection to avoid overwriting existing handlers. + * * @param path JSXElement path. * @param componentName Host component name for lookup in `tapElementsRequiredAttributesMap`. * @param actionPathList Collected actionable attribute paths (will be appended to). @@ -133,6 +148,17 @@ export function ensureMandatoryAttributes( actionPathList: Babel.NodePath[], actionPathNames: string[] ) { + // Check if there are any spread attributes + const hasSpreadAttributes = path.node.openingElement.attributes.some( + attr => attr.type === 'JSXSpreadAttribute' + ); + + // If spread attributes exist, we cannot safely inject handlers + // because we don't know what props are being spread at build time + if (hasSpreadAttributes) { + return; + } + // Resolve any mandatory attributes for this component const requiredAttributes = tapElementsRequiredAttributesMap[componentName]; if (requiredAttributes) { diff --git a/packages/react-native-babel-plugin/test/plugin.test.ts b/packages/react-native-babel-plugin/test/plugin.test.ts index 7e02d676b..ad861b493 100644 --- a/packages/react-native-babel-plugin/test/plugin.test.ts +++ b/packages/react-native-babel-plugin/test/plugin.test.ts @@ -1,18 +1,43 @@ /* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ import { transform } from '@babel/core'; import plugin from '../src/index'; +import type { PluginOptions } from '../src/types'; + +function transformCode(code: string, pluginOptions?: Partial) { + const defaultOptions: PluginOptions = { + actionNameAttribute: 'example-button-prop', + components: { + useContent: true, + useNamePrefix: true, + tracked: [] + }, + sessionReplay: { + svgTracking: false + } + }; + + const options = { + ...defaultOptions, + ...pluginOptions, + components: { + ...defaultOptions.components, + ...pluginOptions?.components + }, + sessionReplay: { + ...defaultOptions.sessionReplay, + ...pluginOptions?.sessionReplay + } + }; -function transformCode(code: string) { return transform(code, { filename: 'file.tsx', presets: ['@babel/preset-react', '@babel/preset-typescript'], - plugins: [[plugin, { actionNameAttribute: 'example-button-prop' }]], + plugins: [[plugin, options]], configFile: false })?.code; } @@ -147,6 +172,280 @@ describe('Babel plugin: wrap interaction handlers for RUM', () => { `); }); + it('should not add mandatory property (onFocus) on supported element (TextInput) when not present if there`s options tracked component with the same name', () => { + const options: Partial = { + components: { + useContent: true, + useNamePrefix: true, + tracked: [ + { + name: 'TextInput', + handlers: [{ event: 'onFocus', action: 'TAP' }] + } + ] + } + }; + + const input = ` + import { TextInput } from 'react-native'; + + `; + const output = transformCode(input, options); + expect(output).toMatchInlineSnapshot(` + "import { TextInput } from 'react-native'; + /*#__PURE__*/React.createElement(TextInput, { + placeholder: "Enter username", + value: username, + onChangeText: setUsername, + style: styles.input + });" + `); + }); + + it('should not add property (onFocus) on supported element (TextInput) when spreading props', () => { + const input = ` + import { TextInput } from 'react-native'; + + `; + const output = transformCode(input); + expect(output).toMatchInlineSnapshot(` + "import { TextInput } from 'react-native'; + /*#__PURE__*/React.createElement(TextInput, props);" + `); + }); + + it('should wrap existing (onFocus) on supported element (TextInput) when spreading props', () => { + const input = ` + import { TextInput } from 'react-native'; + console.log('Focused')}/> + `; + const output = transformCode(input); + expect(output).toMatchInlineSnapshot(` + "import { DdBabelInteractionTracking, __ddExtractText } from "@datadog/mobile-react-native"; + function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } + import { TextInput } from 'react-native'; + /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { + onFocus: () => { + if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => console.log('Focused'), "TAP", { + "options": { + "useContent": true, + "useNamePrefix": true + }, + "getContent": () => { + return __ddExtractText(/*#__PURE__*/React.createElement(React.Fragment, null), []); + }, + "handlerArgs": [], + "componentName": "TextInput" + })();else return (() => console.log('Focused'))(); + } + }));" + `); + }); + + it('should not add property (onFocus) on custom element (TextInput) when not present', () => { + const input = ` + import { TextInput } from './TextInput'; + + `; + const output = transformCode(input); + expect(output).toMatchInlineSnapshot(` + "import { TextInput } from './TextInput'; + /*#__PURE__*/React.createElement(TextInput, null);" + `); + }); + + it('should not add property (onFocus) on custom element (TextInput - not tracked) when spreading props', () => { + const input = ` + import { TextInput } from './TextInput'; + + `; + const output = transformCode(input); + expect(output).toMatchInlineSnapshot(` + "import { TextInput } from './TextInput'; + /*#__PURE__*/React.createElement(TextInput, props);" + `); + }); + + it('should not add property (onFocus) on custom element (TextInput - tracked) when spreading props', () => { + const options: Partial = { + components: { + useContent: true, + useNamePrefix: true, + tracked: [ + { + name: 'TextInput', + handlers: [{ event: 'onFocus', action: 'TAP' }] + } + ] + } + }; + const input = ` + import { TextInput } from './TextInput'; + + `; + const output = transformCode(input, options); + expect(output).toMatchInlineSnapshot(` + "import { TextInput } from './TextInput'; + /*#__PURE__*/React.createElement(TextInput, props);" + `); + }); + + it('should not wrap existing (onFocus) on custom element (TextInput - not tracked) when spreading props', () => { + // Since it's a custom not-tracked component, the plugin doesn't know if this handler should be wraaped or not + const input = ` + import { TextInput } from './TextInput'; + console.log('Focused')}/> + `; + const output = transformCode(input); + expect(output).toMatchInlineSnapshot(` + "function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } + import { TextInput } from './TextInput'; + /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { + onFocus: () => console.log('Focused') + }));" + `); + }); + + it('should wrap existing (onFocus) on custom element (TextInput - tracked) when spreading props', () => { + // Since it's a custom tracked component, the plugin knows which handler to track + const options: Partial = { + components: { + useContent: true, + useNamePrefix: true, + tracked: [ + { + name: 'TextInput', + handlers: [{ event: 'onFocus', action: 'TAP' }] + } + ] + } + }; + + const input = ` + import { TextInput } from './TextInput'; + console.log('Focused')}/> + `; + + const output = transformCode(input, options); + expect(output).toMatchInlineSnapshot(` + "import { DdBabelInteractionTracking, __ddExtractText } from "@datadog/mobile-react-native"; + function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } + import { TextInput } from './TextInput'; + /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { + onFocus: () => { + if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => console.log('Focused'), "TAP", { + "options": { + "useContent": true, + "useNamePrefix": true + }, + "getContent": () => { + return __ddExtractText(/*#__PURE__*/React.createElement(React.Fragment, null), []); + }, + "handlerArgs": [], + "componentName": "TextInput" + })();else return (() => console.log('Focused'))(); + } + }));" + `); + }); + + it('should wrap arrow function with one argument', () => { + const input = ` + import { Pressable } from 'react-native'; + { + console.log('Testing: ', event); + }} /> + `; + const output = transformCode(input); + expect(output).toMatchInlineSnapshot(` + "import { DdBabelInteractionTracking, __ddExtractText } from "@datadog/mobile-react-native"; + import { Pressable } from 'react-native'; + /*#__PURE__*/React.createElement(Pressable, { + onPress: event => { + if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(event => { + console.log('Testing: ', event); + }, "TAP", { + "options": { + "useContent": true, + "useNamePrefix": true + }, + "getContent": () => { + return __ddExtractText(/*#__PURE__*/React.createElement(React.Fragment, null), []); + }, + "handlerArgs": [event], + "componentName": "Pressable" + })(event);else return (event => { + console.log('Testing: ', event); + })(event); + } + });" + `); + }); + + it('should not wrap existing (onFocus) on custom element (TextInput - tracked) when spreading props', () => { + // Since it's a custom not-tracked component, the plugin doesn't know if this handler should be wraaped or not + const input = ` + import { TextInput } from './TextInput'; + console.log('Focused')}/> + `; + const output = transformCode(input); + expect(output).toMatchInlineSnapshot(` + "function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } + import { TextInput } from './TextInput'; + /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { + onFocus: () => console.log('Focused') + }));" + `); + }); + + it('should wrap existing (onFocus) on custom element (TextInput - tracked) when spreading props', () => { + // Since it's a custom tracked component, the plugin knows which handler to track + const options: Partial = { + components: { + useContent: true, + useNamePrefix: true, + tracked: [ + { + name: 'TextInput', + handlers: [{ event: 'onFocus', action: 'TAP' }] + } + ] + } + }; + + const input = ` + import { TextInput } from './TextInput'; + console.log('Focused')}/> + `; + + const output = transformCode(input, options); + expect(output).toMatchInlineSnapshot(` + "import { DdBabelInteractionTracking, __ddExtractText } from "@datadog/mobile-react-native"; + function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } + import { TextInput } from './TextInput'; + /*#__PURE__*/React.createElement(TextInput, _extends({}, props, { + onFocus: () => { + if (DdBabelInteractionTracking.getInstance()) return DdBabelInteractionTracking.getInstance().wrapRumAction(() => console.log('Focused'), "TAP", { + "options": { + "useContent": true, + "useNamePrefix": true + }, + "getContent": () => { + return __ddExtractText(/*#__PURE__*/React.createElement(React.Fragment, null), []); + }, + "handlerArgs": [], + "componentName": "TextInput" + })();else return (() => console.log('Focused'))(); + } + }));" + `); + }); + it('should wrap arrow function with one argument', () => { const input = ` import { Pressable } from 'react-native'; From 1b733a0b950da315f673b05572956d295779f163 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 12 Jan 2026 17:42:03 +0100 Subject: [PATCH 004/168] Case sensistive support for generated assets folder names on sourcemaps gradle task --- packages/core/datadog-sourcemaps.gradle | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/datadog-sourcemaps.gradle b/packages/core/datadog-sourcemaps.gradle index a2553b6cc..d447a8dcb 100644 --- a/packages/core/datadog-sourcemaps.gradle +++ b/packages/core/datadog-sourcemaps.gradle @@ -309,7 +309,8 @@ private def getReactConfig(File buildDir, variant) { def targetPath = variant.name reactConfig['bundleTaskName'] = "createBundle${targetName}JsAndAssets" reactConfig['bundleFileResolver'] = getBundleFileResolver([ - "$buildDir/generated/assets/react/${targetName.toLowerCase()}", // RN 0.82+ + "$buildDir/generated/assets/react/${targetName}", // RN 0.82+ - for case sensitive OS's + "$buildDir/generated/assets/react/${targetName.toLowerCase()}", // RN 0.82+ - for case insensitive OS's "$buildDir/ASSETS/createBundle${targetName}JsAndAssets", // Android Gradle Plugin 7.3 "$buildDir/generated/assets/createBundle${targetName}JsAndAssets" // Android Gradle Plugin 7.4 and up ] as String[], bundleAssetName) From 65ed580539d1ec1abf2d6a92d2dafca6c20ce0a1 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Thu, 8 Jan 2026 11:00:21 +0100 Subject: [PATCH 005/168] Calling original callback after addAction in wrapRumAction --- .../interactionTracking/DdBabelInteractionTracking.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts b/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts index 0657378d2..63186f0c8 100644 --- a/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts +++ b/packages/core/src/rum/instrumentation/interactionTracking/DdBabelInteractionTracking.ts @@ -139,8 +139,6 @@ export class DdBabelInteractionTracking { targetObject: TargetObject ): (...args: any[]) => any { return (...args: any[]) => { - const result = func(...args); - if (!this.telemetrySent) { DdSdk?.sendTelemetryLog( BABEL_PLUGIN_TELEMETRY, @@ -174,7 +172,7 @@ export class DdBabelInteractionTracking { }); } - return result; + return func(...args); }; } } From e710dc3999de7a5a5b6b0f56603e32067acd60a5 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Tue, 13 Jan 2026 16:24:37 +0100 Subject: [PATCH 006/168] Use targetPath instead of targetName to avoid capitalization --- packages/core/datadog-sourcemaps.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/datadog-sourcemaps.gradle b/packages/core/datadog-sourcemaps.gradle index d447a8dcb..5eebb7e0d 100644 --- a/packages/core/datadog-sourcemaps.gradle +++ b/packages/core/datadog-sourcemaps.gradle @@ -309,7 +309,7 @@ private def getReactConfig(File buildDir, variant) { def targetPath = variant.name reactConfig['bundleTaskName'] = "createBundle${targetName}JsAndAssets" reactConfig['bundleFileResolver'] = getBundleFileResolver([ - "$buildDir/generated/assets/react/${targetName}", // RN 0.82+ - for case sensitive OS's + "$buildDir/generated/assets/react/${targetPath}", // RN 0.82+ - for case sensitive OS's "$buildDir/generated/assets/react/${targetName.toLowerCase()}", // RN 0.82+ - for case insensitive OS's "$buildDir/ASSETS/createBundle${targetName}JsAndAssets", // Android Gradle Plugin 7.3 "$buildDir/generated/assets/createBundle${targetName}JsAndAssets" // Android Gradle Plugin 7.4 and up From b97a8218f3763f9b766fc6f52fc53c85a06499f6 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 16 Dec 2025 10:45:15 +0100 Subject: [PATCH 007/168] Fix CVE-2025-64756 (glob) --- benchmarks/package.json | 2 +- example/package.json | 2 +- package.json | 4 +- yarn.lock | 4227 ++++++--------------------------------- 4 files changed, 567 insertions(+), 3668 deletions(-) diff --git a/benchmarks/package.json b/benchmarks/package.json index 03c86ac9e..c4e631ff1 100644 --- a/benchmarks/package.json +++ b/benchmarks/package.json @@ -40,7 +40,7 @@ "@babel/core": "7.25.2", "@babel/preset-env": "7.25.3", "@babel/runtime": "^7.26.10", - "@datadog/datadog-ci": "3.12.0", + "@datadog/datadog-ci": "^4.4.0", "@react-native-community/cli": "15.0.1", "@react-native-community/cli-platform-android": "15.0.1", "@react-native-community/cli-platform-ios": "15.0.1", diff --git a/example/package.json b/example/package.json index d114dc49a..db38f20b6 100644 --- a/example/package.json +++ b/example/package.json @@ -36,7 +36,7 @@ "@babel/core": "^7.25.2", "@babel/preset-env": "^7.25.3", "@babel/runtime": "^7.26.10", - "@datadog/datadog-ci": "^2.39.0", + "@datadog/datadog-ci": "^4.4.0", "@react-native/babel-preset": "0.76.9", "@react-native/eslint-config": "0.76.9", "@react-native/metro-config": "0.76.9", diff --git a/package.json b/package.json index 807253ffd..db8ba26ad 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "eslint-plugin-react-native": "3.10.0", "genversion": "3.0.2", "jest": "^29.7.0", - "lerna": "9.0.0", + "lerna": "9.0.3", "metro": "^0.83.1", "pod-install": "0.1.14", "prettier": "2.2.0", @@ -83,6 +83,8 @@ }, "packageManager": "yarn@3.4.1", "resolutions": { + "cacache/glob": "^10.5.0", + "@npmcli/arborist": "^9.1.8", "ejs": "3.1.10", "shell-quote": "^1.7.3", "cross-spawn": "7.0.5", diff --git a/yarn.lock b/yarn.lock index 22b120805..1420b2b87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,6 +15,16 @@ __metadata: languageName: node linkType: hard +"@antfu/install-pkg@npm:^1.1.0": + version: 1.1.0 + resolution: "@antfu/install-pkg@npm:1.1.0" + dependencies: + package-manager-detector: ^1.3.0 + tinyexec: ^1.0.1 + checksum: e20b7cd1c37eff832cc878cddd794f8c3779175681cf6d75c4cc1ae1475526126a4c1f71fa027161aa1ee35a8850782be9ca0ec01b621893defebe97ba9dc70e + languageName: node + linkType: hard + "@apollo/client@npm:^3.8.3": version: 3.13.8 resolution: "@apollo/client@npm:3.13.8" @@ -51,1545 +61,6 @@ __metadata: languageName: node linkType: hard -"@aws-crypto/crc32@npm:5.2.0": - version: 5.2.0 - resolution: "@aws-crypto/crc32@npm:5.2.0" - dependencies: - "@aws-crypto/util": ^5.2.0 - "@aws-sdk/types": ^3.222.0 - tslib: ^2.6.2 - checksum: 1ddf7ec3fccf106205ff2476d90ae1d6625eabd47752f689c761b71e41fe451962b7a1c9ed25fe54e17dd747a62fbf4de06030fe56fe625f95285f6f70b96c57 - languageName: node - linkType: hard - -"@aws-crypto/sha256-browser@npm:5.2.0": - version: 5.2.0 - resolution: "@aws-crypto/sha256-browser@npm:5.2.0" - dependencies: - "@aws-crypto/sha256-js": ^5.2.0 - "@aws-crypto/supports-web-crypto": ^5.2.0 - "@aws-crypto/util": ^5.2.0 - "@aws-sdk/types": ^3.222.0 - "@aws-sdk/util-locate-window": ^3.0.0 - "@smithy/util-utf8": ^2.0.0 - tslib: ^2.6.2 - checksum: 773f12f2026d82a6bb4a23a8f491894a6d32525bd9b8bfbc12896526cf11882a7607a671c478c45f9cd7d6ba1caaed48a62b67c6f725244bd83a1275108f46c7 - languageName: node - linkType: hard - -"@aws-crypto/sha256-js@npm:5.2.0, @aws-crypto/sha256-js@npm:^5.2.0": - version: 5.2.0 - resolution: "@aws-crypto/sha256-js@npm:5.2.0" - dependencies: - "@aws-crypto/util": ^5.2.0 - "@aws-sdk/types": ^3.222.0 - tslib: ^2.6.2 - checksum: 007fbe0436d714d0d0d282e2b61c90e45adcb9ad75eac9ac7ba03d32b56624afd09b2a9ceb4d659661cf17c51d74d1900ab6b00eacafc002da1101664955ca53 - languageName: node - linkType: hard - -"@aws-crypto/supports-web-crypto@npm:^5.2.0": - version: 5.2.0 - resolution: "@aws-crypto/supports-web-crypto@npm:5.2.0" - dependencies: - tslib: ^2.6.2 - checksum: 6ffc21de48b2b2c3e918193101d7e8fe949d47b37688892e1c39eaedaa938be80c0f404fe1c874c30cce16781026777a53bf47d5d90143ca91d0feb7c4a6f830 - languageName: node - linkType: hard - -"@aws-crypto/util@npm:^5.2.0": - version: 5.2.0 - resolution: "@aws-crypto/util@npm:5.2.0" - dependencies: - "@aws-sdk/types": ^3.222.0 - "@smithy/util-utf8": ^2.0.0 - tslib: ^2.6.2 - checksum: f0f81d9d2771c59946cfec48b86cb23d39f78a966c4a1f89d4753abdc3cb38de06f907d1e6450059b121d48ac65d612ab88bdb70014553a077fc3dabddfbf8d6 - languageName: node - linkType: hard - -"@aws-sdk/client-cloudwatch-logs@npm:^3.624.0": - version: 3.832.0 - resolution: "@aws-sdk/client-cloudwatch-logs@npm:3.832.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/credential-provider-node": 3.830.0 - "@aws-sdk/middleware-host-header": 3.821.0 - "@aws-sdk/middleware-logger": 3.821.0 - "@aws-sdk/middleware-recursion-detection": 3.821.0 - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/region-config-resolver": 3.821.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@aws-sdk/util-user-agent-browser": 3.821.0 - "@aws-sdk/util-user-agent-node": 3.828.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/eventstream-serde-browser": ^4.0.4 - "@smithy/eventstream-serde-config-resolver": ^4.1.2 - "@smithy/eventstream-serde-node": ^4.0.4 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-retry": ^4.1.12 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.19 - "@smithy/util-defaults-mode-node": ^4.0.19 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - "@smithy/util-utf8": ^4.0.0 - "@types/uuid": ^9.0.1 - tslib: ^2.6.2 - uuid: ^9.0.1 - checksum: adce5efe6448b38bcfeb25a991774a8d50dc92bd70b81a69b2182cb098e8c27fb76efe55ac6aea23b367bfec7acb99cc849cb164a25f7b76b1b94f1b0275c008 - languageName: node - linkType: hard - -"@aws-sdk/client-cloudwatch-logs@npm:^3.709.0": - version: 3.844.0 - resolution: "@aws-sdk/client-cloudwatch-logs@npm:3.844.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/credential-provider-node": 3.844.0 - "@aws-sdk/middleware-host-header": 3.840.0 - "@aws-sdk/middleware-logger": 3.840.0 - "@aws-sdk/middleware-recursion-detection": 3.840.0 - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/region-config-resolver": 3.840.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@aws-sdk/util-user-agent-browser": 3.840.0 - "@aws-sdk/util-user-agent-node": 3.844.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/eventstream-serde-browser": ^4.0.4 - "@smithy/eventstream-serde-config-resolver": ^4.1.2 - "@smithy/eventstream-serde-node": ^4.0.4 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-retry": ^4.1.15 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.22 - "@smithy/util-defaults-mode-node": ^4.0.22 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - "@smithy/util-utf8": ^4.0.0 - "@types/uuid": ^9.0.1 - tslib: ^2.6.2 - uuid: ^9.0.1 - checksum: db7974d3e9577bbfa140863c25fc151dcfd91054d07d96fd69e0a25f2da022a04ceeb9fed422f8728da0751490b55458b88ef9b4e2da10496adb1cc37322a918 - languageName: node - linkType: hard - -"@aws-sdk/client-cognito-identity@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.830.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/credential-provider-node": 3.830.0 - "@aws-sdk/middleware-host-header": 3.821.0 - "@aws-sdk/middleware-logger": 3.821.0 - "@aws-sdk/middleware-recursion-detection": 3.821.0 - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/region-config-resolver": 3.821.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@aws-sdk/util-user-agent-browser": 3.821.0 - "@aws-sdk/util-user-agent-node": 3.828.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-retry": ^4.1.12 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.19 - "@smithy/util-defaults-mode-node": ^4.0.19 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: 8b4360286b40ff2f21df0cf1ee666d6cfa0a961d2fe21c37d5050a2a0aa50d66efdd42b92087b26689bde28de3a0b2d24f1aecffe4528da4a7a757a61cf8cd38 - languageName: node - linkType: hard - -"@aws-sdk/client-cognito-identity@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.844.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/credential-provider-node": 3.844.0 - "@aws-sdk/middleware-host-header": 3.840.0 - "@aws-sdk/middleware-logger": 3.840.0 - "@aws-sdk/middleware-recursion-detection": 3.840.0 - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/region-config-resolver": 3.840.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@aws-sdk/util-user-agent-browser": 3.840.0 - "@aws-sdk/util-user-agent-node": 3.844.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-retry": ^4.1.15 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.22 - "@smithy/util-defaults-mode-node": ^4.0.22 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: e3e1e8bd44530b95a7346b0176d3a8c62c5d4f5f4cbf49ce60f5a51fffe66b8cb0f5e9bec11d6b283721f9bc2f5cfc556ef7c730bc48aa9c406ddcf5109fc0f7 - languageName: node - linkType: hard - -"@aws-sdk/client-iam@npm:^3.624.0": - version: 3.830.0 - resolution: "@aws-sdk/client-iam@npm:3.830.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/credential-provider-node": 3.830.0 - "@aws-sdk/middleware-host-header": 3.821.0 - "@aws-sdk/middleware-logger": 3.821.0 - "@aws-sdk/middleware-recursion-detection": 3.821.0 - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/region-config-resolver": 3.821.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@aws-sdk/util-user-agent-browser": 3.821.0 - "@aws-sdk/util-user-agent-node": 3.828.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-retry": ^4.1.12 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.19 - "@smithy/util-defaults-mode-node": ^4.0.19 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - "@smithy/util-utf8": ^4.0.0 - "@smithy/util-waiter": ^4.0.5 - tslib: ^2.6.2 - checksum: 3a4f7f2e2e47990446f1d76ab1858332d47a0bd073f8252a17c55b9ac60810f99583bd233a7b37bab151d8b7b897feea9c144837aa102ca5c948082dde8633f5 - languageName: node - linkType: hard - -"@aws-sdk/client-iam@npm:^3.709.0": - version: 3.844.0 - resolution: "@aws-sdk/client-iam@npm:3.844.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/credential-provider-node": 3.844.0 - "@aws-sdk/middleware-host-header": 3.840.0 - "@aws-sdk/middleware-logger": 3.840.0 - "@aws-sdk/middleware-recursion-detection": 3.840.0 - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/region-config-resolver": 3.840.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@aws-sdk/util-user-agent-browser": 3.840.0 - "@aws-sdk/util-user-agent-node": 3.844.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-retry": ^4.1.15 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.22 - "@smithy/util-defaults-mode-node": ^4.0.22 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - "@smithy/util-utf8": ^4.0.0 - "@smithy/util-waiter": ^4.0.6 - tslib: ^2.6.2 - checksum: a0d8b6e46517c46a491a25ebb883757a7ab7167e4a6b5a0b3510346c1a0954e681f4f4731e284ddf467a811c53e6a32c94a4cea916055e0a88fdaccf45af516f - languageName: node - linkType: hard - -"@aws-sdk/client-lambda@npm:^3.624.0": - version: 3.833.0 - resolution: "@aws-sdk/client-lambda@npm:3.833.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/credential-provider-node": 3.830.0 - "@aws-sdk/middleware-host-header": 3.821.0 - "@aws-sdk/middleware-logger": 3.821.0 - "@aws-sdk/middleware-recursion-detection": 3.821.0 - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/region-config-resolver": 3.821.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@aws-sdk/util-user-agent-browser": 3.821.0 - "@aws-sdk/util-user-agent-node": 3.828.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/eventstream-serde-browser": ^4.0.4 - "@smithy/eventstream-serde-config-resolver": ^4.1.2 - "@smithy/eventstream-serde-node": ^4.0.4 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-retry": ^4.1.12 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.19 - "@smithy/util-defaults-mode-node": ^4.0.19 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - "@smithy/util-stream": ^4.2.2 - "@smithy/util-utf8": ^4.0.0 - "@smithy/util-waiter": ^4.0.5 - tslib: ^2.6.2 - checksum: d9fe0e1d29f00bd5d8f0b63713fb6714a3cede101e2583459cf1404c56c7afec59da118342d0402769dd6ea6db528415b859cc377886224999c175ad2eca2744 - languageName: node - linkType: hard - -"@aws-sdk/client-lambda@npm:^3.709.0": - version: 3.844.0 - resolution: "@aws-sdk/client-lambda@npm:3.844.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/credential-provider-node": 3.844.0 - "@aws-sdk/middleware-host-header": 3.840.0 - "@aws-sdk/middleware-logger": 3.840.0 - "@aws-sdk/middleware-recursion-detection": 3.840.0 - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/region-config-resolver": 3.840.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@aws-sdk/util-user-agent-browser": 3.840.0 - "@aws-sdk/util-user-agent-node": 3.844.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/eventstream-serde-browser": ^4.0.4 - "@smithy/eventstream-serde-config-resolver": ^4.1.2 - "@smithy/eventstream-serde-node": ^4.0.4 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-retry": ^4.1.15 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.22 - "@smithy/util-defaults-mode-node": ^4.0.22 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - "@smithy/util-stream": ^4.2.3 - "@smithy/util-utf8": ^4.0.0 - "@smithy/util-waiter": ^4.0.6 - tslib: ^2.6.2 - checksum: bfefc7a0de33d198ab34925b26f54300f6fb23aaa8f0bf6c5a4098e8438277ee64838ccdee94128036c3705847635e63512aadd267c43cc633c0e5cc7a6d7fd1 - languageName: node - linkType: hard - -"@aws-sdk/client-sfn@npm:^3.624.0": - version: 3.830.0 - resolution: "@aws-sdk/client-sfn@npm:3.830.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/credential-provider-node": 3.830.0 - "@aws-sdk/middleware-host-header": 3.821.0 - "@aws-sdk/middleware-logger": 3.821.0 - "@aws-sdk/middleware-recursion-detection": 3.821.0 - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/region-config-resolver": 3.821.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@aws-sdk/util-user-agent-browser": 3.821.0 - "@aws-sdk/util-user-agent-node": 3.828.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-retry": ^4.1.12 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.19 - "@smithy/util-defaults-mode-node": ^4.0.19 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - "@smithy/util-utf8": ^4.0.0 - "@types/uuid": ^9.0.1 - tslib: ^2.6.2 - uuid: ^9.0.1 - checksum: 7726870268b1e6d7a5e3d2a4cd1147b7ba0e4ff37d612c40207c6a61c690e79366d136b7fb0792d631aeec2a5f54e79d868019dba016ebe4899ea3cc6376f82a - languageName: node - linkType: hard - -"@aws-sdk/client-sfn@npm:^3.709.0": - version: 3.844.0 - resolution: "@aws-sdk/client-sfn@npm:3.844.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/credential-provider-node": 3.844.0 - "@aws-sdk/middleware-host-header": 3.840.0 - "@aws-sdk/middleware-logger": 3.840.0 - "@aws-sdk/middleware-recursion-detection": 3.840.0 - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/region-config-resolver": 3.840.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@aws-sdk/util-user-agent-browser": 3.840.0 - "@aws-sdk/util-user-agent-node": 3.844.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-retry": ^4.1.15 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.22 - "@smithy/util-defaults-mode-node": ^4.0.22 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - "@smithy/util-utf8": ^4.0.0 - "@types/uuid": ^9.0.1 - tslib: ^2.6.2 - uuid: ^9.0.1 - checksum: 0a8453fb72c6cc48cb4ee9534b9ec01fc79f8cf94136a3322ce47012de134d4be4fc2f6375691763586268d042994eb65014479bafa4c1a10c0ffdec1bdd4e43 - languageName: node - linkType: hard - -"@aws-sdk/client-sso@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/client-sso@npm:3.830.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/middleware-host-header": 3.821.0 - "@aws-sdk/middleware-logger": 3.821.0 - "@aws-sdk/middleware-recursion-detection": 3.821.0 - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/region-config-resolver": 3.821.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@aws-sdk/util-user-agent-browser": 3.821.0 - "@aws-sdk/util-user-agent-node": 3.828.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-retry": ^4.1.12 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.19 - "@smithy/util-defaults-mode-node": ^4.0.19 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: 7171831e47374963d0cc1af33319fcc5f0a2ad3c71bd3602422e13bba0c84dcbf55e032bc5907cab996e21f76fcc45e8865bffa7acf6c7e72503e4a0df297d3f - languageName: node - linkType: hard - -"@aws-sdk/client-sso@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/client-sso@npm:3.844.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/middleware-host-header": 3.840.0 - "@aws-sdk/middleware-logger": 3.840.0 - "@aws-sdk/middleware-recursion-detection": 3.840.0 - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/region-config-resolver": 3.840.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@aws-sdk/util-user-agent-browser": 3.840.0 - "@aws-sdk/util-user-agent-node": 3.844.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-retry": ^4.1.15 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.22 - "@smithy/util-defaults-mode-node": ^4.0.22 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: e2e22ccc5e029f044b7647e87140f6f60c637f3791d1f1b26dd171c8b6db71197004040f83f7228b9a31b92ff746ad82c0575f9f71f32499b8df153883a53feb - languageName: node - linkType: hard - -"@aws-sdk/core@npm:3.826.0, @aws-sdk/core@npm:^3.624.0": - version: 3.826.0 - resolution: "@aws-sdk/core@npm:3.826.0" - dependencies: - "@aws-sdk/types": 3.821.0 - "@aws-sdk/xml-builder": 3.821.0 - "@smithy/core": ^3.5.3 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/signature-v4": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-utf8": ^4.0.0 - fast-xml-parser: 4.4.1 - tslib: ^2.6.2 - checksum: 9e46a3c904ae8f690008e4cc71409fba676914ede6dede3924cc9381daaf4a3c6ba1b98514ec37b2d2e387411a3e94dba4e0e5b4a0dcb2369fbca803e9465fdd - languageName: node - linkType: hard - -"@aws-sdk/core@npm:3.844.0, @aws-sdk/core@npm:^3.709.0": - version: 3.844.0 - resolution: "@aws-sdk/core@npm:3.844.0" - dependencies: - "@aws-sdk/types": 3.840.0 - "@aws-sdk/xml-builder": 3.821.0 - "@smithy/core": ^3.7.0 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/signature-v4": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-utf8": ^4.0.0 - fast-xml-parser: 5.2.5 - tslib: ^2.6.2 - checksum: 28f7397c058a39009965b0e76055c78ff599bce13c8204bd6fcb4f04f1a3a514d009489478e089f8f9b79193b294f3b0240994411bba449230e17947cc2af115 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-cognito-identity@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.830.0" - dependencies: - "@aws-sdk/client-cognito-identity": 3.830.0 - "@aws-sdk/types": 3.821.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: d13fcb94b82a40a498967d9552c3214c40adbe820fed43312c320717655b75b0719c0541222c068918ef5482cc7fc42c219f60ee41661216107c27bf092070fb - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-cognito-identity@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.844.0" - dependencies: - "@aws-sdk/client-cognito-identity": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 38b721be3c5981b9f9408021b418256dcf7ea2401758b2ff60bf1ee134f67238d88f1f9d95e8abef6ede6eaa8a47809345aca247c3be54c0cf158ff729d9ca42 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-env@npm:3.826.0": - version: 3.826.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.826.0" - dependencies: - "@aws-sdk/core": 3.826.0 - "@aws-sdk/types": 3.821.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: ee336310c20cdf60dcce01f9073787286cda52800d102b323b41d9eedc701aa22cdd9aef1c4fb467d765fcb6cf957c87a835a8c431ac98ce8954e8df7f433305 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-env@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.844.0" - dependencies: - "@aws-sdk/core": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 292a9b4c4e5402546ec78b971fa3be8e07e0af7fb795dceb038e5dcdbcccf9da6cba484185764a5895a6d681ea5008966313572be241024f379726634abca60b - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-http@npm:3.826.0": - version: 3.826.0 - resolution: "@aws-sdk/credential-provider-http@npm:3.826.0" - dependencies: - "@aws-sdk/core": 3.826.0 - "@aws-sdk/types": 3.821.0 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/property-provider": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/util-stream": ^4.2.2 - tslib: ^2.6.2 - checksum: 3fa44637b42b33d35ad03bdd690bd3e6d86999547d3a9862e580a3ead1e997d85283845d9c5e878d5df830e01eab7a12b6e9facd10bff1fe00757447e43b4eaf - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-http@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-http@npm:3.844.0" - dependencies: - "@aws-sdk/core": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/util-stream": ^4.2.3 - tslib: ^2.6.2 - checksum: 1b4c6ce7bedd77bfe10d2f9b7384ab5b1e0a110044f14e73da92be9969dfd84e07c25b854e252bb34e4032ed61b7863a9581e6c0b302f9adfb6ac3c69f47a20b - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-ini@npm:3.830.0, @aws-sdk/credential-provider-ini@npm:^3.624.0": - version: 3.830.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.830.0" - dependencies: - "@aws-sdk/core": 3.826.0 - "@aws-sdk/credential-provider-env": 3.826.0 - "@aws-sdk/credential-provider-http": 3.826.0 - "@aws-sdk/credential-provider-process": 3.826.0 - "@aws-sdk/credential-provider-sso": 3.830.0 - "@aws-sdk/credential-provider-web-identity": 3.830.0 - "@aws-sdk/nested-clients": 3.830.0 - "@aws-sdk/types": 3.821.0 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 517859540491263c424e7620f31b326fb125c27d30045d75c84fa1ccd515ac4a7a46e5e05e215470296d62352ce01a875ece7b36e1cefb08d6f648f75c882750 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-ini@npm:3.844.0, @aws-sdk/credential-provider-ini@npm:^3.709.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.844.0" - dependencies: - "@aws-sdk/core": 3.844.0 - "@aws-sdk/credential-provider-env": 3.844.0 - "@aws-sdk/credential-provider-http": 3.844.0 - "@aws-sdk/credential-provider-process": 3.844.0 - "@aws-sdk/credential-provider-sso": 3.844.0 - "@aws-sdk/credential-provider-web-identity": 3.844.0 - "@aws-sdk/nested-clients": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 5f5acce165d5de9ba8f90aba3f43b4efb741d0106ad3d47ad26dc3c8b59c3e77e8c8d8a76dd410338f9459c85527ec6f7be27d9a98766ebb3677dee495af5473 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-node@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.830.0" - dependencies: - "@aws-sdk/credential-provider-env": 3.826.0 - "@aws-sdk/credential-provider-http": 3.826.0 - "@aws-sdk/credential-provider-ini": 3.830.0 - "@aws-sdk/credential-provider-process": 3.826.0 - "@aws-sdk/credential-provider-sso": 3.830.0 - "@aws-sdk/credential-provider-web-identity": 3.830.0 - "@aws-sdk/types": 3.821.0 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 78671b0a2517c76e638ce366ac20bc3d5abc9f9a1073eb555f0cb028b3f0fe436a5cf5c908d2ee7e49b38bc61ed957b3c86b4d862edf92af003fdf9875282a9d - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-node@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.844.0" - dependencies: - "@aws-sdk/credential-provider-env": 3.844.0 - "@aws-sdk/credential-provider-http": 3.844.0 - "@aws-sdk/credential-provider-ini": 3.844.0 - "@aws-sdk/credential-provider-process": 3.844.0 - "@aws-sdk/credential-provider-sso": 3.844.0 - "@aws-sdk/credential-provider-web-identity": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 5b859fb4ff746906053e0686951e268743a2b9e025fa70284ed341956917b47d803a16cd38879cfd124407623969e248a90044202909fe4d281e7e5ee331ec8a - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-process@npm:3.826.0": - version: 3.826.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.826.0" - dependencies: - "@aws-sdk/core": 3.826.0 - "@aws-sdk/types": 3.821.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: a063c0c7e62232afe5c756797b7b6254c18aaba546a342107985e61d459c41f5d4666f225c2e57efd8eb15dfd41a00957826b09ad3d7467c502b315884cd7715 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-process@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.844.0" - dependencies: - "@aws-sdk/core": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 0dbf2fd52d308ecc51a456799f48fcb17094fbe46c4f03f9eff7693dda9c15f3d667637b23e0cc810a6b64f00d41c63eba2419637c3c44af3da27bbfd7c09ed4 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-sso@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.830.0" - dependencies: - "@aws-sdk/client-sso": 3.830.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/token-providers": 3.830.0 - "@aws-sdk/types": 3.821.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: a06c80d268d4543d2c2d36b90d6a378df761a16ec96e3a1e59b6d4e91108267ef4561cc754408d644ee34391dff66f9129bad10b08a850635dc2fac6a78a82d3 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-sso@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.844.0" - dependencies: - "@aws-sdk/client-sso": 3.844.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/token-providers": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 9775b782f12fee185fdc13e047a6cf0940e3b5abec99208500a12707ccff4d1c65f0181bb9494f47968fc1159a657387c014bc41b7c4b906ede8e18f3d78f175 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-web-identity@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.830.0" - dependencies: - "@aws-sdk/core": 3.826.0 - "@aws-sdk/nested-clients": 3.830.0 - "@aws-sdk/types": 3.821.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: a1291869c3eb8e7ba7ed8c4865846b220b7f12c5809045ce863d838e1ee085d868122bd9e8dae707630caafab02b2dd5c805bf37a8ce65f945aef889ec09b479 - languageName: node - linkType: hard - -"@aws-sdk/credential-provider-web-identity@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.844.0" - dependencies: - "@aws-sdk/core": 3.844.0 - "@aws-sdk/nested-clients": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 90186be1fa65421141c62571a059203756c3b79ae198ed0413b4d9970a832738e87c62121a1834bfbce1cd956e35ca15e6db9ca07fe855729fbe89cdea0938f5 - languageName: node - linkType: hard - -"@aws-sdk/credential-providers@npm:^3.624.0": - version: 3.830.0 - resolution: "@aws-sdk/credential-providers@npm:3.830.0" - dependencies: - "@aws-sdk/client-cognito-identity": 3.830.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/credential-provider-cognito-identity": 3.830.0 - "@aws-sdk/credential-provider-env": 3.826.0 - "@aws-sdk/credential-provider-http": 3.826.0 - "@aws-sdk/credential-provider-ini": 3.830.0 - "@aws-sdk/credential-provider-node": 3.830.0 - "@aws-sdk/credential-provider-process": 3.826.0 - "@aws-sdk/credential-provider-sso": 3.830.0 - "@aws-sdk/credential-provider-web-identity": 3.830.0 - "@aws-sdk/nested-clients": 3.830.0 - "@aws-sdk/types": 3.821.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 11135dd16f4a0da7f621bad59a15a70b6b49f6515e5347b534e1d9267ca11db5d0114c750c22eabb84196dbc0f7c6a9e85bba6f76c7588bf5d4f539de00a3a4d - languageName: node - linkType: hard - -"@aws-sdk/credential-providers@npm:^3.709.0": - version: 3.844.0 - resolution: "@aws-sdk/credential-providers@npm:3.844.0" - dependencies: - "@aws-sdk/client-cognito-identity": 3.844.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/credential-provider-cognito-identity": 3.844.0 - "@aws-sdk/credential-provider-env": 3.844.0 - "@aws-sdk/credential-provider-http": 3.844.0 - "@aws-sdk/credential-provider-ini": 3.844.0 - "@aws-sdk/credential-provider-node": 3.844.0 - "@aws-sdk/credential-provider-process": 3.844.0 - "@aws-sdk/credential-provider-sso": 3.844.0 - "@aws-sdk/credential-provider-web-identity": 3.844.0 - "@aws-sdk/nested-clients": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 20f98117e9344297e4c099ed5903dbe006a444cf8bba725bf606fab63c2300b88972aa75a1444607a1c2c366126ecf0ca2e42e8894af800b183f9d193577d5a8 - languageName: node - linkType: hard - -"@aws-sdk/middleware-host-header@npm:3.821.0": - version: 3.821.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.821.0" - dependencies: - "@aws-sdk/types": 3.821.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: b626412b0ccb169542311230d0c16e62c21179802e2d6041b5305b3da02de7c3ea352c892162d416ab0fc92e6cc5fbfbf14671b0dd0c54a36cf33efa91a7fd1e - languageName: node - linkType: hard - -"@aws-sdk/middleware-host-header@npm:3.840.0": - version: 3.840.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.840.0" - dependencies: - "@aws-sdk/types": 3.840.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 8d4a51007aa740daeea1c8427d7f2bf5d91d8fa9bd890ed7212a7460b68878bd651666585ef7cf2f553fe34aac141b1eaa8cd9b3520da0fc62918e7e43473b02 - languageName: node - linkType: hard - -"@aws-sdk/middleware-logger@npm:3.821.0": - version: 3.821.0 - resolution: "@aws-sdk/middleware-logger@npm:3.821.0" - dependencies: - "@aws-sdk/types": 3.821.0 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 1f287c7a9a1cff4413070ff84459c41102dcf063b8cbc90efb67e8ab23beebd9c3814c7e2d59166027520d8f91aaaee39ab66aa53bae129327e509b95ab24085 - languageName: node - linkType: hard - -"@aws-sdk/middleware-logger@npm:3.840.0": - version: 3.840.0 - resolution: "@aws-sdk/middleware-logger@npm:3.840.0" - dependencies: - "@aws-sdk/types": 3.840.0 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 2d9744eb17f969057956008d74a34adc27ee810f8a95e26547b2c8d8987bbe42f585ac6a1d033e341761245cd34c58a670155cfec01ee6ae3d29ed5c1531bc48 - languageName: node - linkType: hard - -"@aws-sdk/middleware-recursion-detection@npm:3.821.0": - version: 3.821.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.821.0" - dependencies: - "@aws-sdk/types": 3.821.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 8abb4c2df2c30507b12d6e002f7d99cac09309b350f07094e4b540b3036dd1d2b073b79882fc6819b15f50837e40e5f05a3bd8547964a7a146a210355d39a85f - languageName: node - linkType: hard - -"@aws-sdk/middleware-recursion-detection@npm:3.840.0": - version: 3.840.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.840.0" - dependencies: - "@aws-sdk/types": 3.840.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: aa8aed9a33edb472dceb5eca4f92af4db814415422282ed9910d60ac585c1e99eaf46fed9b5890d358cee65631708a22014ac558a9404c6bd6487387046e6886 - languageName: node - linkType: hard - -"@aws-sdk/middleware-user-agent@npm:3.828.0": - version: 3.828.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.828.0" - dependencies: - "@aws-sdk/core": 3.826.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@smithy/core": ^3.5.3 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 7856fed617d28f1ba6004d378cce0a68d878d26719873778d6c5f1895c33269ce091e980b60267b4a256ca4d6612b58c796b198d28e3c7181d8f281bd1ee12f9 - languageName: node - linkType: hard - -"@aws-sdk/middleware-user-agent@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.844.0" - dependencies: - "@aws-sdk/core": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@smithy/core": ^3.7.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: c693072fb6b63d88d80b6675ab195b47166d53bcd8ae94c1941d284c08271b1dc9903bc121df074da685e5c302d325572e7444974e15bc1d767e424a46dc8c1c - languageName: node - linkType: hard - -"@aws-sdk/nested-clients@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/nested-clients@npm:3.830.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.826.0 - "@aws-sdk/middleware-host-header": 3.821.0 - "@aws-sdk/middleware-logger": 3.821.0 - "@aws-sdk/middleware-recursion-detection": 3.821.0 - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/region-config-resolver": 3.821.0 - "@aws-sdk/types": 3.821.0 - "@aws-sdk/util-endpoints": 3.828.0 - "@aws-sdk/util-user-agent-browser": 3.821.0 - "@aws-sdk/util-user-agent-node": 3.828.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.5.3 - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-retry": ^4.1.12 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.19 - "@smithy/util-defaults-mode-node": ^4.0.19 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: d9ce3b965905eaebf69fd9e2186acd263da063c05cb6dd72379fe3f72ff955dc35fd23a3ab2719da06f32dc2b7fbaf476c58ae26a538afcc28ebcff425bc3e53 - languageName: node - linkType: hard - -"@aws-sdk/nested-clients@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/nested-clients@npm:3.844.0" - dependencies: - "@aws-crypto/sha256-browser": 5.2.0 - "@aws-crypto/sha256-js": 5.2.0 - "@aws-sdk/core": 3.844.0 - "@aws-sdk/middleware-host-header": 3.840.0 - "@aws-sdk/middleware-logger": 3.840.0 - "@aws-sdk/middleware-recursion-detection": 3.840.0 - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/region-config-resolver": 3.840.0 - "@aws-sdk/types": 3.840.0 - "@aws-sdk/util-endpoints": 3.844.0 - "@aws-sdk/util-user-agent-browser": 3.840.0 - "@aws-sdk/util-user-agent-node": 3.844.0 - "@smithy/config-resolver": ^4.1.4 - "@smithy/core": ^3.7.0 - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/hash-node": ^4.0.4 - "@smithy/invalid-dependency": ^4.0.4 - "@smithy/middleware-content-length": ^4.0.4 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-retry": ^4.1.15 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-body-length-node": ^4.0.0 - "@smithy/util-defaults-mode-browser": ^4.0.22 - "@smithy/util-defaults-mode-node": ^4.0.22 - "@smithy/util-endpoints": ^3.0.6 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: a64f94c2f07a919719c5f9e902874b35f96feef0657462a4f11238f9915c00cf395b4246ccc658684545327c1ad6fad8a89af4b8b69f8da51e9bd048af9129cc - languageName: node - linkType: hard - -"@aws-sdk/region-config-resolver@npm:3.821.0": - version: 3.821.0 - resolution: "@aws-sdk/region-config-resolver@npm:3.821.0" - dependencies: - "@aws-sdk/types": 3.821.0 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 - "@smithy/util-config-provider": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - tslib: ^2.6.2 - checksum: e3688c64180308ef3db8347b13402f5d261d9d033c9c7e912746630c519d30f123e8a89cac87fc0314e798aa2edacf0a01d6fb901a14ca0e3d179058959dcc2f - languageName: node - linkType: hard - -"@aws-sdk/region-config-resolver@npm:3.840.0": - version: 3.840.0 - resolution: "@aws-sdk/region-config-resolver@npm:3.840.0" - dependencies: - "@aws-sdk/types": 3.840.0 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 - "@smithy/util-config-provider": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - tslib: ^2.6.2 - checksum: c0368460299c12da578f03cfcdfb3b0fe5f0c29103e4d49fa7b1323fc4ed6b8059801597d1b68b95967df92397cda8d02fe8326eaa31431c26e0ace30cb0d272 - languageName: node - linkType: hard - -"@aws-sdk/token-providers@npm:3.830.0": - version: 3.830.0 - resolution: "@aws-sdk/token-providers@npm:3.830.0" - dependencies: - "@aws-sdk/core": 3.826.0 - "@aws-sdk/nested-clients": 3.830.0 - "@aws-sdk/types": 3.821.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: fe8681e65621f5d422ba63dfd4f76fc351314e29e1e31fa0a72a2b7e1133d78a3f7e194c4b319b3636e8200914c3c05c75ebcee8674a54f9ef73b3af4baad800 - languageName: node - linkType: hard - -"@aws-sdk/token-providers@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/token-providers@npm:3.844.0" - dependencies: - "@aws-sdk/core": 3.844.0 - "@aws-sdk/nested-clients": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: f7756f7213e52f7736a24e98a848036074d7f9be050fc79d8409cd93f0c8b1d0b44165a321c14a6ce147d94a9b0bfb5eac484f83a8a8385e646b282cdf1db847 - languageName: node - linkType: hard - -"@aws-sdk/types@npm:3.821.0, @aws-sdk/types@npm:^3.222.0": - version: 3.821.0 - resolution: "@aws-sdk/types@npm:3.821.0" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 999e0344b0eea74b5d423b9cc562ae6b00c5c1e3fb7d1f6f86e81cbdb0dfa948cc574cc5ab4e9e875043d559904128807f3e95119b97123a8cc37dd5471f6c35 - languageName: node - linkType: hard - -"@aws-sdk/types@npm:3.840.0": - version: 3.840.0 - resolution: "@aws-sdk/types@npm:3.840.0" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 01c30bb35090b8105a120ac10bfb5adb291e2b07b15813eebc45a25e8febe79bb4c363600f52abd5348e73b5171611f5e7da8d7f7aeafb7cb3c7b22ac83a1cf8 - languageName: node - linkType: hard - -"@aws-sdk/util-endpoints@npm:3.828.0": - version: 3.828.0 - resolution: "@aws-sdk/util-endpoints@npm:3.828.0" - dependencies: - "@aws-sdk/types": 3.821.0 - "@smithy/types": ^4.3.1 - "@smithy/util-endpoints": ^3.0.6 - tslib: ^2.6.2 - checksum: 0d914554b517f9ad4828ea09abece8fbf99c0c10f833b9973b373b89d2d7f21c50f01315512f56d71bf6e23a11993a6c02eef90af8e3dfb66d28cbf4039446b0 - languageName: node - linkType: hard - -"@aws-sdk/util-endpoints@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/util-endpoints@npm:3.844.0" - dependencies: - "@aws-sdk/types": 3.840.0 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-endpoints": ^3.0.6 - tslib: ^2.6.2 - checksum: 8610a2c6c21be65ada7795c892f1c0ffabbe84537d02bb8df18f19373165089c10d82ef0bc4aa4c45179fa8a7d36544586635f762a0fce3af6bfe95d694ff938 - languageName: node - linkType: hard - -"@aws-sdk/util-locate-window@npm:^3.0.0": - version: 3.804.0 - resolution: "@aws-sdk/util-locate-window@npm:3.804.0" - dependencies: - tslib: ^2.6.2 - checksum: 87b384533ba5ceade6e212f5783b6134551ade3ecb413c93ea453c2d5af76651137c4dc7b270b643e8ac810b072119a273790046c31921aaf0f6a664d1a31c99 - languageName: node - linkType: hard - -"@aws-sdk/util-user-agent-browser@npm:3.821.0": - version: 3.821.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.821.0" - dependencies: - "@aws-sdk/types": 3.821.0 - "@smithy/types": ^4.3.1 - bowser: ^2.11.0 - tslib: ^2.6.2 - checksum: 8817cc00dcc032af0a7e270dd9954b7b5f598448d8804a430456276ef560a6b4781aea4ad84377fddff3684417ac4e5925de5574d9a8b0f08832002948c4a508 - languageName: node - linkType: hard - -"@aws-sdk/util-user-agent-browser@npm:3.840.0": - version: 3.840.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.840.0" - dependencies: - "@aws-sdk/types": 3.840.0 - "@smithy/types": ^4.3.1 - bowser: ^2.11.0 - tslib: ^2.6.2 - checksum: eb99a07b7d96f0555aca25f11cd9e2f579e149d102cc78300c47cc0031a40e7ea1d559bfe15b47bccd675d33fe56ee8e4855198d8eb2fb6e9bb6517e10f39700 - languageName: node - linkType: hard - -"@aws-sdk/util-user-agent-node@npm:3.828.0": - version: 3.828.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.828.0" - dependencies: - "@aws-sdk/middleware-user-agent": 3.828.0 - "@aws-sdk/types": 3.821.0 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - peerDependencies: - aws-crt: ">=1.0.0" - peerDependenciesMeta: - aws-crt: - optional: true - checksum: 370c64d532a0a2c5bdac7dd7cc45070542a116c5e2061a59e60f8a14df849be25d1cfedd2d4561d57f239b7beb3190a239cb3d774fca6536a27247e535ddbae8 - languageName: node - linkType: hard - -"@aws-sdk/util-user-agent-node@npm:3.844.0": - version: 3.844.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.844.0" - dependencies: - "@aws-sdk/middleware-user-agent": 3.844.0 - "@aws-sdk/types": 3.840.0 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - peerDependencies: - aws-crt: ">=1.0.0" - peerDependenciesMeta: - aws-crt: - optional: true - checksum: 847ae34d65bd4ccefd4ff0ac1ba9c078f73d8f333003053a23b81348d80f7c25ae68344120f73bf5d1ea0c2c8be7ae743313bb3f089b0de79c7ec1b58c674965 - languageName: node - linkType: hard - -"@aws-sdk/xml-builder@npm:3.821.0": - version: 3.821.0 - resolution: "@aws-sdk/xml-builder@npm:3.821.0" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: f6ee1e5f5336afeb72e2b5e712593d1dcaa626729d0a12941c32e14136308b8729b225d4c75e7b6606bc17eeb79ea28076212aced93cc6460fefb9b712b07e28 - languageName: node - linkType: hard - -"@azure/abort-controller@npm:^2.0.0, @azure/abort-controller@npm:^2.1.2": - version: 2.1.2 - resolution: "@azure/abort-controller@npm:2.1.2" - dependencies: - tslib: ^2.6.2 - checksum: 22176c04ea01498311c6bbd336669f6e3faffad1cbb0c9ebc6ee9c1ff2cf958fd17ce73c7354b99d8bda9fcd311325ece7bee248875279174e3fc460e8b1a63d - languageName: node - linkType: hard - -"@azure/arm-appservice@npm:^16.0.0": - version: 16.0.0 - resolution: "@azure/arm-appservice@npm:16.0.0" - dependencies: - "@azure/abort-controller": ^2.1.2 - "@azure/core-auth": ^1.9.0 - "@azure/core-client": ^1.9.2 - "@azure/core-lro": ^2.5.4 - "@azure/core-paging": ^1.6.2 - "@azure/core-rest-pipeline": ^1.19.0 - tslib: ^2.8.1 - checksum: 1ff8289cc4ab0cdec35527d8ca60f57b01d06b1397975ec604e18bb2c5b5b48b045705aa54d254beacdb3f1ad8808234138b5e96d69175c926b6cb105904eac7 - languageName: node - linkType: hard - -"@azure/core-auth@npm:^1.4.0, @azure/core-auth@npm:^1.8.0, @azure/core-auth@npm:^1.9.0": - version: 1.10.0 - resolution: "@azure/core-auth@npm:1.10.0" - dependencies: - "@azure/abort-controller": ^2.0.0 - "@azure/core-util": ^1.11.0 - tslib: ^2.6.2 - checksum: 373d2dae505f5c7681ebe59f1aae7fb000826400cc8f94f42f567e1e91764d37731c0633c07ee2440088878a03e60b941599317c4c59e1d4627257af2563568f - languageName: node - linkType: hard - -"@azure/core-client@npm:^1.9.2": - version: 1.10.0 - resolution: "@azure/core-client@npm:1.10.0" - dependencies: - "@azure/abort-controller": ^2.0.0 - "@azure/core-auth": ^1.4.0 - "@azure/core-rest-pipeline": ^1.20.0 - "@azure/core-tracing": ^1.0.0 - "@azure/core-util": ^1.6.1 - "@azure/logger": ^1.0.0 - tslib: ^2.6.2 - checksum: 5881e8ad4f0948557149e2ccee9c6d820eaa0fb86d6bf21b4e1149d6038f0a1a4f5ed141561fb40dbeb1a7f1f7c999aedcf4652e6b98e411901819226d2e7124 - languageName: node - linkType: hard - -"@azure/core-lro@npm:^2.5.4": - version: 2.7.2 - resolution: "@azure/core-lro@npm:2.7.2" - dependencies: - "@azure/abort-controller": ^2.0.0 - "@azure/core-util": ^1.2.0 - "@azure/logger": ^1.0.0 - tslib: ^2.6.2 - checksum: dc2e5bbb004a86704bcf584422cd099b7a6beef57ce6501afacced65f4f3b5fbba57a2439f701687237867552a661fd6568f8b3c9e3eacdfd9039004772f85b0 - languageName: node - linkType: hard - -"@azure/core-paging@npm:^1.6.2": - version: 1.6.2 - resolution: "@azure/core-paging@npm:1.6.2" - dependencies: - tslib: ^2.6.2 - checksum: 4b57f953998473ee784c3ea774a8b54f4be0ec239bd43cbabe28113ca18f141455289713302d4fcd802898dd7ab58380ff575b7ce9400ec1ec20c505791c0b25 - languageName: node - linkType: hard - -"@azure/core-rest-pipeline@npm:^1.17.0, @azure/core-rest-pipeline@npm:^1.19.0, @azure/core-rest-pipeline@npm:^1.20.0": - version: 1.22.0 - resolution: "@azure/core-rest-pipeline@npm:1.22.0" - dependencies: - "@azure/abort-controller": ^2.0.0 - "@azure/core-auth": ^1.8.0 - "@azure/core-tracing": ^1.0.1 - "@azure/core-util": ^1.11.0 - "@azure/logger": ^1.0.0 - "@typespec/ts-http-runtime": ^0.3.0 - tslib: ^2.6.2 - checksum: 949b24f4e392f8fd4ba6aeb28ef475ffc80dea831d87f6a22133e8e7c98b118bfa448634677fceacb945869bf888f81ec73252518dbb5168b109515093475449 - languageName: node - linkType: hard - -"@azure/core-tracing@npm:^1.0.0, @azure/core-tracing@npm:^1.0.1": - version: 1.3.0 - resolution: "@azure/core-tracing@npm:1.3.0" - dependencies: - tslib: ^2.6.2 - checksum: b05c064fff4b4e8e1d97becacd4fb7983df269e1ac726228c17560fcb2c33a14018c3d0021a8248d3231beeb5dc8395eb676a9ad78896058b7dd9e348937bcb6 - languageName: node - linkType: hard - -"@azure/core-util@npm:^1.11.0, @azure/core-util@npm:^1.2.0, @azure/core-util@npm:^1.6.1": - version: 1.13.0 - resolution: "@azure/core-util@npm:1.13.0" - dependencies: - "@azure/abort-controller": ^2.0.0 - "@typespec/ts-http-runtime": ^0.3.0 - tslib: ^2.6.2 - checksum: a1e1d3c329e895db22348ff6b225dcffb9f3644c567ef76e60c78f3b0223a997c768a6194ce87e5eefab2cfcf08748c67300cb09ee224470a57feaf8c152f07a - languageName: node - linkType: hard - -"@azure/identity@npm:^4.10.0": - version: 4.10.2 - resolution: "@azure/identity@npm:4.10.2" - dependencies: - "@azure/abort-controller": ^2.0.0 - "@azure/core-auth": ^1.9.0 - "@azure/core-client": ^1.9.2 - "@azure/core-rest-pipeline": ^1.17.0 - "@azure/core-tracing": ^1.0.0 - "@azure/core-util": ^1.11.0 - "@azure/logger": ^1.0.0 - "@azure/msal-browser": ^4.2.0 - "@azure/msal-node": ^3.5.0 - open: ^10.1.0 - tslib: ^2.2.0 - checksum: 6b9459dbea35545a643c9df569f19ad2d455ca8a777074fc8f46b50cf53808132433da47cf05b884ddf85af54b46ae6f4fed8aa99d6ff4a36d032848994e21bd - languageName: node - linkType: hard - -"@azure/logger@npm:^1.0.0": - version: 1.3.0 - resolution: "@azure/logger@npm:1.3.0" - dependencies: - "@typespec/ts-http-runtime": ^0.3.0 - tslib: ^2.6.2 - checksum: 2089429ef3eadc6d4d10c5e12f1cf6580587ac0eed5c7107fc621c56927fc162e150e57668e98092d4ba0dc10c6425ee61971dea67e80022fb3906e9c4efc3a6 - languageName: node - linkType: hard - -"@azure/msal-browser@npm:^4.2.0": - version: 4.15.0 - resolution: "@azure/msal-browser@npm:4.15.0" - dependencies: - "@azure/msal-common": 15.8.1 - checksum: 493026644d409addcd22ee975838f8091996a008418cccf49261a1c7cdc7917143f9d6fedb76b18b1cfe898d61f3f7d660405e2793c8d3351064bb03aeb54f9c - languageName: node - linkType: hard - -"@azure/msal-common@npm:15.8.1": - version: 15.8.1 - resolution: "@azure/msal-common@npm:15.8.1" - checksum: 0ea62b41f59d35ebfb8342c73e814e57d7990b5d66f7f89aaf9df6fd066717862e26815d95d27434770a2336253ab1141012b5d384347631e3af0f6791ef8413 - languageName: node - linkType: hard - -"@azure/msal-node@npm:^3.5.0": - version: 3.6.3 - resolution: "@azure/msal-node@npm:3.6.3" - dependencies: - "@azure/msal-common": 15.8.1 - jsonwebtoken: ^9.0.0 - uuid: ^8.3.0 - checksum: be248a4f6ae1e89ea9e15ccd385f88418a4c5f7ac468cb9e9bc338eb3e906380b3bf61970e51e64bf2d062792a25ff78227c3046b3d33981676d3af5feef24ae - languageName: node - linkType: hard - "@babel/cli@npm:^7.27.2": version: 7.28.0 resolution: "@babel/cli@npm:7.28.0" @@ -3626,137 +2097,210 @@ __metadata: languageName: node linkType: hard -"@datadog/datadog-ci@npm:3.12.0": - version: 3.12.0 - resolution: "@datadog/datadog-ci@npm:3.12.0" - dependencies: - "@aws-sdk/client-cloudwatch-logs": ^3.709.0 - "@aws-sdk/client-iam": ^3.709.0 - "@aws-sdk/client-lambda": ^3.709.0 - "@aws-sdk/client-sfn": ^3.709.0 - "@aws-sdk/core": ^3.709.0 - "@aws-sdk/credential-provider-ini": ^3.709.0 - "@aws-sdk/credential-providers": ^3.709.0 - "@azure/arm-appservice": ^16.0.0 - "@azure/identity": ^4.10.0 - "@google-cloud/logging": ^11.1.0 - "@google-cloud/run": ^2.1.0 - "@smithy/property-provider": ^2.0.12 - "@smithy/util-retry": ^2.0.4 +"@datadog/datadog-ci-base@npm:4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci-base@npm:4.4.0" + dependencies: + "@antfu/install-pkg": ^1.1.0 "@types/datadog-metrics": 0.6.1 - ajv: ^8.12.0 - ajv-formats: ^2.1.1 async-retry: 1.3.1 - axios: ^1.8.4 + axios: ^1.12.1 chalk: 3.0.0 clipanion: ^3.2.1 datadog-metrics: 0.9.3 + debug: ^4.4.1 deep-extend: 0.6.0 - deep-object-diff: ^1.1.9 - fast-deep-equal: ^3.1.3 - fast-levenshtein: ^3.0.0 fast-xml-parser: ^4.4.1 - form-data: ^4.0.3 - fuzzy: ^0.1.3 - get-value: ^4.0.1 - glob: ^10.4.5 - google-auth-library: ^9.12.0 + form-data: ^4.0.4 + glob: ^10.5.0 inquirer: ^8.2.5 - inquirer-checkbox-plus-prompt: ^1.4.2 - js-yaml: 3.13.1 + jest-diff: ^30.2.0 jszip: ^3.10.1 - ora: 5.4.1 - packageurl-js: ^2.0.1 proxy-agent: ^6.4.0 semver: ^7.5.3 - set-value: ^4.1.0 simple-git: 3.16.0 - ssh2: ^1.15.0 - ssh2-streams: 0.4.10 - sshpk: 1.16.1 terminal-link: 2.1.1 tiny-async-pool: ^2.1.0 typanion: ^3.14.0 upath: ^2.0.1 + peerDependencies: + "@datadog/datadog-ci-plugin-aas": 4.4.0 + "@datadog/datadog-ci-plugin-cloud-run": 4.4.0 + "@datadog/datadog-ci-plugin-container-app": 4.4.0 + "@datadog/datadog-ci-plugin-deployment": 4.4.0 + "@datadog/datadog-ci-plugin-dora": 4.4.0 + "@datadog/datadog-ci-plugin-gate": 4.4.0 + "@datadog/datadog-ci-plugin-lambda": 4.4.0 + "@datadog/datadog-ci-plugin-sarif": 4.4.0 + "@datadog/datadog-ci-plugin-sbom": 4.4.0 + "@datadog/datadog-ci-plugin-stepfunctions": 4.4.0 + "@datadog/datadog-ci-plugin-synthetics": 4.4.0 + peerDependenciesMeta: + "@datadog/datadog-ci-plugin-aas": + optional: true + "@datadog/datadog-ci-plugin-cloud-run": + optional: true + "@datadog/datadog-ci-plugin-container-app": + optional: true + "@datadog/datadog-ci-plugin-deployment": + optional: true + "@datadog/datadog-ci-plugin-dora": + optional: true + "@datadog/datadog-ci-plugin-gate": + optional: true + "@datadog/datadog-ci-plugin-lambda": + optional: true + "@datadog/datadog-ci-plugin-sarif": + optional: true + "@datadog/datadog-ci-plugin-sbom": + optional: true + "@datadog/datadog-ci-plugin-stepfunctions": + optional: true + "@datadog/datadog-ci-plugin-synthetics": + optional: true + checksum: f7913a9da35e837681ed89af368a072f74f7bbb69611d1c696182cab26d69b4a18597cce2df55d24b1edfc2244eaab1f9be398db9320176950776eb0020f3e61 + languageName: node + linkType: hard + +"@datadog/datadog-ci-plugin-deployment@npm:4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci-plugin-deployment@npm:4.4.0" + dependencies: + axios: ^1.12.1 + chalk: 3.0.0 + simple-git: 3.16.0 + peerDependencies: + "@datadog/datadog-ci-base": 4.4.0 + checksum: 686cbe3facce31f65c2f22c0228b11bb4245b0e2210be6f831f681673b03a178e97fa0a1974dab83c837ded00adae59df6dcf90978ef63993003105181f77e2a + languageName: node + linkType: hard + +"@datadog/datadog-ci-plugin-dora@npm:4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci-plugin-dora@npm:4.4.0" + dependencies: + axios: ^1.12.1 + chalk: 3.0.0 + simple-git: 3.16.0 + peerDependencies: + "@datadog/datadog-ci-base": 4.4.0 + checksum: f439c0eee5c5aaf828730dc267e9957124579a5e5bb651ec61c3ee21638d1352ae1ed2750b33cf759802f73851963b6a5b03dacdfbf6084c73c05b37d1944c8f + languageName: node + linkType: hard + +"@datadog/datadog-ci-plugin-gate@npm:4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci-plugin-gate@npm:4.4.0" + dependencies: + "@types/uuid": ^9.0.2 + axios: ^1.12.1 + chalk: 3.0.0 uuid: ^9.0.0 - ws: ^7.5.10 - xml2js: 0.5.0 - yamux-js: 0.1.2 - bin: - datadog-ci: dist/cli.js - checksum: 8d1be1ac850ec423a842ee35d303b99376b91b79ce387c7bfbe951a6371b565ebef70cad9e3e3381d0a22cf11cba217cfbd17f8ad1434b93a1a67c1b0646ad5a + peerDependencies: + "@datadog/datadog-ci-base": 4.4.0 + checksum: 3914240ce778d8f41511ec8d1e8d95fc8e9c2c69c99c76365d99c1e6663d13ce5ef37e9ef71eec8e02517f4a7e2bb15370058830e6a78fab21c8fd3e75421aa1 languageName: node linkType: hard -"@datadog/datadog-ci@npm:^2.39.0": - version: 2.48.0 - resolution: "@datadog/datadog-ci@npm:2.48.0" +"@datadog/datadog-ci-plugin-sarif@npm:4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci-plugin-sarif@npm:4.4.0" dependencies: - "@aws-sdk/client-cloudwatch-logs": ^3.624.0 - "@aws-sdk/client-iam": ^3.624.0 - "@aws-sdk/client-lambda": ^3.624.0 - "@aws-sdk/client-sfn": ^3.624.0 - "@aws-sdk/core": ^3.624.0 - "@aws-sdk/credential-provider-ini": ^3.624.0 - "@aws-sdk/credential-providers": ^3.624.0 - "@google-cloud/logging": ^11.1.0 - "@google-cloud/run": ^1.4.0 - "@smithy/property-provider": ^2.0.12 - "@smithy/util-retry": ^2.0.4 - "@types/datadog-metrics": 0.6.1 ajv: ^8.12.0 ajv-formats: ^2.1.1 - async-retry: 1.3.1 - axios: ^1.7.4 + axios: ^1.12.1 chalk: 3.0.0 - clipanion: ^3.2.1 - datadog-metrics: 0.9.3 - deep-extend: 0.6.0 - deep-object-diff: ^1.1.9 - fast-levenshtein: ^3.0.0 - fast-xml-parser: ^4.4.1 - form-data: 4.0.0 - fuzzy: ^0.1.3 - glob: ^7.1.4 - google-auth-library: ^9.12.0 - inquirer: ^8.2.5 - inquirer-checkbox-plus-prompt: ^1.4.2 - js-yaml: 3.13.1 - jszip: ^3.10.1 - ora: 5.4.1 - packageurl-js: ^2.0.1 - proxy-agent: ^6.4.0 - rimraf: ^3.0.2 - semver: ^7.5.3 + form-data: ^4.0.4 simple-git: 3.16.0 - ssh2: ^1.15.0 - ssh2-streams: 0.4.10 - sshpk: 1.16.1 - terminal-link: 2.1.1 - tiny-async-pool: ^2.1.0 - typanion: ^3.14.0 + upath: ^2.0.1 uuid: ^9.0.0 - ws: ^7.5.10 - xml2js: 0.5.0 - yamux-js: 0.1.2 - bin: - datadog-ci: dist/cli.js - checksum: e8eafa28eafa1e9397eb8c23dc50540eafbcc1988bfd01a1c50e16423dd51459c6d38fd3c1479aeee8b105a74ceb687a01271f0e4a645032201e959b13a3cd14 + peerDependencies: + "@datadog/datadog-ci-base": 4.4.0 + checksum: aa3649093d9604b8ff49245616e261ef33bc380a42599589f8c1867a6a2cce7e52a82625ec21c01a7ff591bd8433aacc0ea56df93e5abeac0b6f2f737745c2b7 languageName: node linkType: hard -"@datadog/libdatadog@npm:^0.6.0": - version: 0.6.0 - resolution: "@datadog/libdatadog@npm:0.6.0" - checksum: 634bd4e10ce76bed00c8096ac5a9cb2bed5b33547ce5e27b020deeb7dba5a3e13cdd26d505e8bbd6252cc00f4ec9b2c7b62e3b24db31a1a95afcfd83a6e532a6 +"@datadog/datadog-ci-plugin-sbom@npm:4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci-plugin-sbom@npm:4.4.0" + dependencies: + ajv: ^8.12.0 + ajv-formats: ^2.1.1 + axios: ^1.12.1 + chalk: 3.0.0 + packageurl-js: ^2.0.1 + simple-git: 3.16.0 + upath: ^2.0.1 + peerDependencies: + "@datadog/datadog-ci-base": 4.4.0 + checksum: 66468611bf5456073475e89274b40caa337e4fa265976c8339983353662413b31fcedc60291b1af875980242c95dba0a9633292b0338af4a5843b0f652b72bdc languageName: node linkType: hard -"@datadog/mobile-react-native-apollo-client@workspace:packages/react-native-apollo-client": - version: 0.0.0-use.local - resolution: "@datadog/mobile-react-native-apollo-client@workspace:packages/react-native-apollo-client" +"@datadog/datadog-ci-plugin-synthetics@npm:4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci-plugin-synthetics@npm:4.4.0" dependencies: - "@apollo/client": ^3.8.3 + axios: ^1.12.1 + chalk: 3.0.0 + debug: ^4.4.1 + deep-extend: 0.6.0 + fast-levenshtein: ^3.0.0 + get-value: ^4.0.1 + ora: 5.4.1 + set-value: ^4.1.0 + ssh2: ^1.16.0 + sshpk: 1.16.1 + upath: ^2.0.1 + ws: ^7.5.10 + xml2js: 0.5.0 + yamux-js: 0.1.2 + peerDependencies: + "@datadog/datadog-ci-base": 4.4.0 + checksum: 78062b761a9fbf43952ea3bbf63836924a9bf9e65f6b327b2ad4779b58d1829cf23b34245a6973d8923e14a35b3aa57619c169c4c25ea1e3d57def56fda0c7f6 + languageName: node + linkType: hard + +"@datadog/datadog-ci@npm:^4.4.0": + version: 4.4.0 + resolution: "@datadog/datadog-ci@npm:4.4.0" + dependencies: + "@datadog/datadog-ci-base": 4.4.0 + "@datadog/datadog-ci-plugin-deployment": 4.4.0 + "@datadog/datadog-ci-plugin-dora": 4.4.0 + "@datadog/datadog-ci-plugin-gate": 4.4.0 + "@datadog/datadog-ci-plugin-sarif": 4.4.0 + "@datadog/datadog-ci-plugin-sbom": 4.4.0 + "@datadog/datadog-ci-plugin-synthetics": 4.4.0 + axios: ^1.12.1 + chalk: 3.0.0 + clipanion: ^3.2.1 + fast-xml-parser: ^4.4.1 + form-data: ^4.0.4 + js-yaml: 4.1.1 + semver: ^7.5.3 + simple-git: 3.16.0 + typanion: ^3.14.0 + upath: ^2.0.1 + uuid: ^9.0.0 + bin: + datadog-ci: dist/cli.js + checksum: 402860c3aef9ffbd85c94f0e278bc635255b5747f484c1e5d15b502d897929ee5a27320365d4e97705c6f3e3be636cf0cefd2fede65cd91ef5198bcf5d80fba0 + languageName: node + linkType: hard + +"@datadog/libdatadog@npm:^0.6.0": + version: 0.6.0 + resolution: "@datadog/libdatadog@npm:0.6.0" + checksum: 634bd4e10ce76bed00c8096ac5a9cb2bed5b33547ce5e27b020deeb7dba5a3e13cdd26d505e8bbd6252cc00f4ec9b2c7b62e3b24db31a1a95afcfd83a6e532a6 + languageName: node + linkType: hard + +"@datadog/mobile-react-native-apollo-client@workspace:packages/react-native-apollo-client": + version: 0.0.0-use.local + resolution: "@datadog/mobile-react-native-apollo-client@workspace:packages/react-native-apollo-client" + dependencies: + "@apollo/client": ^3.8.3 "@testing-library/react-native": 7.0.2 graphql: ^16.8.0 react-native-builder-bob: 0.26.0 @@ -4070,89 +2614,6 @@ __metadata: languageName: node linkType: hard -"@google-cloud/common@npm:^5.0.0": - version: 5.0.2 - resolution: "@google-cloud/common@npm:5.0.2" - dependencies: - "@google-cloud/projectify": ^4.0.0 - "@google-cloud/promisify": ^4.0.0 - arrify: ^2.0.1 - duplexify: ^4.1.1 - extend: ^3.0.2 - google-auth-library: ^9.0.0 - html-entities: ^2.5.2 - retry-request: ^7.0.0 - teeny-request: ^9.0.0 - checksum: 13c3af95830c1410edb52b9a1bb8cbaf1b47e63be6049eae9c06b728225fd59f6acce1d8cdba575c14a2bb7e929acf9320bf8aec3f67409d920143a90a69dc53 - languageName: node - linkType: hard - -"@google-cloud/logging@npm:^11.1.0": - version: 11.2.0 - resolution: "@google-cloud/logging@npm:11.2.0" - dependencies: - "@google-cloud/common": ^5.0.0 - "@google-cloud/paginator": ^5.0.0 - "@google-cloud/projectify": ^4.0.0 - "@google-cloud/promisify": ^4.0.0 - "@opentelemetry/api": ^1.7.0 - arrify: ^2.0.1 - dot-prop: ^6.0.0 - eventid: ^2.0.0 - extend: ^3.0.2 - gcp-metadata: ^6.0.0 - google-auth-library: ^9.0.0 - google-gax: ^4.0.3 - on-finished: ^2.3.0 - pumpify: ^2.0.1 - stream-events: ^1.0.5 - uuid: ^9.0.0 - checksum: 7e6be3870ebec95438da197bfa1b3c0249251acb8eb4f2cf9ba207463a4ea6c50e2159fa4e60f8e250199619aed1c1c3a77a086d8799ba5b88d216d893361741 - languageName: node - linkType: hard - -"@google-cloud/paginator@npm:^5.0.0": - version: 5.0.2 - resolution: "@google-cloud/paginator@npm:5.0.2" - dependencies: - arrify: ^2.0.0 - extend: ^3.0.2 - checksum: eeb4a387807270ba9f69f22d7439d60c5bd6663573c2da9ea7d998c373d77671d77450b87f0f229c28418df654af4064e70554fa4dcde7edb3c0f5c05f208246 - languageName: node - linkType: hard - -"@google-cloud/projectify@npm:^4.0.0": - version: 4.0.0 - resolution: "@google-cloud/projectify@npm:4.0.0" - checksum: 973d28414ae200433333a3c315aebb881ced42ea4afe6f3f8520d2fecded75e76c913f5189fea8fb29ce6ca36117c4f44001b3c503eecdd3ac7f02597a98354a - languageName: node - linkType: hard - -"@google-cloud/promisify@npm:^4.0.0": - version: 4.1.0 - resolution: "@google-cloud/promisify@npm:4.1.0" - checksum: 96d18c594e3701f5d171966dda625f183107192d6d5feee0dc53b7a7755c93f1158c873878890bffdd8b80e50da8439ec6d03e169042534cb9bd2cdc45c29fff - languageName: node - linkType: hard - -"@google-cloud/run@npm:^1.4.0": - version: 1.5.1 - resolution: "@google-cloud/run@npm:1.5.1" - dependencies: - google-gax: ^4.0.3 - checksum: 8bbdef3aa173cc5d29d41e4a5f44a3d00863acc188b5196edd1d8dc18254e753975ec62ac11b241b362fdad6c934451cb50db0bd828bcaa70d07648b11a2baf0 - languageName: node - linkType: hard - -"@google-cloud/run@npm:^2.1.0": - version: 2.2.0 - resolution: "@google-cloud/run@npm:2.2.0" - dependencies: - google-gax: ^5.0.0 - checksum: 1f315d38eda49eec001c16a9c4e227d2fd9f7ece55682730800e367d48a1729cf0ffcc62982e942f7fab94f92b923fc1f152c97e0b18d6a0e0ac77f8c2770550 - languageName: node - linkType: hard - "@graphql-typed-document-node/core@npm:^3.1.1": version: 3.2.0 resolution: "@graphql-typed-document-node/core@npm:3.2.0" @@ -4162,30 +2623,6 @@ __metadata: languageName: node linkType: hard -"@grpc/grpc-js@npm:^1.10.9, @grpc/grpc-js@npm:^1.12.6": - version: 1.13.4 - resolution: "@grpc/grpc-js@npm:1.13.4" - dependencies: - "@grpc/proto-loader": ^0.7.13 - "@js-sdsl/ordered-map": ^4.4.2 - checksum: fe5db84bbbcd07cc1b68d1683b7fbe9cfcc5c3a60655ecc17fb3e1cd2adc4c1ce891b15e6e9a9c2140f6891def6f93b509a60d2bce253d13b317f9136e968451 - languageName: node - linkType: hard - -"@grpc/proto-loader@npm:^0.7.13": - version: 0.7.15 - resolution: "@grpc/proto-loader@npm:0.7.15" - dependencies: - lodash.camelcase: ^4.3.0 - long: ^5.0.0 - protobufjs: ^7.2.5 - yargs: ^17.7.2 - bin: - proto-loader-gen-types: build/bin/proto-loader-gen-types.js - checksum: 9f19f4c611a17cd33aec0d6e3686a76696495f40593f7c284933c4b7877f58dfa5a225ddc20705860a632311f4dc0d143cb6a0da7b51b6f5ffd7de26938df308 - languageName: node - linkType: hard - "@hapi/hoek@npm:^9.0.0, @hapi/hoek@npm:^9.3.0": version: 9.3.0 resolution: "@hapi/hoek@npm:9.3.0" @@ -4960,13 +3397,6 @@ __metadata: languageName: node linkType: hard -"@js-sdsl/ordered-map@npm:^4.4.2": - version: 4.4.2 - resolution: "@js-sdsl/ordered-map@npm:4.4.2" - checksum: a927ae4ff8565ecb75355cc6886a4f8fadbf2af1268143c96c0cce3ba01261d241c3f4ba77f21f3f017a00f91dfe9e0673e95f830255945c80a0e96c6d30508a - languageName: node - linkType: hard - "@kwsites/file-exists@npm:^1.1.1": version: 1.1.1 resolution: "@kwsites/file-exists@npm:1.1.1" @@ -4983,14 +3413,14 @@ __metadata: languageName: node linkType: hard -"@lerna/create@npm:9.0.0": - version: 9.0.0 - resolution: "@lerna/create@npm:9.0.0" +"@lerna/create@npm:9.0.3": + version: 9.0.3 + resolution: "@lerna/create@npm:9.0.3" dependencies: - "@npmcli/arborist": 9.1.4 - "@npmcli/package-json": 7.0.0 - "@npmcli/run-script": 10.0.0 - "@nx/devkit": ">=21.5.2 < 22.0.0" + "@npmcli/arborist": 9.1.6 + "@npmcli/package-json": 7.0.2 + "@npmcli/run-script": 10.0.2 + "@nx/devkit": ">=21.5.2 < 23.0.0" "@octokit/plugin-enterprise-rest": 6.0.1 "@octokit/rest": 20.1.2 aproba: 2.0.0 @@ -5015,17 +3445,17 @@ __metadata: inquirer: 12.9.6 is-ci: 3.0.1 is-stream: 2.0.0 - js-yaml: 4.1.0 - libnpmpublish: 11.1.0 + js-yaml: 4.1.1 + libnpmpublish: 11.1.2 load-json-file: 6.2.0 make-dir: 4.0.0 make-fetch-happen: 15.0.2 minimatch: 3.0.5 multimatch: 5.0.0 - npm-package-arg: 13.0.0 - npm-packlist: 10.0.1 - npm-registry-fetch: 19.0.0 - nx: ">=21.5.3 < 22.0.0" + npm-package-arg: 13.0.1 + npm-packlist: 10.0.3 + npm-registry-fetch: 19.1.0 + nx: ">=21.5.3 < 23.0.0" p-map: 4.0.0 p-map-series: 2.1.0 p-queue: 6.6.2 @@ -5054,7 +3484,7 @@ __metadata: write-pkg: 4.0.0 yargs: 17.7.2 yargs-parser: 21.1.1 - checksum: 6b0df6039a449dfde2619e8f0163e9ccf7d80b99f88560e5cfbb82b61e4764cdf5784d32470242ed94d227488816e45932bb2705dfa0f1a371769af01c9cc298 + checksum: 6d3b3f9250f8c4b001d54e605f039f999033e48424425e30ba8bd11a612d1159f825bc0dee1de9e2c4facdcaf441269ee115fb311b1c4602e03fd1e83f151669 languageName: node linkType: hard @@ -5145,47 +3575,46 @@ __metadata: languageName: node linkType: hard -"@npmcli/arborist@npm:9.1.4": - version: 9.1.4 - resolution: "@npmcli/arborist@npm:9.1.4" +"@npmcli/arborist@npm:^9.1.8": + version: 9.1.9 + resolution: "@npmcli/arborist@npm:9.1.9" dependencies: "@isaacs/string-locale-compare": ^1.1.0 - "@npmcli/fs": ^4.0.0 - "@npmcli/installed-package-contents": ^3.0.0 - "@npmcli/map-workspaces": ^4.0.1 - "@npmcli/metavuln-calculator": ^9.0.0 - "@npmcli/name-from-folder": ^3.0.0 - "@npmcli/node-gyp": ^4.0.0 - "@npmcli/package-json": ^6.0.1 - "@npmcli/query": ^4.0.0 - "@npmcli/redact": ^3.0.0 - "@npmcli/run-script": ^9.0.1 - bin-links: ^5.0.0 - cacache: ^19.0.1 + "@npmcli/fs": ^5.0.0 + "@npmcli/installed-package-contents": ^4.0.0 + "@npmcli/map-workspaces": ^5.0.0 + "@npmcli/metavuln-calculator": ^9.0.2 + "@npmcli/name-from-folder": ^4.0.0 + "@npmcli/node-gyp": ^5.0.0 + "@npmcli/package-json": ^7.0.0 + "@npmcli/query": ^5.0.0 + "@npmcli/redact": ^4.0.0 + "@npmcli/run-script": ^10.0.0 + bin-links: ^6.0.0 + cacache: ^20.0.1 common-ancestor-path: ^1.0.1 - hosted-git-info: ^8.0.0 + hosted-git-info: ^9.0.0 json-stringify-nice: ^1.1.4 - lru-cache: ^10.2.2 - minimatch: ^9.0.4 - nopt: ^8.0.0 - npm-install-checks: ^7.1.0 - npm-package-arg: ^12.0.0 - npm-pick-manifest: ^10.0.0 - npm-registry-fetch: ^18.0.1 - pacote: ^21.0.0 - parse-conflict-json: ^4.0.0 - proc-log: ^5.0.0 - proggy: ^3.0.0 + lru-cache: ^11.2.1 + minimatch: ^10.0.3 + nopt: ^9.0.0 + npm-install-checks: ^8.0.0 + npm-package-arg: ^13.0.0 + npm-pick-manifest: ^11.0.1 + npm-registry-fetch: ^19.0.0 + pacote: ^21.0.2 + parse-conflict-json: ^5.0.1 + proc-log: ^6.0.0 + proggy: ^4.0.0 promise-all-reject-late: ^1.0.0 promise-call-limit: ^3.0.1 - read-package-json-fast: ^4.0.0 semver: ^7.3.7 - ssri: ^12.0.0 + ssri: ^13.0.0 treeverse: ^3.0.0 walk-up-path: ^4.0.0 bin: arborist: bin/index.js - checksum: 987468d3e4219501e747168dea0824668351f9c070d3737be6c6df6de9544864ad3eefb838e8784060d5a69068d3300dea32d8ca3934ddec1607a920c779a2b8 + checksum: c1b003446534be9cce5b0d86ef70ff99c4214a32a7346d49ba542395c005fd989fcca0f5d081d31e6867df51de789881ee759c9a9d9660c890ef461f956136e1 languageName: node linkType: hard @@ -5198,6 +3627,15 @@ __metadata: languageName: node linkType: hard +"@npmcli/fs@npm:^5.0.0": + version: 5.0.0 + resolution: "@npmcli/fs@npm:5.0.0" + dependencies: + semver: ^7.3.5 + checksum: 897dac32eb37e011800112d406b9ea2ebd96f1dab01bb8fbeb59191b86f6825dffed6a89f3b6c824753d10f8735b76d630927bd7610e9e123b129ef2e5f02cb5 + languageName: node + linkType: hard + "@npmcli/git@npm:^6.0.0": version: 6.0.3 resolution: "@npmcli/git@npm:6.0.3" @@ -5242,19 +3680,31 @@ __metadata: languageName: node linkType: hard -"@npmcli/map-workspaces@npm:^4.0.1": - version: 4.0.2 - resolution: "@npmcli/map-workspaces@npm:4.0.2" +"@npmcli/installed-package-contents@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/installed-package-contents@npm:4.0.0" dependencies: - "@npmcli/name-from-folder": ^3.0.0 - "@npmcli/package-json": ^6.0.0 - glob: ^10.2.2 - minimatch: ^9.0.0 - checksum: 1dba46e94b1e53b59e9b735f89b93ba39c5925120d3f27acb122033833c0e36e5017ab5bdbb0b5dc190300a4359eb4ef9c3539e36e7e2484875b9ac0c75fcfd6 + npm-bundled: ^5.0.0 + npm-normalize-package-bin: ^5.0.0 + bin: + installed-package-contents: bin/index.js + checksum: c94aa13d9f80849221b2bd4eb06f9b7db65dac74ac6176f2dbcba6810c4083240dacddb293a38cfcd5d8c09932aef983b7e01ef434e53d63416a5e8471804fdd + languageName: node + linkType: hard + +"@npmcli/map-workspaces@npm:^5.0.0": + version: 5.0.3 + resolution: "@npmcli/map-workspaces@npm:5.0.3" + dependencies: + "@npmcli/name-from-folder": ^4.0.0 + "@npmcli/package-json": ^7.0.0 + glob: ^13.0.0 + minimatch: ^10.0.3 + checksum: a6f68fadd12b85ef8f955dc9fd9072ff0a9845f3f9f8a255db92b4a4749a6300afea4725a8518dea34d2bfa944cd0014d64845599d3ba5739f7499595a388428 languageName: node linkType: hard -"@npmcli/metavuln-calculator@npm:^9.0.0": +"@npmcli/metavuln-calculator@npm:^9.0.2": version: 9.0.3 resolution: "@npmcli/metavuln-calculator@npm:9.0.3" dependencies: @@ -5267,17 +3717,10 @@ __metadata: languageName: node linkType: hard -"@npmcli/name-from-folder@npm:^3.0.0": - version: 3.0.0 - resolution: "@npmcli/name-from-folder@npm:3.0.0" - checksum: 1b56429f56c8228bf0eaea8298627b36e383930800a49c9445ae4500b905c98eae1d5f506042a36f49d863d5b79f2aadd154a03d9862dc381ce3fabadcb46e70 - languageName: node - linkType: hard - -"@npmcli/node-gyp@npm:^4.0.0": +"@npmcli/name-from-folder@npm:^4.0.0": version: 4.0.0 - resolution: "@npmcli/node-gyp@npm:4.0.0" - checksum: ea4ac6aa273d762a540841315c59c61f3e4ef182c29b1295c30f287cd9d0e33650cd60d626cdce38caf5cff43a5848ea6c213bad5f884110fc90beb167ccbc46 + resolution: "@npmcli/name-from-folder@npm:4.0.0" + checksum: 8fa63a74f041b91394bb10118265fca09976ff196335e51ef00d45f54e246868430cb7a2863a06b54b2a25572f4fab9f499b6c0a8d6c969956ddd0cc00798ac6 languageName: node linkType: hard @@ -5288,37 +3731,7 @@ __metadata: languageName: node linkType: hard -"@npmcli/package-json@npm:7.0.0": - version: 7.0.0 - resolution: "@npmcli/package-json@npm:7.0.0" - dependencies: - "@npmcli/git": ^6.0.0 - glob: ^11.0.3 - hosted-git-info: ^9.0.0 - json-parse-even-better-errors: ^4.0.0 - proc-log: ^5.0.0 - semver: ^7.5.3 - validate-npm-package-license: ^3.0.4 - checksum: 8ca952021a8d418e25ac32177b1c35e42fb2af3f5039432a6f7dc6811ab1c1dbe11d0f97a3c876dce08aaea33267193da344a979071f194f915a2a374de37f14 - languageName: node - linkType: hard - -"@npmcli/package-json@npm:^6.0.0, @npmcli/package-json@npm:^6.0.1, @npmcli/package-json@npm:^6.2.0": - version: 6.2.0 - resolution: "@npmcli/package-json@npm:6.2.0" - dependencies: - "@npmcli/git": ^6.0.0 - glob: ^10.2.2 - hosted-git-info: ^8.0.0 - json-parse-even-better-errors: ^4.0.0 - proc-log: ^5.0.0 - semver: ^7.5.3 - validate-npm-package-license: ^3.0.4 - checksum: 4aee5d2c27c9704c313ff207947fd43d28c76cc3bed9d8004046d4b0989b7a67718173259f45ba1647cf439b3675b50a052e67d5a68f0c34899db8d131c45113 - languageName: node - linkType: hard - -"@npmcli/package-json@npm:^7.0.0": +"@npmcli/package-json@npm:7.0.2, @npmcli/package-json@npm:^7.0.0": version: 7.0.2 resolution: "@npmcli/package-json@npm:7.0.2" dependencies: @@ -5351,12 +3764,12 @@ __metadata: languageName: node linkType: hard -"@npmcli/query@npm:^4.0.0": - version: 4.0.1 - resolution: "@npmcli/query@npm:4.0.1" +"@npmcli/query@npm:^5.0.0": + version: 5.0.0 + resolution: "@npmcli/query@npm:5.0.0" dependencies: postcss-selector-parser: ^7.0.0 - checksum: d648db388b94fe177b6b05a6602f917ed30a8ead9c85b96f2e2585d5b90d62de316f3a294e5301dcb7eb4c947a77119e28ba8d42b2bc48dad8a785e2271a6ea8 + checksum: 7c1cbfb7787b977f5476f8b15c9f9a759f90ad098e061e32df2bfdb91940977ce34c807bff114de4edb9bdfb4eda70b5e299256498bbe9d693aa0f87a065f627 languageName: node linkType: hard @@ -5367,21 +3780,14 @@ __metadata: languageName: node linkType: hard -"@npmcli/run-script@npm:10.0.0": - version: 10.0.0 - resolution: "@npmcli/run-script@npm:10.0.0" - dependencies: - "@npmcli/node-gyp": ^4.0.0 - "@npmcli/package-json": ^7.0.0 - "@npmcli/promise-spawn": ^8.0.0 - node-gyp: ^11.0.0 - proc-log: ^5.0.0 - which: ^5.0.0 - checksum: cad0d870d4d6f5fad0d0eb188a7030ccbfdec410dd69b762a28ea3747665878c3ac9c4528c9099c7dc13c351bfb051d2afec158c79ba6379399e73dc74202327 +"@npmcli/redact@npm:^4.0.0": + version: 4.0.0 + resolution: "@npmcli/redact@npm:4.0.0" + checksum: 4e029c44a8593304bb1aa5a8f1559cb8f37b4dc3880c589ce546da0b8cfa741d16a054db38ee309e81c2120c148ba33edbb3252f97a78b3234ba9ab3fa3e176c languageName: node linkType: hard -"@npmcli/run-script@npm:^10.0.0": +"@npmcli/run-script@npm:10.0.2, @npmcli/run-script@npm:^10.0.0": version: 10.0.2 resolution: "@npmcli/run-script@npm:10.0.2" dependencies: @@ -5395,103 +3801,89 @@ __metadata: languageName: node linkType: hard -"@npmcli/run-script@npm:^9.0.1": - version: 9.1.0 - resolution: "@npmcli/run-script@npm:9.1.0" - dependencies: - "@npmcli/node-gyp": ^4.0.0 - "@npmcli/package-json": ^6.0.0 - "@npmcli/promise-spawn": ^8.0.0 - node-gyp: ^11.0.0 - proc-log: ^5.0.0 - which: ^5.0.0 - checksum: b9965b3d9212459a97e820dbe28b7e46312c2ecddb72b85383e3f72b73453416a1ddfb4b5bac92b9a8508e9817621141caf8e60967de3e158c758cd280494c79 - languageName: node - linkType: hard - -"@nx/devkit@npm:>=21.5.2 < 22.0.0": - version: 21.6.8 - resolution: "@nx/devkit@npm:21.6.8" +"@nx/devkit@npm:>=21.5.2 < 23.0.0": + version: 22.2.5 + resolution: "@nx/devkit@npm:22.2.5" dependencies: + "@zkochan/js-yaml": 0.0.7 ejs: ^3.1.7 enquirer: ~2.3.6 - ignore: ^5.0.4 minimatch: 9.0.3 - semver: ^7.5.3 + semver: ^7.6.3 tslib: ^2.3.0 yargs-parser: 21.1.1 peerDependencies: - nx: ">= 20 <= 22" - checksum: e484cea83bcad4162c80218e7b9a958d0f116ef9d0c2816ca795c534394ac2fdcc245fa3f421cfc4f98521ebbd8e916b6e65628c110b5bc67d80eddcaeb05f13 + nx: ">= 21 <= 23 || ^22.0.0-0" + checksum: f69d98a2cf8c991d32679d0e23d409eec9fb704ed8aebabd5b0e1ea27bba7611349657826f6d332fb0432c51f2b59659eed9ba4bd36e5db8abb1728b956f411b languageName: node linkType: hard -"@nx/nx-darwin-arm64@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-darwin-arm64@npm:21.6.8" +"@nx/nx-darwin-arm64@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-darwin-arm64@npm:22.2.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@nx/nx-darwin-x64@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-darwin-x64@npm:21.6.8" +"@nx/nx-darwin-x64@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-darwin-x64@npm:22.2.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@nx/nx-freebsd-x64@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-freebsd-x64@npm:21.6.8" +"@nx/nx-freebsd-x64@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-freebsd-x64@npm:22.2.5" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@nx/nx-linux-arm-gnueabihf@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-linux-arm-gnueabihf@npm:21.6.8" +"@nx/nx-linux-arm-gnueabihf@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-linux-arm-gnueabihf@npm:22.2.5" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@nx/nx-linux-arm64-gnu@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-linux-arm64-gnu@npm:21.6.8" +"@nx/nx-linux-arm64-gnu@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-linux-arm64-gnu@npm:22.2.5" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@nx/nx-linux-arm64-musl@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-linux-arm64-musl@npm:21.6.8" +"@nx/nx-linux-arm64-musl@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-linux-arm64-musl@npm:22.2.5" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@nx/nx-linux-x64-gnu@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-linux-x64-gnu@npm:21.6.8" +"@nx/nx-linux-x64-gnu@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-linux-x64-gnu@npm:22.2.5" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@nx/nx-linux-x64-musl@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-linux-x64-musl@npm:21.6.8" +"@nx/nx-linux-x64-musl@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-linux-x64-musl@npm:22.2.5" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@nx/nx-win32-arm64-msvc@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-win32-arm64-msvc@npm:21.6.8" +"@nx/nx-win32-arm64-msvc@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-win32-arm64-msvc@npm:22.2.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@nx/nx-win32-x64-msvc@npm:21.6.8": - version: 21.6.8 - resolution: "@nx/nx-win32-x64-msvc@npm:21.6.8" +"@nx/nx-win32-x64-msvc@npm:22.2.5": + version: 22.2.5 + resolution: "@nx/nx-win32-x64-msvc@npm:22.2.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -5635,13 +4027,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api@npm:^1.7.0": - version: 1.9.0 - resolution: "@opentelemetry/api@npm:1.9.0" - checksum: 9e88e59d53ced668f3daaecfd721071c5b85a67dd386f1c6f051d1be54375d850016c881f656ffbe9a03bedae85f7e89c2f2b635313f9c9b195ad033cdc31020 - languageName: node - linkType: hard - "@opentelemetry/core@npm:^1.14.0": version: 1.30.1 resolution: "@opentelemetry/core@npm:1.30.1" @@ -6826,15 +5211,6 @@ __metadata: languageName: node linkType: hard -"@sigstore/bundle@npm:^3.1.0": - version: 3.1.0 - resolution: "@sigstore/bundle@npm:3.1.0" - dependencies: - "@sigstore/protobuf-specs": ^0.4.0 - checksum: 8f560c77eb1cc45785afb1bcb6adfb2af499523038941a92c65fd80924b055bacc0266fa973407c275dbdcf48a976f215f627fea5c9d8f68bd618cfe247393aa - languageName: node - linkType: hard - "@sigstore/bundle@npm:^4.0.0": version: 4.0.0 resolution: "@sigstore/bundle@npm:4.0.0" @@ -6844,13 +5220,6 @@ __metadata: languageName: node linkType: hard -"@sigstore/core@npm:^2.0.0": - version: 2.0.0 - resolution: "@sigstore/core@npm:2.0.0" - checksum: fd21df6ce574ef8fed855955ce864523368bdca8202ed9d90f2b4822f4889315a23f52eef72cbf09534af669329c8affdd36a3615c9598eb9311a4cc22f3f21a - languageName: node - linkType: hard - "@sigstore/core@npm:^3.0.0": version: 3.0.0 resolution: "@sigstore/core@npm:3.0.0" @@ -6858,13 +5227,6 @@ __metadata: languageName: node linkType: hard -"@sigstore/protobuf-specs@npm:^0.4.0, @sigstore/protobuf-specs@npm:^0.4.1": - version: 0.4.3 - resolution: "@sigstore/protobuf-specs@npm:0.4.3" - checksum: a1254864fd3b8795436a3720c61efafd8ca093d79d7bdb1de56a94ef7f7862cc07f2a5e11a926cad8fc58429bbcbba70c561581b0a52dbbef2d1248b8bf5748b - languageName: node - linkType: hard - "@sigstore/protobuf-specs@npm:^0.5.0": version: 0.5.0 resolution: "@sigstore/protobuf-specs@npm:0.5.0" @@ -6872,20 +5234,6 @@ __metadata: languageName: node linkType: hard -"@sigstore/sign@npm:^3.1.0": - version: 3.1.0 - resolution: "@sigstore/sign@npm:3.1.0" - dependencies: - "@sigstore/bundle": ^3.1.0 - "@sigstore/core": ^2.0.0 - "@sigstore/protobuf-specs": ^0.4.0 - make-fetch-happen: ^14.0.2 - proc-log: ^5.0.0 - promise-retry: ^2.0.1 - checksum: 0518aaa542ad7684d86d7c3d6829119cf35fc4084b61329a376e298acda4ffbc8fa53afaf0b6a8dfba41d5e5943034d2cb5bf8034c07dbd0aa86dd32fb01c3d3 - languageName: node - linkType: hard - "@sigstore/sign@npm:^4.0.0": version: 4.0.1 resolution: "@sigstore/sign@npm:4.0.1" @@ -6900,16 +5248,6 @@ __metadata: languageName: node linkType: hard -"@sigstore/tuf@npm:^3.1.0": - version: 3.1.1 - resolution: "@sigstore/tuf@npm:3.1.1" - dependencies: - "@sigstore/protobuf-specs": ^0.4.1 - tuf-js: ^3.0.1 - checksum: 876a1f749458e280b77ec4084ee1e7594b42bfd4391ed22c41bc2984d6dfa46cb52a28756adb58f6c1cc8b018481a220f9fcef1dbd890ae457253ae7225fd7eb - languageName: node - linkType: hard - "@sigstore/tuf@npm:^4.0.0": version: 4.0.0 resolution: "@sigstore/tuf@npm:4.0.0" @@ -6920,17 +5258,6 @@ __metadata: languageName: node linkType: hard -"@sigstore/verify@npm:^2.1.0": - version: 2.1.1 - resolution: "@sigstore/verify@npm:2.1.1" - dependencies: - "@sigstore/bundle": ^3.1.0 - "@sigstore/core": ^2.0.0 - "@sigstore/protobuf-specs": ^0.4.1 - checksum: 378316e2b5462ef400f9ad764b037a046a9835ac11b341137d6b16120d9fbfcf261a87ee8f918ba3cf67846e0ae639caae01b830697c0f10316685afdd319ec8 - languageName: node - linkType: hard - "@sigstore/verify@npm:^3.0.0": version: 3.0.0 resolution: "@sigstore/verify@npm:3.0.0" @@ -6974,788 +5301,38 @@ __metadata: languageName: node linkType: hard -"@smithy/abort-controller@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/abort-controller@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 50e646633160f16d4d131c4a5612a352bca8ee652acfefc811389307756b791d0e0cee1459eeba4662e09b57e9f78681bb6c24d180d76e605126281fa52c20fb - languageName: node - linkType: hard - -"@smithy/config-resolver@npm:^4.1.4": - version: 4.1.4 - resolution: "@smithy/config-resolver@npm:4.1.4" - dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 - "@smithy/util-config-provider": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - tslib: ^2.6.2 - checksum: d3c3b7017377ae30839d3bc684fca7ff58c41c2ca71dd067931aff61f0c570b09cf35e47fde660488c7e1ecc8e1abf720bd41f380b9a91ea302fbd7c7f1b85fb +"@swc/core-darwin-arm64@npm:1.13.3": + version: 1.13.3 + resolution: "@swc/core-darwin-arm64@npm:1.13.3" + conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@smithy/core@npm:^3.5.3": - version: 3.5.3 - resolution: "@smithy/core@npm:3.5.3" - dependencies: - "@smithy/middleware-serde": ^4.0.8 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-stream": ^4.2.2 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: ae1d9393683e07ccff7ff1dcb7bfdaced42d71e4030094f4e43aaed8dba424e2389e60229805f34443cf60a6a05976c17d09f1b995464a87cf2e5774ead55474 +"@swc/core-darwin-x64@npm:1.13.3": + version: 1.13.3 + resolution: "@swc/core-darwin-x64@npm:1.13.3" + conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@smithy/core@npm:^3.7.0": - version: 3.7.0 - resolution: "@smithy/core@npm:3.7.0" - dependencies: - "@smithy/middleware-serde": ^4.0.8 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-body-length-browser": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-stream": ^4.2.3 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: ca9c7f956e054bdb97ed8c4e4758846d6dcde36761955927abd12089371f795925bd9e67e4977f0a31750388f446e4d6eeeb406a4aebfb342d26546e72485ab1 +"@swc/core-linux-arm-gnueabihf@npm:1.13.3": + version: 1.13.3 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.13.3" + conditions: os=linux & cpu=arm languageName: node linkType: hard -"@smithy/credential-provider-imds@npm:^4.0.6": - version: 4.0.6 - resolution: "@smithy/credential-provider-imds@npm:4.0.6" - dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - tslib: ^2.6.2 - checksum: 380ada77c7cc7f6e11ee4246a335799cd855b43df07469164ca7ccaeecd1eb8e037adf0b870e57578de7f82bb1f77e5d534c55ed3aa44491fcef55809b5d1d5c +"@swc/core-linux-arm64-gnu@npm:1.13.3": + version: 1.13.3 + resolution: "@swc/core-linux-arm64-gnu@npm:1.13.3" + conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@smithy/eventstream-codec@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-codec@npm:4.0.4" - dependencies: - "@aws-crypto/crc32": 5.2.0 - "@smithy/types": ^4.3.1 - "@smithy/util-hex-encoding": ^4.0.0 - tslib: ^2.6.2 - checksum: b5ff1a2c9f8ea48406f181c0104daf56e1f52bf251cfc531f497abce86f02a148d381ee1648bcd34a4c2293b8e0e052c02043755ffeb864421957fa320c74435 - languageName: node - linkType: hard - -"@smithy/eventstream-serde-browser@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-serde-browser@npm:4.0.4" - dependencies: - "@smithy/eventstream-serde-universal": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 1d39b5da8fe1fd060d47f41f25b70ea4291c8ae2e4f5ea79bf1849a72af88ab0d819618e5b02606b1758084ce43964a92ffc36688817409dcabacd36a8a2266a - languageName: node - linkType: hard - -"@smithy/eventstream-serde-config-resolver@npm:^4.1.2": - version: 4.1.2 - resolution: "@smithy/eventstream-serde-config-resolver@npm:4.1.2" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 42b81e6c92029966f373be7eb589e1292a201e191965b62f1ff71a4e6b5dabf874850a8b65441fa9cec735aad018365a3e841f95af7443288130603be00a7996 - languageName: node - linkType: hard - -"@smithy/eventstream-serde-node@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-serde-node@npm:4.0.4" - dependencies: - "@smithy/eventstream-serde-universal": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 179dd707b0f730c36ab68b1cfdbd0c6f29012a25bf3fec564076217a929627e119ccae8e5df34c267c1df8512c6294c10aa74e0511ab82b767f4a7ef39209519 - languageName: node - linkType: hard - -"@smithy/eventstream-serde-universal@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/eventstream-serde-universal@npm:4.0.4" - dependencies: - "@smithy/eventstream-codec": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 53cf083f742f2fa381a0d0457e3fbfe3b36b65efec216f52d21f840382466b59c64e685a65412925b60a3def11e24e4bd6bcd3bb5afad336f3fafab2bb2cbd48 - languageName: node - linkType: hard - -"@smithy/fetch-http-handler@npm:^5.0.4": - version: 5.0.4 - resolution: "@smithy/fetch-http-handler@npm:5.0.4" - dependencies: - "@smithy/protocol-http": ^5.1.2 - "@smithy/querystring-builder": ^4.0.4 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - tslib: ^2.6.2 - checksum: 3afb020d42e50d6bb446ceb8efe0cb33a05b449a15156459eea5717860e785f56a9166eb08455b7c1fd3af277bbca4b708e4d8cccda37519f44aa4990e3f50d4 - languageName: node - linkType: hard - -"@smithy/fetch-http-handler@npm:^5.1.0": - version: 5.1.0 - resolution: "@smithy/fetch-http-handler@npm:5.1.0" - dependencies: - "@smithy/protocol-http": ^5.1.2 - "@smithy/querystring-builder": ^4.0.4 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - tslib: ^2.6.2 - checksum: f88242d6b4f1341e7d45b1defdc6b930f1600d840da57ce015583a81fd24a320e12b9fda12e3c51ecf9ce49ede37fe1f77d21d5e4bb94f094e801b6464dfee8c - languageName: node - linkType: hard - -"@smithy/hash-node@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/hash-node@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - "@smithy/util-buffer-from": ^4.0.0 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: 2fd8a1036b9d6d2948249ad41a97b5801b918948ab1f8041b2b2482848570e8b417eeea7810f959376325e9ab33890775025b34a58305355b84ca8bff1417481 - languageName: node - linkType: hard - -"@smithy/invalid-dependency@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/invalid-dependency@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 6d6f53558cb252e2070e4830a18c0c72ad486308378d6eab2a185d63f5a0492ffbdff27dbea59f79e2d48477af2295e80d6314becf9ea3215827be8bb6690b07 - languageName: node - linkType: hard - -"@smithy/is-array-buffer@npm:^2.2.0": - version: 2.2.0 - resolution: "@smithy/is-array-buffer@npm:2.2.0" - dependencies: - tslib: ^2.6.2 - checksum: cd12c2e27884fec89ca8966d33c9dc34d3234efe89b33a9b309c61ebcde463e6f15f6a02d31d4fddbfd6e5904743524ca5b95021b517b98fe10957c2da0cd5fc - languageName: node - linkType: hard - -"@smithy/is-array-buffer@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/is-array-buffer@npm:4.0.0" - dependencies: - tslib: ^2.6.2 - checksum: 8226fc1eca7aacd7f887f3a5ec2f15a3cafa72aa1c42d3fc759c66600481381d18ec7285a8195f24b9c4fe0ce9a565c133b2021d86a8077aebce3f86b3716802 - languageName: node - linkType: hard - -"@smithy/middleware-content-length@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/middleware-content-length@npm:4.0.4" - dependencies: - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 251e47fbb7df19a8c39719f96dfd9e00252fc33733d2585898b7e5a37e85056052de1559d3c62b9daf493e2293b574ac2e92e17806777cadc9b733f1aab42294 - languageName: node - linkType: hard - -"@smithy/middleware-endpoint@npm:^4.1.11": - version: 4.1.11 - resolution: "@smithy/middleware-endpoint@npm:4.1.11" - dependencies: - "@smithy/core": ^3.5.3 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-middleware": ^4.0.4 - tslib: ^2.6.2 - checksum: dea73669a74afcf74a0fcb8d6a12b940d41c1cafed005244e228eb30c2c60af3b9b69bdd11943078d98eb1de15052fd7335099cb073d84f67f61ce97fbe8688a - languageName: node - linkType: hard - -"@smithy/middleware-endpoint@npm:^4.1.14": - version: 4.1.14 - resolution: "@smithy/middleware-endpoint@npm:4.1.14" - dependencies: - "@smithy/core": ^3.7.0 - "@smithy/middleware-serde": ^4.0.8 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - "@smithy/url-parser": ^4.0.4 - "@smithy/util-middleware": ^4.0.4 - tslib: ^2.6.2 - checksum: 8cc4dbfada407590eb2ffae00b655ab76b5915b2380041df9bf71c157e067da7cad9565852ccad9d6def40325de5d91e508f9451664511dc1bf5c111f405c6db - languageName: node - linkType: hard - -"@smithy/middleware-retry@npm:^4.1.12": - version: 4.1.12 - resolution: "@smithy/middleware-retry@npm:4.1.12" - dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/protocol-http": ^5.1.2 - "@smithy/service-error-classification": ^4.0.5 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.5 - tslib: ^2.6.2 - uuid: ^9.0.1 - checksum: 401d97e2b9e2d62220215ec98f9b7299b9a8aed909b3ae9a5e0c29993087fdfe22464a17bb980eb72c027c1a056d365159102ddd9c06c8045a1a6f0be441f1dd - languageName: node - linkType: hard - -"@smithy/middleware-retry@npm:^4.1.15": - version: 4.1.15 - resolution: "@smithy/middleware-retry@npm:4.1.15" - dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/protocol-http": ^5.1.2 - "@smithy/service-error-classification": ^4.0.6 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-retry": ^4.0.6 - tslib: ^2.6.2 - uuid: ^9.0.1 - checksum: 95fa796973ee58420992e97d88f2ac3e8a794d51a845f3c09e6aab7fac239d7f4e36439b46f9706b295c97d8e9c5ae184b5007947905f5e9bc21a3a265fefa55 - languageName: node - linkType: hard - -"@smithy/middleware-serde@npm:^4.0.8": - version: 4.0.8 - resolution: "@smithy/middleware-serde@npm:4.0.8" - dependencies: - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 1c78cf584bf82c2ed80d55694945d63b5d3bdaf0c4dea1a35ff33b201d939e9ee5afbfb01c6725c9cbc0d9efb3ee50703970d177a9d20dba545e7e7ba3c0a3f5 - languageName: node - linkType: hard - -"@smithy/middleware-stack@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/middleware-stack@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: c0b4e057d438fbc900435a4bcae68308bc17361968ebe828d43b4f78d826711e5d196ea2fc3ef86525169508d885979e459db0d46918ae00a2bb5dc8a5bd6796 - languageName: node - linkType: hard - -"@smithy/node-config-provider@npm:^4.1.3": - version: 4.1.3 - resolution: "@smithy/node-config-provider@npm:4.1.3" - dependencies: - "@smithy/property-provider": ^4.0.4 - "@smithy/shared-ini-file-loader": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: c1260719f567b64e979e54698356ffd49f26d82e5eaafc60741588257df6016bbf7d2e26cba902ff900058e5e47e985287fdcb7ab1acdf6534b7cd50252e3856 - languageName: node - linkType: hard - -"@smithy/node-http-handler@npm:^4.0.6": - version: 4.0.6 - resolution: "@smithy/node-http-handler@npm:4.0.6" - dependencies: - "@smithy/abort-controller": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/querystring-builder": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: b0505a812182f29e4ff254f4710a476e0dd9a869248b5c9972ddaaf0c1bef9dc980682947826d5a24c3b54c635ad6a1d7ac6460e61ac99da8becc4ecda55e768 - languageName: node - linkType: hard - -"@smithy/node-http-handler@npm:^4.1.0": - version: 4.1.0 - resolution: "@smithy/node-http-handler@npm:4.1.0" - dependencies: - "@smithy/abort-controller": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/querystring-builder": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 4ea660acadb0f30255066b068451cd8521d130f5702060c19af5e488f681cc7f76834612e566d80a933d92e897b4fca94973ed942f0a32f1703f6140bdd66b81 - languageName: node - linkType: hard - -"@smithy/property-provider@npm:^2.0.12": - version: 2.2.0 - resolution: "@smithy/property-provider@npm:2.2.0" - dependencies: - "@smithy/types": ^2.12.0 - tslib: ^2.6.2 - checksum: 8d257cbc5222baf6706e288c3b51196588f135878141f8af76fcb3f0abafc027ed46cf4bb938266d1906111175082ee85f73806d5a2b1c929aee16ec8b5283e6 - languageName: node - linkType: hard - -"@smithy/property-provider@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/property-provider@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 1cd552792897e43c1d4cf91edac0956a8a8d6a7ba588e46532644ae5aca535ec0fb33e3aa71c73f325632b72cd1b8f26732525a6723f74c54238026432b0118e - languageName: node - linkType: hard - -"@smithy/protocol-http@npm:^5.1.2": - version: 5.1.2 - resolution: "@smithy/protocol-http@npm:5.1.2" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 48dbb715956f7089f3422c6c875fd5c6c901bb55363091905f749bba4bac03c40bf11e63dcc92c9b5de058305c60513e987e1fd7550e585c9e2a98e7355676c8 - languageName: node - linkType: hard - -"@smithy/querystring-builder@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/querystring-builder@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - "@smithy/util-uri-escape": ^4.0.0 - tslib: ^2.6.2 - checksum: e521cd60294aebabb11386f4db7925095ca7dcdd1eda1904ad3443aa65c992a74e7d57b24018c3e141320bcc8b8928a24b8a14c4328bc7176bdb1eac15f6655b - languageName: node - linkType: hard - -"@smithy/querystring-parser@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/querystring-parser@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: ebe874dfec44ec3d6ff63f9570cac7c18f5b1b2fb3d6a72722adb9d24bb891970fbbabb18d15b8ce46902cc5f21f1751218794f3ff2e110865804d822f4b83a0 - languageName: node - linkType: hard - -"@smithy/service-error-classification@npm:^2.1.5": - version: 2.1.5 - resolution: "@smithy/service-error-classification@npm:2.1.5" - dependencies: - "@smithy/types": ^2.12.0 - checksum: 00ac54110a258c7a47c62d4f655d4998bd40e5adb47e10281b28df7a585f2f1e960dc35325eac006636280e7fb2b81dbeb32b89e08bac87acc136c4d29a4dc53 - languageName: node - linkType: hard - -"@smithy/service-error-classification@npm:^4.0.5": - version: 4.0.5 - resolution: "@smithy/service-error-classification@npm:4.0.5" - dependencies: - "@smithy/types": ^4.3.1 - checksum: c93c0fbcd7f094a8652ecfa0b1281621bb49b93eae36b94330c4990436666e53e9a3a9dc57915b79e79f731f801e18873256c06f7fb091b3b7e64d23fdb33960 - languageName: node - linkType: hard - -"@smithy/service-error-classification@npm:^4.0.6": - version: 4.0.6 - resolution: "@smithy/service-error-classification@npm:4.0.6" - dependencies: - "@smithy/types": ^4.3.1 - checksum: c851c882358af75cac41508ffdd2cfdc59e0cd298cb25cf6a4a97dd6cbc92f4890ce04590305726ebb1bbb6b6c527dde8d80ec84095c76bcdb6a3a1cc2107a90 - languageName: node - linkType: hard - -"@smithy/shared-ini-file-loader@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/shared-ini-file-loader@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: b9405d3fea03cb7d1b031a41d7a91581eaaf47a5e6322c7bf2c57e27bcf8af403eea68c46a9c878a31af8a1f08fa803fce3d253c55b3318548fc93d61b0e56e8 - languageName: node - linkType: hard - -"@smithy/signature-v4@npm:^5.1.2": - version: 5.1.2 - resolution: "@smithy/signature-v4@npm:5.1.2" - dependencies: - "@smithy/is-array-buffer": ^4.0.0 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - "@smithy/util-hex-encoding": ^4.0.0 - "@smithy/util-middleware": ^4.0.4 - "@smithy/util-uri-escape": ^4.0.0 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: b8acbdd600279be860650b8c99ea443b653f0a980a9aceb7cdf8bf0f017d0376a4aac9bef738c24aa0c2f12b3ae1984bf1ed5d99235f64a9ff41a7fcd851b531 - languageName: node - linkType: hard - -"@smithy/smithy-client@npm:^4.4.3": - version: 4.4.3 - resolution: "@smithy/smithy-client@npm:4.4.3" - dependencies: - "@smithy/core": ^3.5.3 - "@smithy/middleware-endpoint": ^4.1.11 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - "@smithy/util-stream": ^4.2.2 - tslib: ^2.6.2 - checksum: 72ceab6f5cd4d908d0e11c8d1437699b9d83aa398704bf9ccd5536305d8d990023484b5b0129027654aaf6d9ebdb89c4fc536c4349e0bdc4b10463c774a21f6a - languageName: node - linkType: hard - -"@smithy/smithy-client@npm:^4.4.6": - version: 4.4.6 - resolution: "@smithy/smithy-client@npm:4.4.6" - dependencies: - "@smithy/core": ^3.7.0 - "@smithy/middleware-endpoint": ^4.1.14 - "@smithy/middleware-stack": ^4.0.4 - "@smithy/protocol-http": ^5.1.2 - "@smithy/types": ^4.3.1 - "@smithy/util-stream": ^4.2.3 - tslib: ^2.6.2 - checksum: 6579149131e16f0dbcb87ff9d6fc4d9f8d71056066d73e4838e8233a0a9f2bf9abd07a8b890db7dded9d539d37a6a66fa6044ae91e0e015d0b73453c5344eeae - languageName: node - linkType: hard - -"@smithy/types@npm:^2.12.0": - version: 2.12.0 - resolution: "@smithy/types@npm:2.12.0" - dependencies: - tslib: ^2.6.2 - checksum: 2dd93746624d87afbf51c22116fc69f82e95004b78cf681c4a283d908155c22a2b7a3afbd64a3aff7deefb6619276f186e212422ad200df3b42c32ef5330374e - languageName: node - linkType: hard - -"@smithy/types@npm:^4.3.1": - version: 4.3.1 - resolution: "@smithy/types@npm:4.3.1" - dependencies: - tslib: ^2.6.2 - checksum: 45f2e15cec06eefb6a2470346c65ec927e56ab1757eee5ab1c431f703a9b350b331679e1f60105a1529ecb9cdb953104883942e655701fb4710bbaf566ec0bc6 - languageName: node - linkType: hard - -"@smithy/url-parser@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/url-parser@npm:4.0.4" - dependencies: - "@smithy/querystring-parser": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 1d3df1c58809f424af00396f987607ec9ebb0840625e4353af6dcd6baf480db8dd080b2f01ed41598ff18681ab2fcecab37f18f4c253fcbdd71eab2fab049400 - languageName: node - linkType: hard - -"@smithy/util-base64@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-base64@npm:4.0.0" - dependencies: - "@smithy/util-buffer-from": ^4.0.0 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: 7fb3430d6e1cbb4bcc61458587bb0746458f0ec8e8cd008224ca984ff65c3c3307b3a528d040cef4c1fc7d1bd4111f6de8f4f1595845422f14ac7d100b3871b1 - languageName: node - linkType: hard - -"@smithy/util-body-length-browser@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-body-length-browser@npm:4.0.0" - dependencies: - tslib: ^2.6.2 - checksum: 72381e12de7cccbb722c60e3f3ae0f8bce7fc9a9e8064c7968ac733698a5a30bea098a3c365095c519491fe64e2e949c22f74d4f1e0d910090d6389b41c416eb - languageName: node - linkType: hard - -"@smithy/util-body-length-node@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-body-length-node@npm:4.0.0" - dependencies: - tslib: ^2.6.2 - checksum: 12d8de9c526647f51f56804044f5847f0c7c7afee30fa368d2b7bd4b4de8fe2438a925aab51965fe8a4b2f08f68e8630cc3c54a449beae6646d99cae900ed106 - languageName: node - linkType: hard - -"@smithy/util-buffer-from@npm:^2.2.0": - version: 2.2.0 - resolution: "@smithy/util-buffer-from@npm:2.2.0" - dependencies: - "@smithy/is-array-buffer": ^2.2.0 - tslib: ^2.6.2 - checksum: 424c5b7368ae5880a8f2732e298d17879a19ca925f24ca45e1c6c005f717bb15b76eb28174d308d81631ad457ea0088aab0fd3255dd42f45a535c81944ad64d3 - languageName: node - linkType: hard - -"@smithy/util-buffer-from@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-buffer-from@npm:4.0.0" - dependencies: - "@smithy/is-array-buffer": ^4.0.0 - tslib: ^2.6.2 - checksum: 8124e28d3e34b5335c08398a9081cc56a232d23e08172d488669f91a167d0871d36aba9dd3e4b70175a52f1bd70e2bf708d4c989a19512a4374d2cf67650a15e - languageName: node - linkType: hard - -"@smithy/util-config-provider@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-config-provider@npm:4.0.0" - dependencies: - tslib: ^2.6.2 - checksum: 91bd9e0bec4c4a37c3fc286e72f3387be9272b090111edaee992d9e9619370f3f2ad88ce771ef42dbfe40a44500163b633914486e662526591f5f737d5e4ff5a - languageName: node - linkType: hard - -"@smithy/util-defaults-mode-browser@npm:^4.0.19": - version: 4.0.19 - resolution: "@smithy/util-defaults-mode-browser@npm:4.0.19" - dependencies: - "@smithy/property-provider": ^4.0.4 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - bowser: ^2.11.0 - tslib: ^2.6.2 - checksum: 2e523365df792b6e8615a5f6030dde5b6aeda84f149d694bbc6b2a3f9a44d29e6ac447ee50d5e69349e6c9f318b0559874790c20c5b300253f7dea41ac1bd5b0 - languageName: node - linkType: hard - -"@smithy/util-defaults-mode-browser@npm:^4.0.22": - version: 4.0.22 - resolution: "@smithy/util-defaults-mode-browser@npm:4.0.22" - dependencies: - "@smithy/property-provider": ^4.0.4 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - bowser: ^2.11.0 - tslib: ^2.6.2 - checksum: 7ba4f23367a66cb73db19f9e5ae2fb2a6337127ed6f65278b909a37a152b3efb09c5fbcd933e4c4531a257f691cf4f5b3b1f4816024a3112a4c94c4afc55c04d - languageName: node - linkType: hard - -"@smithy/util-defaults-mode-node@npm:^4.0.19": - version: 4.0.19 - resolution: "@smithy/util-defaults-mode-node@npm:4.0.19" - dependencies: - "@smithy/config-resolver": ^4.1.4 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/smithy-client": ^4.4.3 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 7e76598b9af449bc412d8184c1539c4dfe1997090ccc16fb95a9c66ddf362a6e31feb8eb3e7ad7252d9d4361b5a8e34bff1e203f774818c581fbf526f4833015 - languageName: node - linkType: hard - -"@smithy/util-defaults-mode-node@npm:^4.0.22": - version: 4.0.22 - resolution: "@smithy/util-defaults-mode-node@npm:4.0.22" - dependencies: - "@smithy/config-resolver": ^4.1.4 - "@smithy/credential-provider-imds": ^4.0.6 - "@smithy/node-config-provider": ^4.1.3 - "@smithy/property-provider": ^4.0.4 - "@smithy/smithy-client": ^4.4.6 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 1a10953d1b6b0e0ae484cfa6a1df3a0782414c2d6b95e2c43d3320c2e1260a80b582a68bbfb48eb373c1e5effb337286e85794878c4da6a9dc797011ff809d7a - languageName: node - linkType: hard - -"@smithy/util-endpoints@npm:^3.0.6": - version: 3.0.6 - resolution: "@smithy/util-endpoints@npm:3.0.6" - dependencies: - "@smithy/node-config-provider": ^4.1.3 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 185c096db895f5bfabc05f1500d3428761fc4d450e998d6bf269879f7fc3f6fd770c1ed5a2de395a6b5300197bd40748a5b06c74b91ff01c9c499a25f2ba827e - languageName: node - linkType: hard - -"@smithy/util-hex-encoding@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-hex-encoding@npm:4.0.0" - dependencies: - tslib: ^2.6.2 - checksum: b932fa0e5cd2ba2598ad55ce46722bbbd15109809badaa3e4402fe4dd6f31f62b9fb49d2616e38d660363dc92a5898391f9c8f3b18507c36109e908400785e2a - languageName: node - linkType: hard - -"@smithy/util-middleware@npm:^4.0.4": - version: 4.0.4 - resolution: "@smithy/util-middleware@npm:4.0.4" - dependencies: - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 6cfdec16f03cc963e78d888a0ef349c0d80645775e9933a88c4615fbd5a683a8230997f89372e2597bd956bc05df5adc41de6524fa8c0cc93fb7150d6530a03b - languageName: node - linkType: hard - -"@smithy/util-retry@npm:^2.0.4": - version: 2.2.0 - resolution: "@smithy/util-retry@npm:2.2.0" - dependencies: - "@smithy/service-error-classification": ^2.1.5 - "@smithy/types": ^2.12.0 - tslib: ^2.6.2 - checksum: 1a8071c8ac5a2646b3d3894e3bd9c36a9db045f52eadb194f32b02d2fdedd69fb267a2b02bcef9f91d0f8f3fe061754ac075d07ac166d90894acb27d68c62a41 - languageName: node - linkType: hard - -"@smithy/util-retry@npm:^4.0.5": - version: 4.0.5 - resolution: "@smithy/util-retry@npm:4.0.5" - dependencies: - "@smithy/service-error-classification": ^4.0.5 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 2027f792a76591f2c761fa322ed50a95d40c6ef457f6d044f6b64694e037fcf549bf56e750a4f36a1460a0a6b05c6690f911733d66b40b4c84f8fcfe69bd931d - languageName: node - linkType: hard - -"@smithy/util-retry@npm:^4.0.6": - version: 4.0.6 - resolution: "@smithy/util-retry@npm:4.0.6" - dependencies: - "@smithy/service-error-classification": ^4.0.6 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 0faef3d90da51024a5abd90de6bf1a846b6cd0f61c78791a2fecc7e49b0e8a705ca5619ae538cad4bab8995456d8219fe1c2769dacd156195cb73befcb02ca03 - languageName: node - linkType: hard - -"@smithy/util-stream@npm:^4.2.2": - version: 4.2.2 - resolution: "@smithy/util-stream@npm:4.2.2" - dependencies: - "@smithy/fetch-http-handler": ^5.0.4 - "@smithy/node-http-handler": ^4.0.6 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-buffer-from": ^4.0.0 - "@smithy/util-hex-encoding": ^4.0.0 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: 52b7c38ccf397536c882fd15453ca4c214618e045fcd96b049e79829f9c5be06a526f3672e88f1f578fa730debead264ce53e6fc3b18f43d8e6079a24ba7bbb0 - languageName: node - linkType: hard - -"@smithy/util-stream@npm:^4.2.3": - version: 4.2.3 - resolution: "@smithy/util-stream@npm:4.2.3" - dependencies: - "@smithy/fetch-http-handler": ^5.1.0 - "@smithy/node-http-handler": ^4.1.0 - "@smithy/types": ^4.3.1 - "@smithy/util-base64": ^4.0.0 - "@smithy/util-buffer-from": ^4.0.0 - "@smithy/util-hex-encoding": ^4.0.0 - "@smithy/util-utf8": ^4.0.0 - tslib: ^2.6.2 - checksum: 3384df45323f9af1ecc3bad506e8dc0100af44397d623e4b456654b997c87458b9c550b6f540f31e1d498f93e914b868f4bda6cf7eb36b34e26f86426c5299fd - languageName: node - linkType: hard - -"@smithy/util-uri-escape@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-uri-escape@npm:4.0.0" - dependencies: - tslib: ^2.6.2 - checksum: 7ea350545971f8a009d56e085c34c949c9045862cfab233ee7adc16e111a076a814bb5d9279b2b85ee382e0ed204a1c673ac32e3e28f1073b62a2c53a5dd6d19 - languageName: node - linkType: hard - -"@smithy/util-utf8@npm:^2.0.0": - version: 2.3.0 - resolution: "@smithy/util-utf8@npm:2.3.0" - dependencies: - "@smithy/util-buffer-from": ^2.2.0 - tslib: ^2.6.2 - checksum: 00e55d4b4e37d48be0eef3599082402b933c52a1407fed7e8e8ad76d94d81a0b30b8bfaf2047c59d9c3af31e5f20e7a8c959cb7ae270f894255e05a2229964f0 - languageName: node - linkType: hard - -"@smithy/util-utf8@npm:^4.0.0": - version: 4.0.0 - resolution: "@smithy/util-utf8@npm:4.0.0" - dependencies: - "@smithy/util-buffer-from": ^4.0.0 - tslib: ^2.6.2 - checksum: 08811c5a18c341782b3b65acc4640a9f559aeba61c889dbdc56e5153a3b7f395e613bfb1ade25cf15311d6237f291e1fce8af197c6313065e0cb084fd2148c64 - languageName: node - linkType: hard - -"@smithy/util-waiter@npm:^4.0.5": - version: 4.0.5 - resolution: "@smithy/util-waiter@npm:4.0.5" - dependencies: - "@smithy/abort-controller": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 6fa635c78093d4512aa569338af4d36bfb7c2982ac53a0bfb83f3c90cc483df05bc05450cabab57e0e1a1348efd2a469ce711b0e90d9f7a9c8b19f51dfa3d880 - languageName: node - linkType: hard - -"@smithy/util-waiter@npm:^4.0.6": - version: 4.0.6 - resolution: "@smithy/util-waiter@npm:4.0.6" - dependencies: - "@smithy/abort-controller": ^4.0.4 - "@smithy/types": ^4.3.1 - tslib: ^2.6.2 - checksum: 0fb8f5bd351f875f50e4b82845eb427f42d200dd49d39001be3c1f8da6c08383e41c2fcfb5a53fb628211210fe181da30c3c60cb3923805a2063df5cf1be6dd7 - languageName: node - linkType: hard - -"@swc/core-darwin-arm64@npm:1.13.3": - version: 1.13.3 - resolution: "@swc/core-darwin-arm64@npm:1.13.3" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - -"@swc/core-darwin-x64@npm:1.13.3": - version: 1.13.3 - resolution: "@swc/core-darwin-x64@npm:1.13.3" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - -"@swc/core-linux-arm-gnueabihf@npm:1.13.3": - version: 1.13.3 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.13.3" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - -"@swc/core-linux-arm64-gnu@npm:1.13.3": - version: 1.13.3 - resolution: "@swc/core-linux-arm64-gnu@npm:1.13.3" - conditions: os=linux & cpu=arm64 & libc=glibc - languageName: node - linkType: hard - -"@swc/core-linux-arm64-musl@npm:1.13.3": - version: 1.13.3 - resolution: "@swc/core-linux-arm64-musl@npm:1.13.3" - conditions: os=linux & cpu=arm64 & libc=musl +"@swc/core-linux-arm64-musl@npm:1.13.3": + version: 1.13.3 + resolution: "@swc/core-linux-arm64-musl@npm:1.13.3" + conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard @@ -7881,13 +5458,6 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 - languageName: node - linkType: hard - "@tootallnate/quickjs-emscripten@npm:^0.23.0": version: 0.23.0 resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" @@ -7902,16 +5472,6 @@ __metadata: languageName: node linkType: hard -"@tufjs/models@npm:3.0.1": - version: 3.0.1 - resolution: "@tufjs/models@npm:3.0.1" - dependencies: - "@tufjs/canonical-json": 2.0.0 - minimatch: ^9.0.5 - checksum: 95b179bc09e5a0b6dfc9e7001e15882e863e034bf41e0502e89f2fa82cb3f6d5bd9edaefd2baf2a7f515abdb521127adf771e8bbe66f3e7f212e3b777ae993f5 - languageName: node - linkType: hard - "@tufjs/models@npm:4.0.0": version: 4.0.0 resolution: "@tufjs/models@npm:4.0.0" @@ -7972,13 +5532,6 @@ __metadata: languageName: node linkType: hard -"@types/caseless@npm:*": - version: 0.12.5 - resolution: "@types/caseless@npm:0.12.5" - checksum: f6a3628add76d27005495914c9c3873a93536957edaa5b69c63b46fe10b4649a6fecf16b676c1695f46aab851da47ec6047dcf3570fa8d9b6883492ff6d074e0 - languageName: node - linkType: hard - "@types/datadog-metrics@npm:0.6.1": version: 0.6.1 resolution: "@types/datadog-metrics@npm:0.6.1" @@ -8077,13 +5630,6 @@ __metadata: languageName: node linkType: hard -"@types/long@npm:^4.0.0": - version: 4.0.2 - resolution: "@types/long@npm:4.0.2" - checksum: d16cde7240d834cf44ba1eaec49e78ae3180e724cd667052b194a372f350d024cba8dd3f37b0864931683dab09ca935d52f0c4c1687178af5ada9fc85b0635f4 - languageName: node - linkType: hard - "@types/minimatch@npm:^3.0.3": version: 3.0.5 resolution: "@types/minimatch@npm:3.0.5" @@ -8185,18 +5731,6 @@ __metadata: languageName: node linkType: hard -"@types/request@npm:^2.48.12, @types/request@npm:^2.48.8": - version: 2.48.12 - resolution: "@types/request@npm:2.48.12" - dependencies: - "@types/caseless": "*" - "@types/node": "*" - "@types/tough-cookie": "*" - form-data: ^2.5.0 - checksum: 20dfad0a46b4249bf42f09c51fbd4d02ec6738c5152194b5c7c69bab80b00eae9cc71df4489ffa929d0968d453ef7d0823d1f98871efed563a4fdb57bf0a4c58 - languageName: node - linkType: hard - "@types/semver@npm:^7.3.12": version: 7.7.0 resolution: "@types/semver@npm:7.7.0" @@ -8211,14 +5745,7 @@ __metadata: languageName: node linkType: hard -"@types/tough-cookie@npm:*": - version: 4.0.5 - resolution: "@types/tough-cookie@npm:4.0.5" - checksum: f19409d0190b179331586365912920d192733112a195e870c7f18d20ac8adb7ad0b0ff69dad430dba8bc2be09593453a719cfea92dc3bda19748fd158fe1498d - languageName: node - linkType: hard - -"@types/uuid@npm:^9.0.1": +"@types/uuid@npm:^9.0.2": version: 9.0.8 resolution: "@types/uuid@npm:9.0.8" checksum: b8c60b7ba8250356b5088302583d1704a4e1a13558d143c549c408bf8920535602ffc12394ede77f8a8083511b023704bc66d1345792714002bfa261b17c5275 @@ -8548,18 +6075,7 @@ __metadata: dependencies: "@typescript-eslint/types": 7.18.0 eslint-visitor-keys: ^3.4.3 - checksum: 6e806a7cdb424c5498ea187a5a11d0fef7e4602a631be413e7d521e5aec1ab46ba00c76cfb18020adaa0a8c9802354a163bfa0deb74baa7d555526c7517bb158 - languageName: node - linkType: hard - -"@typespec/ts-http-runtime@npm:^0.3.0": - version: 0.3.0 - resolution: "@typespec/ts-http-runtime@npm:0.3.0" - dependencies: - http-proxy-agent: ^7.0.0 - https-proxy-agent: ^7.0.0 - tslib: ^2.6.2 - checksum: 1adc5ebdad420d5db3cc59b3f26d079b98c3a7a11ce82f6c6effd93319b5ce27a18537f03b0c79e26a2ccb42bbed9cd8e872c19b8500e51b39c2b98b77839e01 + checksum: 6e806a7cdb424c5498ea187a5a11d0fef7e4602a631be413e7d521e5aec1ab46ba00c76cfb18020adaa0a8c9802354a163bfa0deb74baa7d555526c7517bb158 languageName: node linkType: hard @@ -8688,6 +6204,13 @@ __metadata: languageName: node linkType: hard +"abbrev@npm:^4.0.0": + version: 4.0.0 + resolution: "abbrev@npm:4.0.0" + checksum: d0344b63d28e763f259b4898c41bdc92c08e9d06d0da5617d0bbe4d78244e46daea88c510a2f9472af59b031d9060ec1a999653144e793fd029a59dae2f56dc8 + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -8741,15 +6264,6 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:6": - version: 6.0.2 - resolution: "agent-base@npm:6.0.2" - dependencies: - debug: 4 - checksum: f52b6872cc96fd5f622071b71ef200e01c7c4c454ee68bc9accca90c98cfb39f2810e3e9aa330435835eedc8c23f4f8a15267f67c6e245d2b33757575bdac49d - languageName: node - linkType: hard - "agent-base@npm:^7.1.0, agent-base@npm:^7.1.2": version: 7.1.3 resolution: "agent-base@npm:7.1.3" @@ -9078,7 +6592,7 @@ __metadata: languageName: node linkType: hard -"arrify@npm:^2.0.0, arrify@npm:^2.0.1": +"arrify@npm:^2.0.1": version: 2.0.1 resolution: "arrify@npm:2.0.1" checksum: 067c4c1afd182806a82e4c1cb8acee16ab8b5284fbca1ce29408e6e91281c36bb5b612f6ddfbd40a0f7a7e0c75bf2696eb94c027f6e328d6e9c52465c98e4209 @@ -9092,7 +6606,7 @@ __metadata: languageName: node linkType: hard -"asn1@npm:^0.2.6, asn1@npm:~0.2.0, asn1@npm:~0.2.3": +"asn1@npm:^0.2.6, asn1@npm:~0.2.3": version: 0.2.6 resolution: "asn1@npm:0.2.6" dependencies: @@ -9425,7 +6939,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.0, base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": +"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -9462,7 +6976,7 @@ __metadata: "@babel/core": 7.25.2 "@babel/preset-env": 7.25.3 "@babel/runtime": ^7.26.10 - "@datadog/datadog-ci": 3.12.0 + "@datadog/datadog-ci": ^4.4.0 "@datadog/mobile-react-native": "workspace:packages/core" "@datadog/mobile-react-native-session-replay": "workspace:packages/react-native-session-replay" "@datadog/mobile-react-native-webview": "workspace:packages/react-native-webview" @@ -9510,16 +7024,16 @@ __metadata: languageName: node linkType: hard -"bin-links@npm:^5.0.0": - version: 5.0.0 - resolution: "bin-links@npm:5.0.0" +"bin-links@npm:^6.0.0": + version: 6.0.0 + resolution: "bin-links@npm:6.0.0" dependencies: - cmd-shim: ^7.0.0 - npm-normalize-package-bin: ^4.0.0 - proc-log: ^5.0.0 - read-cmd-shim: ^5.0.0 - write-file-atomic: ^6.0.0 - checksum: b3793e0e5af4b42ac911c4a2abf78c460f0a787c038d4b401ee1017e64823679d8aef25ada5f9c39f53889c62329a23547f724b7a784aab128fb6defd9515485 + cmd-shim: ^8.0.0 + npm-normalize-package-bin: ^5.0.0 + proc-log: ^6.0.0 + read-cmd-shim: ^6.0.0 + write-file-atomic: ^7.0.0 + checksum: 8819afa657109d140eee4556e2da7538f9cad12eb0e67687347049338fa551ca2a372684ca69ae8710e7eeb77415f36d35403bfc75aee9fc5327f9b31d1829da languageName: node linkType: hard @@ -9555,13 +7069,6 @@ __metadata: languageName: node linkType: hard -"bowser@npm:^2.11.0": - version: 2.11.0 - resolution: "bowser@npm:2.11.0" - checksum: 29c3f01f22e703fa6644fc3b684307442df4240b6e10f6cfe1b61c6ca5721073189ca97cdeedb376081148c8518e33b1d818a57f781d70b0b70e1f31fb48814f - languageName: node - linkType: hard - "bplist-creator@npm:0.1.1": version: 0.1.1 resolution: "bplist-creator@npm:0.1.1" @@ -9652,13 +7159,6 @@ __metadata: languageName: node linkType: hard -"buffer-equal-constant-time@npm:^1.0.1": - version: 1.0.1 - resolution: "buffer-equal-constant-time@npm:1.0.1" - checksum: 80bb945f5d782a56f374b292770901065bad21420e34936ecbe949e57724b4a13874f735850dd1cc61f078773c4fb5493a41391e7bda40d1fa388d6bd80daaab - languageName: node - linkType: hard - "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -9683,15 +7183,6 @@ __metadata: languageName: node linkType: hard -"bundle-name@npm:^4.1.0": - version: 4.1.0 - resolution: "bundle-name@npm:4.1.0" - dependencies: - run-applescript: ^7.0.0 - checksum: 1d966c8d2dbf4d9d394e53b724ac756c2414c45c01340b37743621f59cc565a435024b394ddcb62b9b335d1c9a31f4640eb648c3fec7f97ee74dc0694c9beb6c - languageName: node - linkType: hard - "byte-size@npm:8.1.1": version: 8.1.1 resolution: "byte-size@npm:8.1.1" @@ -9868,7 +7359,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:4.1.2, chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -10110,10 +7601,10 @@ __metadata: languageName: node linkType: hard -"cmd-shim@npm:^7.0.0": - version: 7.0.0 - resolution: "cmd-shim@npm:7.0.0" - checksum: 4cf622d175b505aff1f8a9ad26164022cfb5599c88a7d0f4b443b78a45945b0950ff6898a854bdefdf5c3155f84e862e2502756a1a83115b0d1d40825be30e96 +"cmd-shim@npm:^8.0.0": + version: 8.0.0 + resolution: "cmd-shim@npm:8.0.0" + checksum: c2412176eca7ae3371b7e2f5a5927a61d2b59066f1c3be5fd64b951eb76d41b5c909f644efdbcb3e49a6197499672cac5e8796e25a008c12f930dc8001e115e4 languageName: node linkType: hard @@ -10681,13 +8172,6 @@ __metadata: languageName: node linkType: hard -"data-uri-to-buffer@npm:^4.0.0": - version: 4.0.1 - resolution: "data-uri-to-buffer@npm:4.0.1" - checksum: 0d0790b67ffec5302f204c2ccca4494f70b4e2d940fea3d36b09f0bb2b8539c2e86690429eb1f1dc4bcc9e4df0644193073e63d9ee48ac9fce79ec1506e4aa4c - languageName: node - linkType: hard - "data-uri-to-buffer@npm:^6.0.2": version: 6.0.2 resolution: "data-uri-to-buffer@npm:6.0.2" @@ -10766,7 +8250,7 @@ __metadata: "@babel/core": ^7.25.2 "@babel/preset-env": ^7.25.3 "@babel/runtime": ^7.26.10 - "@datadog/datadog-ci": ^2.39.0 + "@datadog/datadog-ci": ^4.4.0 "@datadog/mobile-react-native": "workspace:packages/core" "@datadog/mobile-react-native-navigation": "workspace:packages/react-native-navigation" "@datadog/mobile-react-native-session-replay": "workspace:packages/react-native-session-replay" @@ -10829,7 +8313,7 @@ __metadata: eslint-plugin-react-native: 3.10.0 genversion: 3.0.2 jest: ^29.7.0 - lerna: 9.0.0 + lerna: 9.0.3 metro: ^0.83.1 pod-install: 0.1.14 prettier: 2.2.0 @@ -11002,13 +8486,6 @@ __metadata: languageName: node linkType: hard -"deep-object-diff@npm:^1.1.9": - version: 1.1.9 - resolution: "deep-object-diff@npm:1.1.9" - checksum: ecd42455e4773f653595d28070295e7aaa8402db5f8ab21d0bec115a7cb4de5e207a5665514767da5f025c96597f1d3a0a4888aeb4dd49e03c996871a3aa05ef - languageName: node - linkType: hard - "deepmerge@npm:^4.2.2, deepmerge@npm:^4.3.0": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" @@ -11016,23 +8493,6 @@ __metadata: languageName: node linkType: hard -"default-browser-id@npm:^5.0.0": - version: 5.0.0 - resolution: "default-browser-id@npm:5.0.0" - checksum: 185bfaecec2c75fa423544af722a3469b20704c8d1942794a86e4364fe7d9e8e9f63241a5b769d61c8151993bc65833a5b959026fa1ccea343b3db0a33aa6deb - languageName: node - linkType: hard - -"default-browser@npm:^5.2.1": - version: 5.2.1 - resolution: "default-browser@npm:5.2.1" - dependencies: - bundle-name: ^4.1.0 - default-browser-id: ^5.0.0 - checksum: afab7eff7b7f5f7a94d9114d1ec67273d3fbc539edf8c0f80019879d53aa71e867303c6f6d7cffeb10a6f3cfb59d4f963dba3f9c96830b4540cc7339a1bf9840 - languageName: node - linkType: hard - "defaults@npm:^1.0.3": version: 1.0.4 resolution: "defaults@npm:1.0.4" @@ -11060,13 +8520,6 @@ __metadata: languageName: node linkType: hard -"define-lazy-prop@npm:^3.0.0": - version: 3.0.0 - resolution: "define-lazy-prop@npm:3.0.0" - checksum: 54884f94caac0791bf6395a3ec530ce901cf71c47b0196b8754f3fd17edb6c0e80149c1214429d851873bb0d689dbe08dcedbb2306dc45c8534a5934723851b6 - languageName: node - linkType: hard - "define-properties@npm:^1.1.3, define-properties@npm:^1.2.1": version: 1.2.1 resolution: "define-properties@npm:1.2.1" @@ -11267,15 +8720,6 @@ __metadata: languageName: node linkType: hard -"dot-prop@npm:^6.0.0": - version: 6.0.1 - resolution: "dot-prop@npm:6.0.1" - dependencies: - is-obj: ^2.0.0 - checksum: 0f47600a4b93e1dc37261da4e6909652c008832a5d3684b5bf9a9a0d3f4c67ea949a86dceed9b72f5733ed8e8e6383cc5958df3bbd0799ee317fd181f2ece700 - languageName: node - linkType: hard - "dotenv-expand@npm:~11.0.6": version: 11.0.7 resolution: "dotenv-expand@npm:11.0.7" @@ -11310,18 +8754,6 @@ __metadata: languageName: node linkType: hard -"duplexify@npm:^4.0.0, duplexify@npm:^4.1.1, duplexify@npm:^4.1.3": - version: 4.1.3 - resolution: "duplexify@npm:4.1.3" - dependencies: - end-of-stream: ^1.4.1 - inherits: ^2.0.3 - readable-stream: ^3.1.1 - stream-shift: ^1.0.2 - checksum: 9636a027345de3dd3c801594d01a7c73d9ce260019538beb1ee650bba7544e72f40a4d4902b52e1ab283dc32a06f210d42748773af02ff15e3064a9659deab7f - languageName: node - linkType: hard - "eastasianwidth@npm:^0.2.0": version: 0.2.0 resolution: "eastasianwidth@npm:0.2.0" @@ -11339,15 +8771,6 @@ __metadata: languageName: node linkType: hard -"ecdsa-sig-formatter@npm:1.0.11, ecdsa-sig-formatter@npm:^1.0.11": - version: 1.0.11 - resolution: "ecdsa-sig-formatter@npm:1.0.11" - dependencies: - safe-buffer: ^5.0.1 - checksum: 207f9ab1c2669b8e65540bce29506134613dd5f122cccf1e6a560f4d63f2732d427d938f8481df175505aad94583bcb32c688737bb39a6df0625f903d6d93c03 - languageName: node - linkType: hard - "ee-first@npm:1.1.1": version: 1.1.1 resolution: "ee-first@npm:1.1.1" @@ -12168,15 +9591,6 @@ __metadata: languageName: node linkType: hard -"eventid@npm:^2.0.0": - version: 2.0.1 - resolution: "eventid@npm:2.0.1" - dependencies: - uuid: ^8.0.0 - checksum: d7de09a0792127796d8ff413e972c0fd2bf52547fd38b7d67bc6dcb10464eb6508f60b92b60e118ecce035586326b2e159be3cec7074fcd5e0e0217c754db3be - languageName: node - linkType: hard - "execa@npm:5.0.0": version: 5.0.0 resolution: "execa@npm:5.0.0" @@ -12426,16 +9840,6 @@ __metadata: languageName: node linkType: hard -"fetch-blob@npm:^3.1.2, fetch-blob@npm:^3.1.4": - version: 3.2.0 - resolution: "fetch-blob@npm:3.2.0" - dependencies: - node-domexception: ^1.0.0 - web-streams-polyfill: ^3.0.3 - checksum: f19bc28a2a0b9626e69fd7cf3a05798706db7f6c7548da657cbf5026a570945f5eeaedff52007ea35c8bcd3d237c58a20bf1543bc568ab2422411d762dd3d5bf - languageName: node - linkType: hard - "figures@npm:3.2.0, figures@npm:^3.0.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -12661,15 +10065,6 @@ __metadata: languageName: node linkType: hard -"formdata-polyfill@npm:^4.0.10": - version: 4.0.10 - resolution: "formdata-polyfill@npm:4.0.10" - dependencies: - fetch-blob: ^3.1.2 - checksum: 82a34df292afadd82b43d4a740ce387bc08541e0a534358425193017bf9fb3567875dc5f69564984b1da979979b70703aa73dee715a17b6c229752ae736dd9db - languageName: node - linkType: hard - "formidable@npm:3.5.3": version: 3.5.3 resolution: "formidable@npm:3.5.3" @@ -12835,59 +10230,6 @@ __metadata: languageName: node linkType: hard -"fuzzy@npm:^0.1.3": - version: 0.1.3 - resolution: "fuzzy@npm:0.1.3" - checksum: acc09c6173e12d5dc8ae51857551ddbe834befa9ebc6be6d5581d09117265d704809d80407d220fd0652f347a9975a4d106854cacc8bd031487a0ede86982f84 - languageName: node - linkType: hard - -"gaxios@npm:^6.0.0, gaxios@npm:^6.1.1": - version: 6.7.1 - resolution: "gaxios@npm:6.7.1" - dependencies: - extend: ^3.0.2 - https-proxy-agent: ^7.0.1 - is-stream: ^2.0.0 - node-fetch: ^2.6.9 - uuid: ^9.0.1 - checksum: ed5952655339918e0868c6f4e079d6e9e55b20a242ddb1be25076cdfb0dd1ca5a2cb233da7352baa972c19f898a78fa6ba67e7d848717c9ca9877c269b5b02f7 - languageName: node - linkType: hard - -"gaxios@npm:^7.0.0": - version: 7.1.1 - resolution: "gaxios@npm:7.1.1" - dependencies: - extend: ^3.0.2 - https-proxy-agent: ^7.0.1 - node-fetch: ^3.3.2 - checksum: 1dc0416deab839e3ce2dfe813fc6aae361c9ab51a1c05f810acbd4d7f87fa64e551e0faeba18c9d7da8cc94866a84c536e5695db2a6e77dc8a4550f264e385bb - languageName: node - linkType: hard - -"gcp-metadata@npm:^6.0.0, gcp-metadata@npm:^6.1.0": - version: 6.1.1 - resolution: "gcp-metadata@npm:6.1.1" - dependencies: - gaxios: ^6.1.1 - google-logging-utils: ^0.0.2 - json-bigint: ^1.0.0 - checksum: 7dffe884fd718482b559a841da469dc30e766a4b86c71cda96bed42579763d58f28328238b2eb424c29ba10ef45d4bb8a6586441921734f01012b55bbea79711 - languageName: node - linkType: hard - -"gcp-metadata@npm:^7.0.0": - version: 7.0.1 - resolution: "gcp-metadata@npm:7.0.1" - dependencies: - gaxios: ^7.0.0 - google-logging-utils: ^1.0.0 - json-bigint: ^1.0.0 - checksum: 2f66aaad97198212cce06b22879769aac79f16edfdea46927d2eae37e40202d01286e1807f2e0b02727b992971f2add8bff1c3bc890bb1f01f8eaf034006877f - languageName: node - linkType: hard - "gensync@npm:^1.0.0-beta.2": version: 1.0.0-beta.2 resolution: "gensync@npm:1.0.0-beta.2" @@ -13128,9 +10470,9 @@ __metadata: languageName: node linkType: hard -"glob@npm:^10.2.2, glob@npm:^10.4.5": - version: 10.4.5 - resolution: "glob@npm:10.4.5" +"glob@npm:^10.5.0": + version: 10.5.0 + resolution: "glob@npm:10.5.0" dependencies: foreground-child: ^3.1.0 jackspeak: ^3.1.2 @@ -13140,7 +10482,7 @@ __metadata: path-scurry: ^1.11.1 bin: glob: dist/esm/bin.mjs - checksum: 0bc725de5e4862f9f387fd0f2b274baf16850dcd2714502ccf471ee401803997983e2c05590cb65f9675a3c6f2a58e7a53f9e365704108c6ad3cbf1d60934c4a + checksum: cda96c074878abca9657bd984d2396945cf0d64283f6feeb40d738fe2da642be0010ad5210a1646244a5fc3511b0cab5a374569b3de5a12b8a63d392f18c6043 languageName: node linkType: hard @@ -13160,6 +10502,17 @@ __metadata: languageName: node linkType: hard +"glob@npm:^13.0.0": + version: 13.0.0 + resolution: "glob@npm:13.0.0" + dependencies: + minimatch: ^10.1.1 + minipass: ^7.1.2 + path-scurry: ^2.0.0 + checksum: 963730222b0acc85a0d2616c08ba3a5d5b5f33fbf69182791967b8a02245db505577a6fc19836d5d58e1cbbfb414ad4f62f605a0372ab05cd9e6998efe944369 + languageName: node + linkType: hard + "glob@npm:^7.1.1, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -13253,88 +10606,6 @@ __metadata: languageName: node linkType: hard -"google-auth-library@npm:^10.1.0": - version: 10.1.0 - resolution: "google-auth-library@npm:10.1.0" - dependencies: - base64-js: ^1.3.0 - ecdsa-sig-formatter: ^1.0.11 - gaxios: ^7.0.0 - gcp-metadata: ^7.0.0 - google-logging-utils: ^1.0.0 - gtoken: ^8.0.0 - jws: ^4.0.0 - checksum: b453dc5595b54aecd9ad92189a67dab9f5fd644637189577aeedbc50a750946f21e9b4999c2e0a30904b6606ca4dbca8e800bbcebb194a41b2704935968d4172 - languageName: node - linkType: hard - -"google-auth-library@npm:^9.0.0, google-auth-library@npm:^9.12.0, google-auth-library@npm:^9.3.0": - version: 9.15.1 - resolution: "google-auth-library@npm:9.15.1" - dependencies: - base64-js: ^1.3.0 - ecdsa-sig-formatter: ^1.0.11 - gaxios: ^6.1.1 - gcp-metadata: ^6.1.0 - gtoken: ^7.0.0 - jws: ^4.0.0 - checksum: 1c7660b7d21504a58b6b7780b0ca56f07a4d2b68d2ca14e5c4beaedd45cc4898baa8df1cfaf4dac15413a8db3cecc00e84662d8a327f9b9488ff2df7d8d23c84 - languageName: node - linkType: hard - -"google-gax@npm:^4.0.3": - version: 4.6.1 - resolution: "google-gax@npm:4.6.1" - dependencies: - "@grpc/grpc-js": ^1.10.9 - "@grpc/proto-loader": ^0.7.13 - "@types/long": ^4.0.0 - abort-controller: ^3.0.0 - duplexify: ^4.0.0 - google-auth-library: ^9.3.0 - node-fetch: ^2.7.0 - object-hash: ^3.0.0 - proto3-json-serializer: ^2.0.2 - protobufjs: ^7.3.2 - retry-request: ^7.0.0 - uuid: ^9.0.1 - checksum: b46479b3160b9a71f73c82ef100a54f1b8f40ac1e43d819eef307022cc17d67a575764eef94fe72f379cfd10f818ccc7b94c80eb15910d5560c0a1f77bf36728 - languageName: node - linkType: hard - -"google-gax@npm:^5.0.0": - version: 5.0.1 - resolution: "google-gax@npm:5.0.1" - dependencies: - "@grpc/grpc-js": ^1.12.6 - "@grpc/proto-loader": ^0.7.13 - abort-controller: ^3.0.0 - duplexify: ^4.1.3 - google-auth-library: ^10.1.0 - google-logging-utils: ^1.1.1 - node-fetch: ^3.3.2 - object-hash: ^3.0.0 - proto3-json-serializer: ^3.0.0 - protobufjs: ^7.5.3 - retry-request: ^8.0.0 - checksum: 0e876061b67ef79c4e7a2dd4d9bddd36ce7dff2017801951a939cc5d0191d2f2f2ea31569df6af02ac84795e5a19b875c72b37c596178b6b629f546696ef357d - languageName: node - linkType: hard - -"google-logging-utils@npm:^0.0.2": - version: 0.0.2 - resolution: "google-logging-utils@npm:0.0.2" - checksum: 270de74cde8abe0a6639b4bebbb6db2c7d8a495cf14b23779450d3d010e759f3ffcb7eb200b1bcb05da1897fa63f3e336df2274f02fc69d3bb388019fa2b3134 - languageName: node - linkType: hard - -"google-logging-utils@npm:^1.0.0, google-logging-utils@npm:^1.1.1": - version: 1.1.1 - resolution: "google-logging-utils@npm:1.1.1" - checksum: a1739b445963e82f5894d98fe0b0c3ef393670c6ae8b47810dee69d1d10697cad61b05cd94faf4d963c8207edc0694dc2b3b168c9cca984764801ca47b68333f - languageName: node - linkType: hard - "gopd@npm:^1.0.1, gopd@npm:^1.2.0": version: 1.2.0 resolution: "gopd@npm:1.2.0" @@ -13374,26 +10645,6 @@ __metadata: languageName: node linkType: hard -"gtoken@npm:^7.0.0": - version: 7.1.0 - resolution: "gtoken@npm:7.1.0" - dependencies: - gaxios: ^6.0.0 - jws: ^4.0.0 - checksum: 1f338dced78f9d895ea03cd507454eb5a7b77e841ecd1d45e44483b08c1e64d16a9b0342358d37586d87462ffc2d5f5bff5dfe77ed8d4f0aafc3b5b0347d5d16 - languageName: node - linkType: hard - -"gtoken@npm:^8.0.0": - version: 8.0.0 - resolution: "gtoken@npm:8.0.0" - dependencies: - gaxios: ^7.0.0 - jws: ^4.0.0 - checksum: 6825476d404fc6d1577799e222f44824787da96865a5929ec44d18ef5fbe22f3053433c79dfef3c9d944c8a87e78b85b48c31cae7fe7e3cc02f1aed35d160b10 - languageName: node - linkType: hard - "handlebars@npm:^4.7.7": version: 4.7.8 resolution: "handlebars@npm:4.7.8" @@ -13588,13 +10839,6 @@ __metadata: languageName: node linkType: hard -"html-entities@npm:^2.5.2": - version: 2.6.0 - resolution: "html-entities@npm:2.6.0" - checksum: 720643f7954019c80911430a7df2728524c07080edfe812610bfc5d8191cd772b470bee0ee151bf7426679314ae53cf28a1c845d702123714e625a8565b26567 - languageName: node - linkType: hard - "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -13622,17 +10866,6 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" - dependencies: - "@tootallnate/once": 2 - agent-base: 6 - debug: 4 - checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 - languageName: node - linkType: hard - "http-proxy-agent@npm:^7.0.0, http-proxy-agent@npm:^7.0.1": version: 7.0.2 resolution: "http-proxy-agent@npm:7.0.2" @@ -13643,17 +10876,7 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:^5.0.0": - version: 5.0.1 - resolution: "https-proxy-agent@npm:5.0.1" - dependencies: - agent-base: 6 - debug: 4 - checksum: 571fccdf38184f05943e12d37d6ce38197becdd69e58d03f43637f7fa1269cf303a7d228aa27e5b27bbd3af8f09fd938e1c91dcfefff2df7ba77c20ed8dfc765 - languageName: node - linkType: hard - -"https-proxy-agent@npm:^7.0.0, https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6": +"https-proxy-agent@npm:^7.0.1, https-proxy-agent@npm:^7.0.5, https-proxy-agent@npm:^7.0.6": version: 7.0.6 resolution: "https-proxy-agent@npm:7.0.6" dependencies: @@ -13720,13 +10943,20 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.0.4, ignore@npm:^5.0.5, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": +"ignore@npm:^5.0.5, ignore@npm:^5.2.0, ignore@npm:^5.2.4, ignore@npm:^5.3.1": version: 5.3.2 resolution: "ignore@npm:5.3.2" checksum: 2acfd32a573260ea522ea0bfeff880af426d68f6831f973129e2ba7363f422923cf53aab62f8369cbf4667c7b25b6f8a3761b34ecdb284ea18e87a5262a865be languageName: node linkType: hard +"ignore@npm:^7.0.5": + version: 7.0.5 + resolution: "ignore@npm:7.0.5" + checksum: d0862bf64d3d58bf34d5fb0a9f725bec9ca5ce8cd1aecc8f28034269e8f69b8009ffd79ca3eda96962a6a444687781cd5efdb8c7c8ddc0a6996e36d31c217f14 + languageName: node + linkType: hard + "image-size@npm:^1.0.2": version: 1.2.1 resolution: "image-size@npm:1.2.1" @@ -13861,21 +11091,6 @@ __metadata: languageName: node linkType: hard -"inquirer-checkbox-plus-prompt@npm:^1.4.2": - version: 1.4.2 - resolution: "inquirer-checkbox-plus-prompt@npm:1.4.2" - dependencies: - chalk: 4.1.2 - cli-cursor: ^3.1.0 - figures: ^3.0.0 - lodash: ^4.17.5 - rxjs: ^6.6.7 - peerDependencies: - inquirer: < 9.x - checksum: 0bdadc94e4bd13cc3b918b3a7d343376939e5713ccceb51b28293649bd51c476aaa6aab6cafecf89c784cffcaf879c307ab3830a12d3e54ba7a26c6e81c85d27 - languageName: node - linkType: hard - "inquirer@npm:12.9.6": version: 12.9.6 resolution: "inquirer@npm:12.9.6" @@ -14089,15 +11304,6 @@ __metadata: languageName: node linkType: hard -"is-docker@npm:^3.0.0": - version: 3.0.0 - resolution: "is-docker@npm:3.0.0" - bin: - is-docker: cli.js - checksum: b698118f04feb7eaf3338922bd79cba064ea54a1c3db6ec8c0c8d8ee7613e7e5854d802d3ef646812a8a3ace81182a085dfa0a71cc68b06f3fa794b9783b3c90 - languageName: node - linkType: hard - "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -14176,17 +11382,6 @@ __metadata: languageName: node linkType: hard -"is-inside-container@npm:^1.0.0": - version: 1.0.0 - resolution: "is-inside-container@npm:1.0.0" - dependencies: - is-docker: ^3.0.0 - bin: - is-inside-container: cli.js - checksum: c50b75a2ab66ab3e8b92b3bc534e1ea72ca25766832c0623ac22d134116a98bcf012197d1caabe1d1c4bd5f84363d4aa5c36bb4b585fbcaf57be172cd10a1a03 - languageName: node - linkType: hard - "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -14440,15 +11635,6 @@ __metadata: languageName: node linkType: hard -"is-wsl@npm:^3.1.0": - version: 3.1.0 - resolution: "is-wsl@npm:3.1.0" - dependencies: - is-inside-container: ^1.0.0 - checksum: f9734c81f2f9cf9877c5db8356bfe1ff61680f1f4c1011e91278a9c0564b395ae796addb4bf33956871041476ec82c3e5260ed57b22ac91794d4ae70a1d2f0a9 - languageName: node - linkType: hard - "isarray@npm:^2.0.5": version: 2.0.5 resolution: "isarray@npm:2.0.5" @@ -14702,7 +11888,7 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:>=30.0.0 < 31, jest-diff@npm:^30.0.2": +"jest-diff@npm:>=30.0.0 < 31, jest-diff@npm:^30.0.2, jest-diff@npm:^30.2.0": version: 30.2.0 resolution: "jest-diff@npm:30.2.0" dependencies: @@ -15096,26 +12282,14 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:3.13.1": - version: 3.13.1 - resolution: "js-yaml@npm:3.13.1" - dependencies: - argparse: ^1.0.7 - esprima: ^4.0.0 - bin: - js-yaml: bin/js-yaml.js - checksum: 7511b764abb66d8aa963379f7d2a404f078457d106552d05a7b556d204f7932384e8477513c124749fa2de52eb328961834562bd09924902c6432e40daa408bc - languageName: node - linkType: hard - -"js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": - version: 4.1.0 - resolution: "js-yaml@npm:4.1.0" +"js-yaml@npm:4.1.1": + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" dependencies: argparse: ^2.0.1 bin: js-yaml: bin/js-yaml.js - checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a + checksum: ea2339c6930fe048ec31b007b3c90be2714ab3e7defcc2c27ebf30c74fd940358f29070b4345af0019ef151875bf3bc3f8644bea1bab0372652b5044813ac02d languageName: node linkType: hard @@ -15131,6 +12305,17 @@ __metadata: languageName: node linkType: hard +"js-yaml@npm:^4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: ^2.0.1 + bin: + js-yaml: bin/js-yaml.js + checksum: c7830dfd456c3ef2c6e355cc5a92e6700ceafa1d14bba54497b34a99f0376cecbb3e9ac14d3e5849b426d5a5140709a66237a8c991c675431271c4ce5504151a + languageName: node + linkType: hard + "jsbn@npm:1.1.0": version: 1.1.0 resolution: "jsbn@npm:1.1.0" @@ -15271,13 +12456,6 @@ __metadata: languageName: node linkType: hard -"json-parse-even-better-errors@npm:^4.0.0": - version: 4.0.0 - resolution: "json-parse-even-better-errors@npm:4.0.0" - checksum: da1ae7ef0cc9db02972a06a71322f26bdcda5d7f648c23b28ce7f158ba35707461bcbd91945d8aace10d8d79c383b896725c65ffa410242352692328aa9b5edf - languageName: node - linkType: hard - "json-parse-even-better-errors@npm:^5.0.0": version: 5.0.0 resolution: "json-parse-even-better-errors@npm:5.0.0" @@ -15406,24 +12584,6 @@ __metadata: languageName: node linkType: hard -"jsonwebtoken@npm:^9.0.0": - version: 9.0.2 - resolution: "jsonwebtoken@npm:9.0.2" - dependencies: - jws: ^3.2.2 - lodash.includes: ^4.3.0 - lodash.isboolean: ^3.0.3 - lodash.isinteger: ^4.0.4 - lodash.isnumber: ^3.0.3 - lodash.isplainobject: ^4.0.6 - lodash.isstring: ^4.0.1 - lodash.once: ^4.0.0 - ms: ^2.1.1 - semver: ^7.5.4 - checksum: fc739a6a8b33f1974f9772dca7f8493ca8df4cc31c5a09dcfdb7cff77447dcf22f4236fb2774ef3fe50df0abeb8e1c6f4c41eba82f500a804ab101e2fbc9d61a - languageName: node - linkType: hard - "jsx-ast-utils@npm:^2.4.1 || ^3.0.0": version: 3.3.5 resolution: "jsx-ast-utils@npm:3.3.5" @@ -15462,27 +12622,6 @@ __metadata: languageName: node linkType: hard -"jwa@npm:^2.0.1": - version: 2.0.1 - resolution: "jwa@npm:2.0.1" - dependencies: - buffer-equal-constant-time: ^1.0.1 - ecdsa-sig-formatter: 1.0.11 - safe-buffer: ^5.0.1 - checksum: 6a9828c054c407f6718057089bd3d46dfcb1394e1553e3867abd4579dbec7728b4b0759e7253422ab7d824d95615a86427b35c43f94b83fc3a76470ca4bd2037 - languageName: node - linkType: hard - -"jws@npm:^4.0.1": - version: 4.0.1 - resolution: "jws@npm:4.0.1" - dependencies: - jwa: ^2.0.1 - safe-buffer: ^5.0.1 - checksum: c33a060b2cce1e0e49f85054a49a951f9d52a9e2ae732d720f0fc51843c9ac07a68aacd8e9d086ef4c7c4437d42978b698b57a3e7c9bc4a91c0b74276ea85a9a - languageName: node - linkType: hard - "keyv@npm:^4.5.3": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -15529,15 +12668,15 @@ __metadata: languageName: node linkType: hard -"lerna@npm:9.0.0": - version: 9.0.0 - resolution: "lerna@npm:9.0.0" +"lerna@npm:9.0.3": + version: 9.0.3 + resolution: "lerna@npm:9.0.3" dependencies: - "@lerna/create": 9.0.0 - "@npmcli/arborist": 9.1.4 - "@npmcli/package-json": 7.0.0 - "@npmcli/run-script": 10.0.0 - "@nx/devkit": ">=21.5.2 < 22.0.0" + "@lerna/create": 9.0.3 + "@npmcli/arborist": 9.1.6 + "@npmcli/package-json": 7.0.2 + "@npmcli/run-script": 10.0.2 + "@nx/devkit": ">=21.5.2 < 23.0.0" "@octokit/plugin-enterprise-rest": 6.0.1 "@octokit/rest": 20.1.2 aproba: 2.0.0 @@ -15567,18 +12706,18 @@ __metadata: is-ci: 3.0.1 is-stream: 2.0.0 jest-diff: ">=30.0.0 < 31" - js-yaml: 4.1.0 - libnpmaccess: 10.0.1 - libnpmpublish: 11.1.0 + js-yaml: 4.1.1 + libnpmaccess: 10.0.3 + libnpmpublish: 11.1.2 load-json-file: 6.2.0 make-dir: 4.0.0 make-fetch-happen: 15.0.2 minimatch: 3.0.5 multimatch: 5.0.0 - npm-package-arg: 13.0.0 - npm-packlist: 10.0.1 - npm-registry-fetch: 19.0.0 - nx: ">=21.5.3 < 22.0.0" + npm-package-arg: 13.0.1 + npm-packlist: 10.0.3 + npm-registry-fetch: 19.1.0 + nx: ">=21.5.3 < 23.0.0" p-map: 4.0.0 p-map-series: 2.1.0 p-pipe: 3.1.0 @@ -15612,7 +12751,7 @@ __metadata: yargs-parser: 21.1.1 bin: lerna: dist/cli.js - checksum: 4c788ccb909a80cfb69d194aea02789fcb3865b7cedf9d5c8f77c5bbc4dceeaf4eb01c822c529f23dc892af228b5420231c1af7c6ccd51d09224fd0db0d8f6aa + checksum: a9b28bb47b2acbd42e3f504ebe506e4b785e24cafeb248e56468aac9e2cc595e59aa10fd7def327118f0975836e40d6d4969611cc11ec6d619a4f0daf6259373 languageName: node linkType: hard @@ -15633,29 +12772,29 @@ __metadata: languageName: node linkType: hard -"libnpmaccess@npm:10.0.1": - version: 10.0.1 - resolution: "libnpmaccess@npm:10.0.1" +"libnpmaccess@npm:10.0.3": + version: 10.0.3 + resolution: "libnpmaccess@npm:10.0.3" dependencies: - npm-package-arg: ^12.0.0 - npm-registry-fetch: ^18.0.1 - checksum: c8f7e30f7a6d33a63c4989087c2fb2d135b271c293f6e0963cd92dc45cc5b8a1ca488c1d8828b8d1d66ea25c20ebd1b56d1b92e69eacb00845960b9acbf1b134 + npm-package-arg: ^13.0.0 + npm-registry-fetch: ^19.0.0 + checksum: 8bcd40e89bcec85250f3fe38049e14bc2fb0bd1769a7c3e2c82fd72c35730b322af30a031f6de85434651ce08731461c7beac752ed80e49df3fb689a5fdcc0b2 languageName: node linkType: hard -"libnpmpublish@npm:11.1.0": - version: 11.1.0 - resolution: "libnpmpublish@npm:11.1.0" +"libnpmpublish@npm:11.1.2": + version: 11.1.2 + resolution: "libnpmpublish@npm:11.1.2" dependencies: - "@npmcli/package-json": ^6.2.0 + "@npmcli/package-json": ^7.0.0 ci-info: ^4.0.0 - npm-package-arg: ^12.0.0 - npm-registry-fetch: ^18.0.1 + npm-package-arg: ^13.0.0 + npm-registry-fetch: ^19.0.0 proc-log: ^5.0.0 semver: ^7.3.7 - sigstore: ^3.0.0 + sigstore: ^4.0.0 ssri: ^12.0.0 - checksum: b33bd488f6ae04bb6bfbf747f77e3c30341565714104b706246757ce3703771e507516e6212559642442ee372a91bcbf627136f7b7d9cab0823aee2889dfcf3b + checksum: e9e21ed63339b5c54c373dda4915c82e24a4f17d86336d541a57891984fc4259b077a380eac60a8d943375d02908d885978f4a981bf966fa92d31f498bde6d3c languageName: node linkType: hard @@ -15779,13 +12918,6 @@ __metadata: languageName: node linkType: hard -"lodash.camelcase@npm:^4.3.0": - version: 4.3.0 - resolution: "lodash.camelcase@npm:4.3.0" - checksum: cb9227612f71b83e42de93eccf1232feeb25e705bdb19ba26c04f91e885bfd3dd5c517c4a97137658190581d3493ea3973072ca010aab7e301046d90740393d1 - languageName: node - linkType: hard - "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -15793,20 +12925,6 @@ __metadata: languageName: node linkType: hard -"lodash.includes@npm:^4.3.0": - version: 4.3.0 - resolution: "lodash.includes@npm:4.3.0" - checksum: 71092c130515a67ab3bd928f57f6018434797c94def7f46aafa417771e455ce3a4834889f4267b17887d7f75297dfabd96231bf704fd2b8c5096dc4a913568b6 - languageName: node - linkType: hard - -"lodash.isboolean@npm:^3.0.3": - version: 3.0.3 - resolution: "lodash.isboolean@npm:3.0.3" - checksum: b70068b4a8b8837912b54052557b21fc4774174e3512ed3c5b94621e5aff5eb6c68089d0a386b7e801d679cd105d2e35417978a5e99071750aa2ed90bffd0250 - languageName: node - linkType: hard - "lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" @@ -15821,13 +12939,6 @@ __metadata: languageName: node linkType: hard -"lodash.isinteger@npm:^4.0.4": - version: 4.0.4 - resolution: "lodash.isinteger@npm:4.0.4" - checksum: 6034821b3fc61a2ffc34e7d5644bb50c5fd8f1c0121c554c21ac271911ee0c0502274852845005f8651d51e199ee2e0cfebfe40aaa49c7fe617f603a8a0b1691 - languageName: node - linkType: hard - "lodash.ismatch@npm:^4.4.0": version: 4.4.0 resolution: "lodash.ismatch@npm:4.4.0" @@ -15835,13 +12946,6 @@ __metadata: languageName: node linkType: hard -"lodash.isnumber@npm:^3.0.3": - version: 3.0.3 - resolution: "lodash.isnumber@npm:3.0.3" - checksum: 913784275b565346255e6ae6a6e30b760a0da70abc29f3e1f409081585875105138cda4a429ff02577e1bc0a7ae2a90e0a3079a37f3a04c3d6c5aaa532f4cab2 - languageName: node - linkType: hard - "lodash.isobjectlike@npm:^4.0.0": version: 4.0.0 resolution: "lodash.isobjectlike@npm:4.0.0" @@ -15849,20 +12953,6 @@ __metadata: languageName: node linkType: hard -"lodash.isplainobject@npm:^4.0.6": - version: 4.0.6 - resolution: "lodash.isplainobject@npm:4.0.6" - checksum: 29c6351f281e0d9a1d58f1a4c8f4400924b4c79f18dfc4613624d7d54784df07efaff97c1ff2659f3e085ecf4fff493300adc4837553104cef2634110b0d5337 - languageName: node - linkType: hard - -"lodash.isstring@npm:^4.0.1": - version: 4.0.1 - resolution: "lodash.isstring@npm:4.0.1" - checksum: eaac87ae9636848af08021083d796e2eea3d02e80082ab8a9955309569cb3a463ce97fd281d7dc119e402b2e7d8c54a23914b15d2fc7fff56461511dc8937ba0 - languageName: node - linkType: hard - "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -15870,13 +12960,6 @@ __metadata: languageName: node linkType: hard -"lodash.once@npm:^4.0.0": - version: 4.1.1 - resolution: "lodash.once@npm:4.1.1" - checksum: d768fa9f9b4e1dc6453be99b753906f58990e0c45e7b2ca5a3b40a33111e5d17f6edf2f768786e2716af90a8e78f8f91431ab8435f761fef00f9b0c256f6d245 - languageName: node - linkType: hard - "lodash.sortby@npm:^4.7.0": version: 4.7.0 resolution: "lodash.sortby@npm:4.7.0" @@ -15891,7 +12974,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.17.x, lodash@npm:^4.17.21, lodash@npm:^4.17.5": +"lodash@npm:4.17.x, lodash@npm:^4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -15939,7 +13022,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.2.2": +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0": version: 10.4.3 resolution: "lru-cache@npm:10.4.3" checksum: 6476138d2125387a6d20f100608c2583d415a4f64a0fecf30c9e2dda976614f09cad4baa0842447bd37dd459a7bd27f57d9d8f8ce558805abd487c583f3d774a @@ -16016,7 +13099,7 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^14.0.0, make-fetch-happen@npm:^14.0.2, make-fetch-happen@npm:^14.0.3": +"make-fetch-happen@npm:^14.0.3": version: 14.0.3 resolution: "make-fetch-happen@npm:14.0.3" dependencies: @@ -16683,7 +13766,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.0.3": +"minimatch@npm:^10.0.3, minimatch@npm:^10.1.1": version: 10.1.1 resolution: "minimatch@npm:10.1.1" dependencies: @@ -16719,7 +13802,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.0, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": +"minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" dependencies: @@ -16966,7 +14049,7 @@ __metadata: languageName: node linkType: hard -"nan@npm:^2.19.0, nan@npm:^2.20.0": +"nan@npm:^2.19.0": version: 2.22.2 resolution: "nan@npm:2.22.2" dependencies: @@ -16975,6 +14058,15 @@ __metadata: languageName: node linkType: hard +"nan@npm:^2.23.0": + version: 2.24.0 + resolution: "nan@npm:2.24.0" + dependencies: + node-gyp: latest + checksum: ab4080188a2fe2bef0a1f3ce5c65a6c3d71fa23be08f4e0696dc256c5030c809d11569d5bcf28810148a7b0029c195c592b98b7b22c5e9e7e9aa0e71905a63b8 + languageName: node + linkType: hard + "nanoid@npm:3.3.8": version: 3.3.8 resolution: "nanoid@npm:3.3.8" @@ -17058,14 +14150,7 @@ __metadata: languageName: node linkType: hard -"node-domexception@npm:^1.0.0": - version: 1.0.0 - resolution: "node-domexception@npm:1.0.0" - checksum: ee1d37dd2a4eb26a8a92cd6b64dfc29caec72bff5e1ed9aba80c294f57a31ba4895a60fd48347cf17dd6e766da0ae87d75657dfd1f384ebfa60462c2283f5c7f - languageName: node - linkType: hard - -"node-fetch@npm:^2.2.0, node-fetch@npm:^2.6.9, node-fetch@npm:^2.7.0": +"node-fetch@npm:^2.2.0, node-fetch@npm:^2.7.0": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -17079,17 +14164,6 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^3.3.2": - version: 3.3.2 - resolution: "node-fetch@npm:3.3.2" - dependencies: - data-uri-to-buffer: ^4.0.0 - fetch-blob: ^3.1.4 - formdata-polyfill: ^4.0.10 - checksum: 06a04095a2ddf05b0830a0d5302699704d59bda3102894ea64c7b9d4c865ecdff2d90fd042df7f5bc40337266961cb6183dcc808ea4f3000d024f422b462da92 - languageName: node - linkType: hard - "node-forge@npm:^1": version: 1.3.1 resolution: "node-forge@npm:1.3.1" @@ -17198,6 +14272,17 @@ __metadata: languageName: node linkType: hard +"nopt@npm:^9.0.0": + version: 9.0.0 + resolution: "nopt@npm:9.0.0" + dependencies: + abbrev: ^4.0.0 + bin: + nopt: bin/nopt.js + checksum: 7a5d9ab0629eaec1944a95438cc4efa6418ed2834aa8eb21a1bea579a7d8ac3e30120131855376a96ef59ab0e23ad8e0bc94d3349770a95e5cb7119339f7c7fb + languageName: node + linkType: hard + "normalize-package-data@npm:^2.3.2, normalize-package-data@npm:^2.5.0": version: 2.5.0 resolution: "normalize-package-data@npm:2.5.0" @@ -17238,6 +14323,15 @@ __metadata: languageName: node linkType: hard +"npm-bundled@npm:^5.0.0": + version: 5.0.0 + resolution: "npm-bundled@npm:5.0.0" + dependencies: + npm-normalize-package-bin: ^5.0.0 + checksum: dd2e7535d6463bb4e65a1126564d0db9a54f6c9c47fee08e9583a611e3c6205626a4fa6ecc6fe7aea178e72787e5e236fdedb318f0defd4355bf5d5534640fca + languageName: node + linkType: hard + "npm-install-checks@npm:^7.1.0": version: 7.1.2 resolution: "npm-install-checks@npm:7.1.2" @@ -17270,15 +14364,15 @@ __metadata: languageName: node linkType: hard -"npm-package-arg@npm:13.0.0": - version: 13.0.0 - resolution: "npm-package-arg@npm:13.0.0" +"npm-package-arg@npm:13.0.1, npm-package-arg@npm:^13.0.0": + version: 13.0.1 + resolution: "npm-package-arg@npm:13.0.1" dependencies: hosted-git-info: ^9.0.0 proc-log: ^5.0.0 semver: ^7.3.5 validate-npm-package-name: ^6.0.0 - checksum: 6c2dc4029f6633300dfcc7223dcdcee713014e3702daee76410dfe48e8e93d4db35703721569fcec3fdeb03fefa398eb38b799d6e9af46b92cc8162827eb9fa7 + checksum: aba57acfaa0f18c42e3fe432cafd5e316b4dffa8c2c3ddaa36c2af3186968eb9cd89fcd5dfb18410fbbcb4ffc3b2b0cf02b2c0630d5642b33ab2f3fb500aa2b5 languageName: node linkType: hard @@ -17294,28 +14388,7 @@ __metadata: languageName: node linkType: hard -"npm-package-arg@npm:^13.0.0": - version: 13.0.1 - resolution: "npm-package-arg@npm:13.0.1" - dependencies: - hosted-git-info: ^9.0.0 - proc-log: ^5.0.0 - semver: ^7.3.5 - validate-npm-package-name: ^6.0.0 - checksum: aba57acfaa0f18c42e3fe432cafd5e316b4dffa8c2c3ddaa36c2af3186968eb9cd89fcd5dfb18410fbbcb4ffc3b2b0cf02b2c0630d5642b33ab2f3fb500aa2b5 - languageName: node - linkType: hard - -"npm-packlist@npm:10.0.1": - version: 10.0.1 - resolution: "npm-packlist@npm:10.0.1" - dependencies: - ignore-walk: ^8.0.0 - checksum: 051630a785fe91023ba28b2787c7e8c81d82008bba4eeaf183fdf0318961b24f5143247eb765f9a8a769de04f378dff842c9e3fdf5b61eccd9433c1229a9a523 - languageName: node - linkType: hard - -"npm-packlist@npm:^10.0.1": +"npm-packlist@npm:10.0.3, npm-packlist@npm:^10.0.1": version: 10.0.3 resolution: "npm-packlist@npm:10.0.3" dependencies: @@ -17349,39 +14422,7 @@ __metadata: languageName: node linkType: hard -"npm-registry-fetch@npm:19.0.0": - version: 19.0.0 - resolution: "npm-registry-fetch@npm:19.0.0" - dependencies: - "@npmcli/redact": ^3.0.0 - jsonparse: ^1.3.1 - make-fetch-happen: ^15.0.0 - minipass: ^7.0.2 - minipass-fetch: ^4.0.0 - minizlib: ^3.0.1 - npm-package-arg: ^13.0.0 - proc-log: ^5.0.0 - checksum: 3f8698ed049fc574d5036772cdf53a3de398550b8102253a76f565346ad792090a4261aa821dfe9246bff085d04ebc7de61ed843e3a1673f1198835dff654059 - languageName: node - linkType: hard - -"npm-registry-fetch@npm:^18.0.1": - version: 18.0.2 - resolution: "npm-registry-fetch@npm:18.0.2" - dependencies: - "@npmcli/redact": ^3.0.0 - jsonparse: ^1.3.1 - make-fetch-happen: ^14.0.0 - minipass: ^7.0.2 - minipass-fetch: ^4.0.0 - minizlib: ^3.0.1 - npm-package-arg: ^12.0.0 - proc-log: ^5.0.0 - checksum: 99d11962674f56ebf2e3a4623e486ec45db6cbc2bc3e1678afb3fbe0fe827ab668aeb04ee3e5aea0534e293a6ac98d01fd5a15dab8a3647e36c9c34342ff5211 - languageName: node - linkType: hard - -"npm-registry-fetch@npm:^19.0.0": +"npm-registry-fetch@npm:19.1.0, npm-registry-fetch@npm:^19.0.0": version: 19.1.0 resolution: "npm-registry-fetch@npm:19.1.0" dependencies: @@ -17422,21 +14463,21 @@ __metadata: languageName: node linkType: hard -"nx@npm:>=21.5.3 < 22.0.0": - version: 21.6.8 - resolution: "nx@npm:21.6.8" +"nx@npm:>=21.5.3 < 23.0.0": + version: 22.2.5 + resolution: "nx@npm:22.2.5" dependencies: "@napi-rs/wasm-runtime": 0.2.4 - "@nx/nx-darwin-arm64": 21.6.8 - "@nx/nx-darwin-x64": 21.6.8 - "@nx/nx-freebsd-x64": 21.6.8 - "@nx/nx-linux-arm-gnueabihf": 21.6.8 - "@nx/nx-linux-arm64-gnu": 21.6.8 - "@nx/nx-linux-arm64-musl": 21.6.8 - "@nx/nx-linux-x64-gnu": 21.6.8 - "@nx/nx-linux-x64-musl": 21.6.8 - "@nx/nx-win32-arm64-msvc": 21.6.8 - "@nx/nx-win32-x64-msvc": 21.6.8 + "@nx/nx-darwin-arm64": 22.2.5 + "@nx/nx-darwin-x64": 22.2.5 + "@nx/nx-freebsd-x64": 22.2.5 + "@nx/nx-linux-arm-gnueabihf": 22.2.5 + "@nx/nx-linux-arm64-gnu": 22.2.5 + "@nx/nx-linux-arm64-musl": 22.2.5 + "@nx/nx-linux-x64-gnu": 22.2.5 + "@nx/nx-linux-x64-musl": 22.2.5 + "@nx/nx-win32-arm64-msvc": 22.2.5 + "@nx/nx-win32-x64-msvc": 22.2.5 "@yarnpkg/lockfile": ^1.1.0 "@yarnpkg/parsers": 3.0.2 "@zkochan/js-yaml": 0.0.7 @@ -17451,7 +14492,7 @@ __metadata: figures: 3.2.0 flat: ^5.0.2 front-matter: ^4.0.2 - ignore: ^5.0.4 + ignore: ^7.0.5 jest-diff: ^30.0.2 jsonc-parser: 3.2.0 lines-and-columns: 2.0.3 @@ -17461,7 +14502,7 @@ __metadata: open: ^8.4.0 ora: 5.3.0 resolve.exports: 2.0.3 - semver: ^7.5.3 + semver: ^7.6.3 string-width: ^4.2.3 tar-stream: ~2.2.0 tmp: ~0.2.1 @@ -17503,7 +14544,7 @@ __metadata: bin: nx: bin/nx.js nx-cloud: bin/nx-cloud.js - checksum: 4b07f3c6b55f2c4bb25ad1baa2190570f41ca78a0c99045d68203d7372c69bc82344257bdb0e001dd4a8b426832a3d527884e665cd84d81449db21118fad8956 + checksum: 0638dd09e46f0327c9046ecacbd2560f7057c3654020a9d76ad371fb02e796a96f2ce7eb03918b123ff65e2e617a3584f790ca4332b0b49250886b1019957e7e languageName: node linkType: hard @@ -17532,13 +14573,6 @@ __metadata: languageName: node linkType: hard -"object-hash@npm:^3.0.0": - version: 3.0.0 - resolution: "object-hash@npm:3.0.0" - checksum: 80b4904bb3857c52cc1bfd0b52c0352532ca12ed3b8a6ff06a90cd209dfda1b95cee059a7625eb9da29537027f68ac4619363491eedb2f5d3dddbba97494fd6c - languageName: node - linkType: hard - "object-inspect@npm:^1.13.3, object-inspect@npm:^1.13.4": version: 1.13.4 resolution: "object-inspect@npm:1.13.4" @@ -17614,7 +14648,7 @@ __metadata: languageName: node linkType: hard -"on-finished@npm:2.4.1, on-finished@npm:^2.3.0": +"on-finished@npm:2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" dependencies: @@ -17657,18 +14691,6 @@ __metadata: languageName: node linkType: hard -"open@npm:^10.1.0": - version: 10.1.2 - resolution: "open@npm:10.1.2" - dependencies: - default-browser: ^5.2.1 - define-lazy-prop: ^3.0.0 - is-inside-container: ^1.0.0 - is-wsl: ^3.1.0 - checksum: cb40d9786e8c679a1c647c62ad3642bbf1a8cc986c3ec970e99ed13d279c93328ea46bf756db64ea465d2d707815d4e792e7ba906830c3439cc8577cdefb8610 - languageName: node - linkType: hard - "open@npm:^6.2.0": version: 6.4.0 resolution: "open@npm:6.4.0" @@ -17958,6 +14980,13 @@ __metadata: languageName: node linkType: hard +"package-manager-detector@npm:^1.3.0": + version: 1.6.0 + resolution: "package-manager-detector@npm:1.6.0" + checksum: 154d55225e70e32582f59b5d4a46d25716f0730a14d7e4b6f0fd76c870c720cc6f448d2becca06a15f2042492f0293cf26e5ad8fcd85d0eab0af3b9b46c0b43a + languageName: node + linkType: hard + "packageurl-js@npm:^2.0.1": version: 2.0.1 resolution: "packageurl-js@npm:2.0.1" @@ -18019,6 +15048,33 @@ __metadata: languageName: node linkType: hard +"pacote@npm:^21.0.2": + version: 21.0.4 + resolution: "pacote@npm:21.0.4" + dependencies: + "@npmcli/git": ^7.0.0 + "@npmcli/installed-package-contents": ^4.0.0 + "@npmcli/package-json": ^7.0.0 + "@npmcli/promise-spawn": ^9.0.0 + "@npmcli/run-script": ^10.0.0 + cacache: ^20.0.0 + fs-minipass: ^3.0.0 + minipass: ^7.0.2 + npm-package-arg: ^13.0.0 + npm-packlist: ^10.0.1 + npm-pick-manifest: ^11.0.1 + npm-registry-fetch: ^19.0.0 + proc-log: ^6.0.0 + promise-retry: ^2.0.1 + sigstore: ^4.0.0 + ssri: ^13.0.0 + tar: ^7.4.3 + bin: + pacote: bin/index.js + checksum: 09e86b71376cbc2d8089e3028d52b6ba41cb034f7f1d2a0a7f8958376eba36426ec75424b94d04672dcfb2fa2fe2120e0ac1dd6c61d64a2b8fd438a0346cc67a + languageName: node + linkType: hard + "pako@npm:~1.0.2": version: 1.0.11 resolution: "pako@npm:1.0.11" @@ -18044,14 +15100,14 @@ __metadata: languageName: node linkType: hard -"parse-conflict-json@npm:^4.0.0": - version: 4.0.0 - resolution: "parse-conflict-json@npm:4.0.0" +"parse-conflict-json@npm:^5.0.1": + version: 5.0.1 + resolution: "parse-conflict-json@npm:5.0.1" dependencies: - json-parse-even-better-errors: ^4.0.0 + json-parse-even-better-errors: ^5.0.0 just-diff: ^6.0.0 just-diff-apply: ^5.2.0 - checksum: ee4e1da52a54a127460713c82a12fffc1071dd2945350ebd9e203337b944901d086d515f7b15f42c6c5d9cf031de76eae0ebf5338fe8339d5695a7882d0aeba9 + checksum: 4d627fc264fb09fa0897e30e7e33d95c0742efe69ecb3bf2c065f8eb04b402317f2182a2d9e800c797dec17f1269b98c8ec6e96e81bc53aba74f077dd19e5342 languageName: node linkType: hard @@ -18444,10 +15500,10 @@ __metadata: languageName: node linkType: hard -"proggy@npm:^3.0.0": - version: 3.0.0 - resolution: "proggy@npm:3.0.0" - checksum: 8c274b56e8eaaa1f59ea938df3c501d80d24fbfd3dffd1de42a66e2657d131f5e0b7165127457a3a7b38e1dcc71a81060a685c6840001136ecad36cec900bfc8 +"proggy@npm:^4.0.0": + version: 4.0.0 + resolution: "proggy@npm:4.0.0" + checksum: 9f63245658b588a1290b1f1a092af5ac06ba005580b874d4c3e525217b30fbdf8c248403f963e8d4521319e080c9aa4c13e45fce3e6e2a9a38eb61dab4f88a83 languageName: node linkType: hard @@ -18523,25 +15579,7 @@ __metadata: languageName: node linkType: hard -"proto3-json-serializer@npm:^2.0.2": - version: 2.0.2 - resolution: "proto3-json-serializer@npm:2.0.2" - dependencies: - protobufjs: ^7.2.5 - checksum: 21b8aa65be6dac2bb24920e5bdabef48b249bdf65b1498ae7e69ac4e70722275b083cd60a21d2b4be3ead9d768de2f6f5fb6b188bd177d51c824a539b5ba55cc - languageName: node - linkType: hard - -"proto3-json-serializer@npm:^3.0.0": - version: 3.0.1 - resolution: "proto3-json-serializer@npm:3.0.1" - dependencies: - protobufjs: ^7.4.0 - checksum: fa91b1a65ce58bb688ed26b2f881a3d0a46e0da09cdb377e4a69934ff2b769827944ee8beb332ea3086537b1bfe5be13c24eb00e65750ecb649083731a7acce1 - languageName: node - linkType: hard - -"protobufjs@npm:^7.2.5, protobufjs@npm:^7.3.2, protobufjs@npm:^7.4.0, protobufjs@npm:^7.5.3": +"protobufjs@npm:^7.5.3": version: 7.5.3 resolution: "protobufjs@npm:7.5.3" dependencies: @@ -18601,17 +15639,6 @@ __metadata: languageName: node linkType: hard -"pumpify@npm:^2.0.1": - version: 2.0.1 - resolution: "pumpify@npm:2.0.1" - dependencies: - duplexify: ^4.1.1 - inherits: ^2.0.3 - pump: ^3.0.0 - checksum: cfc96f5307ee828ef8e6eca9fe9e1ae1de0a23ca55688bfe71ea376bc126418073dab870f02b433617f421c4545726b39e31295fce9a99b78bda5f0e527a7c11 - languageName: node - linkType: hard - "punycode@npm:^2.1.0": version: 2.3.1 resolution: "punycode@npm:2.3.1" @@ -19227,20 +16254,10 @@ __metadata: languageName: node linkType: hard -"read-cmd-shim@npm:^5.0.0": - version: 5.0.0 - resolution: "read-cmd-shim@npm:5.0.0" - checksum: 7b403e009373d0e441c4ed3364f791680c6846fb6d7c4041e5af2f4da45b07a0325c43c60b3066e16e567d2c3a37f1b6096ed0e93a7b5e575806df0b860ff308 - languageName: node - linkType: hard - -"read-package-json-fast@npm:^4.0.0": - version: 4.0.0 - resolution: "read-package-json-fast@npm:4.0.0" - dependencies: - json-parse-even-better-errors: ^4.0.0 - npm-normalize-package-bin: ^4.0.0 - checksum: bf0becd7d0b652dcc5874b466d1dbd98313180e89505c072f35ff48a1ad6bdaf2427143301e1924d64e4af5064cda8be5df16f14de882f03130e29051bbaab87 +"read-cmd-shim@npm:^6.0.0": + version: 6.0.0 + resolution: "read-cmd-shim@npm:6.0.0" + checksum: 937161ab54d65f3749a1a704b1a06defde1df811353521f998e6afdd22356477ba4aa58bacc9f68ca6cf1bb13f199af3fbc258247dca9c67c3497cb44d7e1105 languageName: node linkType: hard @@ -19655,28 +16672,6 @@ __metadata: languageName: node linkType: hard -"retry-request@npm:^7.0.0": - version: 7.0.2 - resolution: "retry-request@npm:7.0.2" - dependencies: - "@types/request": ^2.48.8 - extend: ^3.0.2 - teeny-request: ^9.0.0 - checksum: 2d7307422333f548e5f40524978a344b62193714f6209c4f6a41057ae279804eb9bc8e0a277791e7b6f2d5d76068bdaca8590662a909cf1e6cfc3ab789e4c6b6 - languageName: node - linkType: hard - -"retry-request@npm:^8.0.0": - version: 8.0.0 - resolution: "retry-request@npm:8.0.0" - dependencies: - "@types/request": ^2.48.12 - extend: ^3.0.2 - teeny-request: ^10.0.0 - checksum: 29cf787917802a80d1f8ce802520927106e084413c63f6bc133620c9380fda17ac4bd3c4365d119f176ca9d1defe373f3353c2e3dd8aab6056b9c05b4ba78bdf - languageName: node - linkType: hard - "retry@npm:0.12.0, retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -19770,13 +16765,6 @@ __metadata: languageName: node linkType: hard -"run-applescript@npm:^7.0.0": - version: 7.0.0 - resolution: "run-applescript@npm:7.0.0" - checksum: b02462454d8b182ad4117e5d4626e9e6782eb2072925c9fac582170b0627ae3c1ea92ee9b2df7daf84b5e9ffe14eb1cf5fb70bc44b15c8a0bfcdb47987e2410c - languageName: node - linkType: hard - "run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" @@ -19800,15 +16788,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^6.6.7": - version: 6.6.7 - resolution: "rxjs@npm:6.6.7" - dependencies: - tslib: ^1.9.0 - checksum: bc334edef1bb8bbf56590b0b25734ba0deaf8825b703256a93714308ea36dff8a11d25533671adf8e104e5e8f256aa6fdfe39b2e248cdbd7a5f90c260acbbd1b - languageName: node - linkType: hard - "rxjs@npm:^7.5.5, rxjs@npm:^7.8.2": version: 7.8.2 resolution: "rxjs@npm:7.8.2" @@ -19831,7 +16810,7 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": +"safe-buffer@npm:5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -19956,7 +16935,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.7.2": +"semver@npm:^7.6.3, semver@npm:^7.7.2": version: 7.7.3 resolution: "semver@npm:7.7.3" bin: @@ -20176,20 +17155,6 @@ __metadata: languageName: node linkType: hard -"sigstore@npm:^3.0.0": - version: 3.1.0 - resolution: "sigstore@npm:3.1.0" - dependencies: - "@sigstore/bundle": ^3.1.0 - "@sigstore/core": ^2.0.0 - "@sigstore/protobuf-specs": ^0.4.0 - "@sigstore/sign": ^3.1.0 - "@sigstore/tuf": ^3.1.0 - "@sigstore/verify": ^2.1.0 - checksum: 52a1d88b0e48f4008ef8c7135cd9a6edbca3c0fcda0234a73a304eeff57ad6e37ff605cc0a21ad2cffd8bdb510742e556ba3ef04a60bd968f9821ec3ace00f93 - languageName: node - linkType: hard - "sigstore@npm:^4.0.0": version: 4.0.0 resolution: "sigstore@npm:4.0.0" @@ -20444,31 +17409,20 @@ __metadata: languageName: node linkType: hard -"ssh2-streams@npm:0.4.10": - version: 0.4.10 - resolution: "ssh2-streams@npm:0.4.10" - dependencies: - asn1: ~0.2.0 - bcrypt-pbkdf: ^1.0.2 - streamsearch: ~0.1.2 - checksum: dad86d7aa83c08a8f57feedfda26f4032a3f5e605a1d2018da835ced4ebb003739962aa416066f374a006d941067e4215f8b84a8ec81e888156c9274445ae5db - languageName: node - linkType: hard - -"ssh2@npm:^1.15.0": - version: 1.16.0 - resolution: "ssh2@npm:1.16.0" +"ssh2@npm:^1.16.0": + version: 1.17.0 + resolution: "ssh2@npm:1.17.0" dependencies: asn1: ^0.2.6 bcrypt-pbkdf: ^1.0.2 cpu-features: ~0.0.10 - nan: ^2.20.0 + nan: ^2.23.0 dependenciesMeta: cpu-features: optional: true nan: optional: true - checksum: c024c4a432aae2457852037f31c0d9bec323fb062ace3a31e4a6dd6c55842246c80e7d20ff93ffed22dde1e523250d8438bc2f7d4a1450cf4fa4887818176f0e + checksum: 1661b020e367e358603187a1efbb7628cb9b2f75543f60e354ede67be1216d331f2b99a73c57fb01a04be050a1e06fc97d04760d1396ea658ca816ddf80df9a9 languageName: node linkType: hard @@ -20502,6 +17456,15 @@ __metadata: languageName: node linkType: hard +"ssri@npm:^13.0.0": + version: 13.0.0 + resolution: "ssri@npm:13.0.0" + dependencies: + minipass: ^7.0.3 + checksum: 9705dff9e686b11f3035fb4c3d44ce690359a15a54adcd6a18951f2763f670877321178dc72c37a2b804dba3287ecaa48726dbd0cff79b2715b1cc24521b3af3 + languageName: node + linkType: hard + "stack-utils@npm:^2.0.3": version: 2.0.6 resolution: "stack-utils@npm:2.0.6" @@ -20558,29 +17521,6 @@ __metadata: languageName: node linkType: hard -"stream-events@npm:^1.0.5": - version: 1.0.5 - resolution: "stream-events@npm:1.0.5" - dependencies: - stubs: ^3.0.0 - checksum: 969ce82e34bfbef5734629cc06f9d7f3705a9ceb8fcd6a526332f9159f1f8bbfdb1a453f3ced0b728083454f7706adbbe8428bceb788a0287ca48ba2642dc3fc - languageName: node - linkType: hard - -"stream-shift@npm:^1.0.2": - version: 1.0.3 - resolution: "stream-shift@npm:1.0.3" - checksum: a24c0a3f66a8f9024bd1d579a533a53be283b4475d4e6b4b3211b964031447bdf6532dd1f3c2b0ad66752554391b7c62bd7ca4559193381f766534e723d50242 - languageName: node - linkType: hard - -"streamsearch@npm:~0.1.2": - version: 0.1.2 - resolution: "streamsearch@npm:0.1.2" - checksum: d2db57cbfbf7947ab9c75a7b4c80a8ef8d24850cf0a1a24258bb6956c97317ce1eab7dbcbf9c5aba3e6198611af1053b02411057bbedb99bf9c64b8275248997 - languageName: node - linkType: hard - "strict-uri-encode@npm:^2.0.0": version: 2.0.0 resolution: "strict-uri-encode@npm:2.0.0" @@ -20792,13 +17732,6 @@ __metadata: languageName: node linkType: hard -"stubs@npm:^3.0.0": - version: 3.0.0 - resolution: "stubs@npm:3.0.0" - checksum: dec7b82186e3743317616235c59bfb53284acc312cb9f4c3e97e2205c67a5c158b0ca89db5927e52351582e90a2672822eeaec9db396e23e56893d2a8676e024 - languageName: node - linkType: hard - "sudo-prompt@npm:^9.0.0": version: 9.2.1 resolution: "sudo-prompt@npm:9.2.1" @@ -20950,31 +17883,6 @@ __metadata: languageName: node linkType: hard -"teeny-request@npm:^10.0.0": - version: 10.1.0 - resolution: "teeny-request@npm:10.1.0" - dependencies: - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 - node-fetch: ^3.3.2 - stream-events: ^1.0.5 - checksum: d966f1bd8b3a348d391d981533cf3aa54ac733b02905103a570f66108b86b99fed4d1bb8d4ec6ac20fc13335ea750611826ff4da61d53e676d1d4d35d2c5266a - languageName: node - linkType: hard - -"teeny-request@npm:^9.0.0": - version: 9.0.0 - resolution: "teeny-request@npm:9.0.0" - dependencies: - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 - node-fetch: ^2.6.9 - stream-events: ^1.0.5 - uuid: ^9.0.0 - checksum: 9cb0ad83f9ca6ce6515b3109cbb30ceb2533cdeab8e41c3a0de89f509bd92c5a9aabd27b3adf7f3e49516e106a358859b19fa4928a1937a4ab95809ccb7d52eb - languageName: node - linkType: hard - "temp-dir@npm:1.0.0": version: 1.0.0 resolution: "temp-dir@npm:1.0.0" @@ -21078,6 +17986,13 @@ __metadata: languageName: node linkType: hard +"tinyexec@npm:^1.0.1": + version: 1.0.2 + resolution: "tinyexec@npm:1.0.2" + checksum: af22de2191cc70bb782eef29bbba7cf6ac16664e550b547b0db68804f988eeb2c70e12fbb7d2d688ee994b28ba831d746e9eded98c3d10042fd3a9b8de208514 + languageName: node + linkType: hard + "tinyglobby@npm:0.2.12": version: 0.2.12 resolution: "tinyglobby@npm:0.2.12" @@ -21230,14 +18145,14 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^1.8.1, tslib@npm:^1.9.0": +"tslib@npm:^1.8.1": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd languageName: node linkType: hard -"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.6.2, tslib@npm:^2.8.1": +"tslib@npm:^2.0.1, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0": version: 2.8.1 resolution: "tslib@npm:2.8.1" checksum: e4aba30e632b8c8902b47587fd13345e2827fa639e7c3121074d5ee0880723282411a8838f830b55100cbe4517672f84a2472667d355b81e8af165a55dc6203a @@ -21264,17 +18179,6 @@ __metadata: languageName: node linkType: hard -"tuf-js@npm:^3.0.1": - version: 3.1.0 - resolution: "tuf-js@npm:3.1.0" - dependencies: - "@tufjs/models": 3.0.1 - debug: ^4.4.1 - make-fetch-happen: ^14.0.3 - checksum: 1a45a055e97041459aacca576e439baf0517d88db3eb600479b57fb60c2403dc193048135f1a2612378dc0b00c8437ec990b132d404b0a5e7471bf2e2824d2aa - languageName: node - linkType: hard - "tuf-js@npm:^4.0.0": version: 4.0.0 resolution: "tuf-js@npm:4.0.0" @@ -21683,7 +18587,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^8.0.0, uuid@npm:^8.3.0, uuid@npm:^8.3.2": +"uuid@npm:^8.3.2": version: 8.3.2 resolution: "uuid@npm:8.3.2" bin: @@ -21692,7 +18596,7 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^9.0.0, uuid@npm:^9.0.1": +"uuid@npm:^9.0.0": version: 9.0.1 resolution: "uuid@npm:9.0.1" bin: @@ -21782,13 +18686,6 @@ __metadata: languageName: node linkType: hard -"web-streams-polyfill@npm:^3.0.3": - version: 3.3.3 - resolution: "web-streams-polyfill@npm:3.3.3" - checksum: 21ab5ea08a730a2ef8023736afe16713b4f2023ec1c7085c16c8e293ee17ed085dff63a0ad8722da30c99c4ccbd4ccd1b2e79c861829f7ef2963d7de7004c2cb - languageName: node - linkType: hard - "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -21997,13 +18894,13 @@ __metadata: languageName: node linkType: hard -"write-file-atomic@npm:^6.0.0": - version: 6.0.0 - resolution: "write-file-atomic@npm:6.0.0" +"write-file-atomic@npm:^7.0.0": + version: 7.0.0 + resolution: "write-file-atomic@npm:7.0.0" dependencies: imurmurhash: ^0.1.4 signal-exit: ^4.0.1 - checksum: 35f1303b0229c89c36d0817de9912b43a242f775cb0f386fecf97bac735013e1fde5f464c2ce9f63288d2c91b1ec5bc18d55347b0e37c0e4dbc64b60dc220629 + checksum: dc6c836ceeca27f718fb367117f9bb2108987acfc72be489138889b05119013e2af4a0e34364efd03e2bb6d1b7e42ad16df5b8723cecef2727a74fc78dfc2287 languageName: node linkType: hard @@ -22181,7 +19078,7 @@ __metadata: languageName: node linkType: hard -"yargs@npm:17.7.2, yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2, yargs@npm:^17.7.2": +"yargs@npm:17.7.2, yargs@npm:^17.3.1, yargs@npm:^17.5.1, yargs@npm:^17.6.2": version: 17.7.2 resolution: "yargs@npm:17.7.2" dependencies: From d87081abe762bc91713334ddfd53dfb19ca7edb6 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 16 Dec 2025 10:50:41 +0100 Subject: [PATCH 008/168] Fix CVE-2025-12816 (node-forge) --- package.json | 1 + yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index db8ba26ad..4d671beb2 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ }, "packageManager": "yarn@3.4.1", "resolutions": { + "node-forge": "^1.3.1", "cacache/glob": "^10.5.0", "@npmcli/arborist": "^9.1.8", "ejs": "3.1.10", diff --git a/yarn.lock b/yarn.lock index 1420b2b87..b6e744bbe 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14164,10 +14164,10 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:^1": - version: 1.3.1 - resolution: "node-forge@npm:1.3.1" - checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 +"node-forge@npm:^1.3.1": + version: 1.3.3 + resolution: "node-forge@npm:1.3.3" + checksum: 045b650d61eeba57588744b7be4671044e83871e2c4dc5d4a38a8eb5af7e55fa790c93ba9db1d1ee14a567d25fde41e97a5132e076cff738622e0916c77b48d2 languageName: node linkType: hard From fde78bf32ee235a57d0eac7717cbb4d9008e712d Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 16 Dec 2025 10:56:58 +0100 Subject: [PATCH 009/168] Fix CVE-2025-64718 (js-yaml) --- package.json | 4 ++++ yarn.lock | 22 +++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 4d671beb2..a6e9d072d 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,10 @@ }, "packageManager": "yarn@3.4.1", "resolutions": { + "@istanbuljs/load-nyc-config/js-yaml": "3.14.2", + "@yarnpkg/parsers/js-yaml": "3.14.2", + "cosmiconfig@npm:5.2.1/js-yaml": "3.14.2", + "front-matter@npm:4.0.2/js-yaml": "3.14.2", "node-forge": "^1.3.1", "cacache/glob": "^10.5.0", "@npmcli/arborist": "^9.1.8", diff --git a/yarn.lock b/yarn.lock index b6e744bbe..4e7e6a27e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -12282,26 +12282,26 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:4.1.1": - version: 4.1.1 - resolution: "js-yaml@npm:4.1.1" +"js-yaml@npm:3.14.2": + version: 3.14.2 + resolution: "js-yaml@npm:3.14.2" dependencies: - argparse: ^2.0.1 + argparse: ^1.0.7 + esprima: ^4.0.0 bin: js-yaml: bin/js-yaml.js - checksum: ea2339c6930fe048ec31b007b3c90be2714ab3e7defcc2c27ebf30c74fd940358f29070b4345af0019ef151875bf3bc3f8644bea1bab0372652b5044813ac02d + checksum: 626fc207734a3452d6ba84e1c8c226240e6d431426ed94d0ab043c50926d97c509629c08b1d636f5d27815833b7cfd225865631da9fb33cb957374490bf3e90b languageName: node linkType: hard -"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.1": - version: 3.14.1 - resolution: "js-yaml@npm:3.14.1" +"js-yaml@npm:4.1.1": + version: 4.1.1 + resolution: "js-yaml@npm:4.1.1" dependencies: - argparse: ^1.0.7 - esprima: ^4.0.0 + argparse: ^2.0.1 bin: js-yaml: bin/js-yaml.js - checksum: bef146085f472d44dee30ec34e5cf36bf89164f5d585435a3d3da89e52622dff0b188a580e4ad091c3341889e14cb88cac6e4deb16dc5b1e9623bb0601fc255c + checksum: ea2339c6930fe048ec31b007b3c90be2714ab3e7defcc2c27ebf30c74fd940358f29070b4345af0019ef151875bf3bc3f8644bea1bab0372652b5044813ac02d languageName: node linkType: hard From 5b49506193ed32f771f2b47c997736e472c444ab Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 12 Jan 2026 15:31:18 +0100 Subject: [PATCH 010/168] Fix types for AssertionError and metroSerializer hot reload --- packages/core/src/metro/plugin/metroSerializer.ts | 6 +++--- .../internal-testing-tools/src/assertions/assertionError.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/core/src/metro/plugin/metroSerializer.ts b/packages/core/src/metro/plugin/metroSerializer.ts index 828bf15df..6a2d30791 100644 --- a/packages/core/src/metro/plugin/metroSerializer.ts +++ b/packages/core/src/metro/plugin/metroSerializer.ts @@ -46,7 +46,7 @@ export const createDatadogMetroSerializer = ( const serializer = customSerializer || createDefaultMetroSerializer(); return async (entryPoint, preModules, graph, options) => { // Skip for hot reload mode - if (graph.transformOptions.hot) { + if ((graph.transformOptions as any).hot) { return serializer(entryPoint, preModules, graph, options); } @@ -109,7 +109,7 @@ export const createDefaultMetroSerializer = (): MetroSerializer => { // Modify the bundle through the datadogBundleCallback, if we are not in hot-reload mode if ( (options as any).datadogBundleCallback && - !graph.transformOptions.hot + !(graph.transformOptions as any).hot ) { bundle = (options as any).datadogBundleCallback(bundle); } @@ -119,7 +119,7 @@ export const createDefaultMetroSerializer = (): MetroSerializer => { const { code } = bundleToString(bundle); // If we are in hot-reload mode, we skip sourcemaps generation, and only return the code. - if (graph.transformOptions.hot) { + if ((graph.transformOptions as any).hot) { return code; } diff --git a/packages/internal-testing-tools/src/assertions/assertionError.ts b/packages/internal-testing-tools/src/assertions/assertionError.ts index d36ede8b3..dc86452b1 100644 --- a/packages/internal-testing-tools/src/assertions/assertionError.ts +++ b/packages/internal-testing-tools/src/assertions/assertionError.ts @@ -4,7 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ -import type { Wireframe } from 'rum-events-format'; +import type { RumEvent, Wireframe } from 'rum-events-format'; import type { DDEvent } from '../types/events'; @@ -13,7 +13,7 @@ export class AssertionError extends Error { message: string, expected: string, actual: string | undefined, - events: DDEvent[] | Wireframe | Wireframe[] + events: DDEvent[] | Wireframe | Wireframe[] | RumEvent[] ) { if (actual !== undefined) { super( From 52fb5d4d1ae7308fd660bd502a5c324eb5fbf6a0 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Thu, 15 Jan 2026 10:34:48 +0000 Subject: [PATCH 011/168] Bump to version 2.14.1 --- NATIVE_SDK_VERSIONS.md | 1 + benchmarks/ios/Podfile.lock | 12 ++++++------ example-new-architecture/ios/Podfile.lock | 6 +++--- example/ios/Podfile.lock | 18 +++++++++--------- lerna.json | 2 +- packages/codepush/package.json | 4 ++-- .../com/datadog/reactnative/SdkVersion.kt | 2 +- packages/core/ios/Sources/SdkVersion.swift | 2 +- packages/core/package.json | 2 +- packages/core/src/version.ts | 2 +- packages/internal-testing-tools/package.json | 2 +- .../react-native-apollo-client/package.json | 2 +- .../react-native-babel-plugin/package.json | 2 +- packages/react-native-navigation/package.json | 4 ++-- .../react-native-session-replay/package.json | 2 +- packages/react-native-webview/package.json | 2 +- packages/react-navigation/package.json | 4 ++-- yarn.lock | 8 ++++---- 18 files changed, 39 insertions(+), 38 deletions(-) diff --git a/NATIVE_SDK_VERSIONS.md b/NATIVE_SDK_VERSIONS.md index 6ebb13627..7ba795c56 100644 --- a/NATIVE_SDK_VERSIONS.md +++ b/NATIVE_SDK_VERSIONS.md @@ -1,5 +1,6 @@ | React Native | iOS Bridge / iOS SDK | Android Bridge / Android SDK | |-------------|---------------------|-----------------------------| +| 2.14.1 | 2.30.2 | 2.26.2 | | 2.14.0 | 2.30.2 | 2.26.2 | | 2.13.2 | 2.30.2 | 2.26.2 | | 2.13.1 | 2.30.2 | 2.26.2 | diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index 681b5f169..771bfab42 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - DatadogInternal (= 2.30.2) - DatadogRUM (2.30.2): - DatadogInternal (= 2.30.2) - - DatadogSDKReactNative (2.14.0): + - DatadogSDKReactNative (2.14.1): - DatadogCore (= 2.30.2) - DatadogCrashReporting (= 2.30.2) - DatadogLogs (= 2.30.2) @@ -37,7 +37,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeSessionReplay (2.14.0): + - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - DatadogSessionReplay (= 2.30.2) - DoubleConversion @@ -60,7 +60,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.14.0): + - DatadogSDKReactNativeWebView (2.14.1): - DatadogInternal (= 2.30.2) - DatadogSDKReactNative - DatadogWebViewTracking (= 2.30.2) @@ -2075,9 +2075,9 @@ SPEC CHECKSUMS: DatadogInternal: bd8672d506a7e67936ed5f7ca612169e49029b44 DatadogLogs: d683aa9e0c9339f5ae679ead70bbdbe41cdc32f6 DatadogRUM: 8b794aa458e6323ea9b1cef3f820fd3d092cbe27 - DatadogSDKReactNative: 5febb3635248ca87c73b9c74db0a9f214a9ab876 - DatadogSDKReactNativeSessionReplay: 38b40554550ecf191980cc22f1d9afebe1a914bd - DatadogSDKReactNativeWebView: 597c2fa95d43260228a326d1f33ec08500b74949 + DatadogSDKReactNative: 14c7f36115fdd37841dd620f3376848204596909 + DatadogSDKReactNativeSessionReplay: 27bcc3d5280a85fb2a244b84916c1d01a43668e8 + DatadogSDKReactNativeWebView: 1849aa7769bde0a2a2088705e596276631769656 DatadogSessionReplay: 56a91d799fe34967c5ae79a222364e37d67020f5 DatadogTrace: 3ba194791267efa09634234749111cac95abd3e5 DatadogWebViewTracking: 8287d5ad06e992de5e46dd72a17e05c7513344be diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index f37de03b0..ca3b81b1d 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - DatadogInternal (= 2.30.2) - DatadogRUM (2.30.2): - DatadogInternal (= 2.30.2) - - DatadogSDKReactNative (2.14.0): + - DatadogSDKReactNative (2.14.1): - DatadogCore (= 2.30.2) - DatadogCrashReporting (= 2.30.2) - DatadogLogs (= 2.30.2) @@ -37,7 +37,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNative/Tests (2.14.0): + - DatadogSDKReactNative/Tests (2.14.1): - DatadogCore (= 2.30.2) - DatadogCrashReporting (= 2.30.2) - DatadogLogs (= 2.30.2) @@ -1855,7 +1855,7 @@ SPEC CHECKSUMS: DatadogInternal: bd8672d506a7e67936ed5f7ca612169e49029b44 DatadogLogs: d683aa9e0c9339f5ae679ead70bbdbe41cdc32f6 DatadogRUM: 8b794aa458e6323ea9b1cef3f820fd3d092cbe27 - DatadogSDKReactNative: b641dbbbe1b80c9551f55d86cdddac8d71568263 + DatadogSDKReactNative: 3092efeaf685e37afe770740ef058ee3f9f5bf5d DatadogTrace: 3ba194791267efa09634234749111cac95abd3e5 DatadogWebViewTracking: 8287d5ad06e992de5e46dd72a17e05c7513344be DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index d31ef2ba3..0deeb0a40 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - DatadogInternal (= 2.30.2) - DatadogRUM (2.30.2): - DatadogInternal (= 2.30.2) - - DatadogSDKReactNative (2.14.0): + - DatadogSDKReactNative (2.14.1): - DatadogCore (= 2.30.2) - DatadogCrashReporting (= 2.30.2) - DatadogLogs (= 2.30.2) @@ -18,7 +18,7 @@ PODS: - DatadogTrace (= 2.30.2) - DatadogWebViewTracking (= 2.30.2) - React-Core - - DatadogSDKReactNative/Tests (2.14.0): + - DatadogSDKReactNative/Tests (2.14.1): - DatadogCore (= 2.30.2) - DatadogCrashReporting (= 2.30.2) - DatadogLogs (= 2.30.2) @@ -26,7 +26,7 @@ PODS: - DatadogTrace (= 2.30.2) - DatadogWebViewTracking (= 2.30.2) - React-Core - - DatadogSDKReactNativeSessionReplay (2.14.0): + - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - DatadogSessionReplay (= 2.30.2) - DoubleConversion @@ -49,7 +49,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeSessionReplay/Tests (2.14.0): + - DatadogSDKReactNativeSessionReplay/Tests (2.14.1): - DatadogSDKReactNative - DatadogSessionReplay (= 2.30.2) - DoubleConversion @@ -73,12 +73,12 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.14.0): + - DatadogSDKReactNativeWebView (2.14.1): - DatadogInternal (= 2.30.2) - DatadogSDKReactNative - DatadogWebViewTracking (= 2.30.2) - React-Core - - DatadogSDKReactNativeWebView/Tests (2.14.0): + - DatadogSDKReactNativeWebView/Tests (2.14.1): - DatadogInternal (= 2.30.2) - DatadogSDKReactNative - DatadogWebViewTracking (= 2.30.2) @@ -1993,9 +1993,9 @@ SPEC CHECKSUMS: DatadogInternal: bd8672d506a7e67936ed5f7ca612169e49029b44 DatadogLogs: d683aa9e0c9339f5ae679ead70bbdbe41cdc32f6 DatadogRUM: 8b794aa458e6323ea9b1cef3f820fd3d092cbe27 - DatadogSDKReactNative: 7f1df67f855450c33ff4169adeb901548d32fc8d - DatadogSDKReactNativeSessionReplay: b76757bea41273d7a69485d444194d8e00f53e27 - DatadogSDKReactNativeWebView: 49b59eda4795e53b6f065d768712cb3d2eb2c46d + DatadogSDKReactNative: 676327b41d63d42172cc3543dbed83a9e5f0e32c + DatadogSDKReactNativeSessionReplay: 65bb7f28d508739d76c939d1e8242100c0d42204 + DatadogSDKReactNativeWebView: dfddb218204ae6eec3c092fd20ed3c9d268b8683 DatadogSessionReplay: 56a91d799fe34967c5ae79a222364e37d67020f5 DatadogTrace: 3ba194791267efa09634234749111cac95abd3e5 DatadogWebViewTracking: 8287d5ad06e992de5e46dd72a17e05c7513344be diff --git a/lerna.json b/lerna.json index 08e58b2d8..bc628b996 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "npmClient": "yarn", - "version": "2.14.0", + "version": "2.14.1", "packages": [ "packages/*" ], diff --git a/packages/codepush/package.json b/packages/codepush/package.json index 0dbcc5b45..99b79cc57 100644 --- a/packages/codepush/package.json +++ b/packages/codepush/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-native-code-push", - "version": "2.14.0", + "version": "2.14.1", "description": "A client-side React Native module to interact with Appcenter Codepush and Datadog", "keywords": [ "datadog", @@ -38,7 +38,7 @@ "prepare": "rm -rf lib && yarn bob build" }, "devDependencies": { - "@datadog/mobile-react-native": "workspace:2.14.0", + "@datadog/mobile-react-native": "workspace:2.14.1", "@testing-library/react-native": "7.0.2", "react-native-builder-bob": "0.26.0", "react-native-code-push": "7.1.0" diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/SdkVersion.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/SdkVersion.kt index 3c4be8539..f1ada6483 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/SdkVersion.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/SdkVersion.kt @@ -7,4 +7,4 @@ package com.datadog.reactnative // This is automatically updated by the update-version.sh script -internal const val SDK_VERSION = "2.14.0" +internal const val SDK_VERSION = "2.14.1" diff --git a/packages/core/ios/Sources/SdkVersion.swift b/packages/core/ios/Sources/SdkVersion.swift index c71ba5440..4ca619c2b 100644 --- a/packages/core/ios/Sources/SdkVersion.swift +++ b/packages/core/ios/Sources/SdkVersion.swift @@ -7,4 +7,4 @@ import Foundation // This is automatically updated by the update-version.sh script -let SdkVersion = "2.14.0" +let SdkVersion = "2.14.1" diff --git a/packages/core/package.json b/packages/core/package.json index 7e47b7244..ae22a8cee 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-native", - "version": "2.14.0", + "version": "2.14.1", "description": "A client-side React Native module to interact with Datadog", "keywords": [ "datadog", diff --git a/packages/core/src/version.ts b/packages/core/src/version.ts index f52edffe8..8aaf7d22e 100644 --- a/packages/core/src/version.ts +++ b/packages/core/src/version.ts @@ -1,2 +1,2 @@ // generated by genversion -export const version = '2.14.0'; +export const version = '2.14.1'; diff --git a/packages/internal-testing-tools/package.json b/packages/internal-testing-tools/package.json index bebc02165..69303b257 100644 --- a/packages/internal-testing-tools/package.json +++ b/packages/internal-testing-tools/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/react-native-internal-testing-tools", - "version": "2.14.0", + "version": "2.14.1", "description": "Internal tools for testing the Datadog React Native SDK.", "keywords": [ "datadog", diff --git a/packages/react-native-apollo-client/package.json b/packages/react-native-apollo-client/package.json index c8814a0ce..fced95a27 100644 --- a/packages/react-native-apollo-client/package.json +++ b/packages/react-native-apollo-client/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-native-apollo-client", - "version": "2.14.0", + "version": "2.14.1", "description": "A client-side React Native module to interact with Apollo Client and Datadog", "keywords": [ "datadog", diff --git a/packages/react-native-babel-plugin/package.json b/packages/react-native-babel-plugin/package.json index 83287d9e9..759545076 100644 --- a/packages/react-native-babel-plugin/package.json +++ b/packages/react-native-babel-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-native-babel-plugin", - "version": "2.14.0", + "version": "2.14.1", "description": "A Babel plugin that enhances Datadog's React Native SDK by automatically enriching React components with contextual metadata.", "keywords": [ "babel", diff --git a/packages/react-native-navigation/package.json b/packages/react-native-navigation/package.json index a2839ad4d..cdbb6b06d 100644 --- a/packages/react-native-navigation/package.json +++ b/packages/react-native-navigation/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-native-navigation", - "version": "2.14.0", + "version": "2.14.1", "description": "A client-side React Native module to interact with Datadog", "keywords": [ "datadog", @@ -36,7 +36,7 @@ "prepare": "rm -rf lib && yarn bob build" }, "devDependencies": { - "@datadog/mobile-react-native": "^2.14.0", + "@datadog/mobile-react-native": "^2.14.1", "@testing-library/react-native": "7.0.2", "react-native-builder-bob": "0.26.0", "react-native-gesture-handler": "1.10.3", diff --git a/packages/react-native-session-replay/package.json b/packages/react-native-session-replay/package.json index 99532c842..92502fe19 100644 --- a/packages/react-native-session-replay/package.json +++ b/packages/react-native-session-replay/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-native-session-replay", - "version": "2.14.0", + "version": "2.14.1", "description": "A client-side React Native module to enable session replay with Datadog", "keywords": [ "datadog", diff --git a/packages/react-native-webview/package.json b/packages/react-native-webview/package.json index cdff26f17..691865da0 100644 --- a/packages/react-native-webview/package.json +++ b/packages/react-native-webview/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-native-webview", - "version": "2.14.0", + "version": "2.14.1", "description": "A client-side React Native module to interact with react-native-webview and Datadog", "keywords": [ "datadog", diff --git a/packages/react-navigation/package.json b/packages/react-navigation/package.json index 09bf15306..3169d45ff 100644 --- a/packages/react-navigation/package.json +++ b/packages/react-navigation/package.json @@ -1,6 +1,6 @@ { "name": "@datadog/mobile-react-navigation", - "version": "2.14.0", + "version": "2.14.1", "description": "A client-side React Native module to interact with Datadog", "keywords": [ "datadog", @@ -36,7 +36,7 @@ "prepare": "rm -rf lib && yarn bob build" }, "devDependencies": { - "@datadog/mobile-react-native": "^2.14.0", + "@datadog/mobile-react-native": "^2.14.1", "@react-navigation/native-v5": "npm:@react-navigation/native@5.9.8", "@react-navigation/native-v6": "npm:@react-navigation/native@6.1.2", "@react-navigation/stack-v5": "npm:@react-navigation/stack@5.14.2", diff --git a/yarn.lock b/yarn.lock index 4e7e6a27e..bb7e26521 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2348,7 +2348,7 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/mobile-react-native-code-push@workspace:packages/codepush" dependencies: - "@datadog/mobile-react-native": "workspace:2.14.0" + "@datadog/mobile-react-native": "workspace:2.14.1" "@testing-library/react-native": 7.0.2 react-native-builder-bob: 0.26.0 react-native-code-push: 7.1.0 @@ -2364,7 +2364,7 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/mobile-react-native-navigation@workspace:packages/react-native-navigation" dependencies: - "@datadog/mobile-react-native": ^2.14.0 + "@datadog/mobile-react-native": ^2.14.1 "@testing-library/react-native": 7.0.2 react-native-builder-bob: 0.26.0 react-native-gesture-handler: 1.10.3 @@ -2406,7 +2406,7 @@ __metadata: languageName: unknown linkType: soft -"@datadog/mobile-react-native@^2.14.0, @datadog/mobile-react-native@workspace:2.14.0, @datadog/mobile-react-native@workspace:packages/core": +"@datadog/mobile-react-native@^2.14.1, @datadog/mobile-react-native@workspace:2.14.1, @datadog/mobile-react-native@workspace:packages/core": version: 0.0.0-use.local resolution: "@datadog/mobile-react-native@workspace:packages/core" dependencies: @@ -2423,7 +2423,7 @@ __metadata: version: 0.0.0-use.local resolution: "@datadog/mobile-react-navigation@workspace:packages/react-navigation" dependencies: - "@datadog/mobile-react-native": ^2.14.0 + "@datadog/mobile-react-native": ^2.14.1 "@react-navigation/native-v5": "npm:@react-navigation/native@5.9.8" "@react-navigation/native-v6": "npm:@react-navigation/native@6.1.2" "@react-navigation/stack-v5": "npm:@react-navigation/stack@5.14.2" From 1e5c4a0521feb3cc14025ea071791ff068f8293a Mon Sep 17 00:00:00 2001 From: "Xavier F. Gouchet" Date: Mon, 7 Apr 2025 14:55:53 +0200 Subject: [PATCH 012/168] RUM-9023 use session id to sample network traces --- .../distributedTracing/distributedTracing.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx index e529f3cd1..9c4fcbff7 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx @@ -77,7 +77,12 @@ export const generateTracingAttributesWithSampling = ( } const traceId = TracingIdentifier.createTraceId(); - const hash = Number(traceId.id.multiply(knuthFactor).remainder(twoPow64)); + // for a UUID with value aaaaaaaa-bbbb-Mccc-Nddd-1234567890ab + // we use as the input id the last part : 0x1234567890ab + const baseId = rumSessionId + ? BigInt(rumSessionId.split('-')[4], 16) + : traceId.id; + const hash = Number(baseId.multiply(knuthFactor).remainder(twoPow64)); const threshold = (tracingSamplingRate / 100) * Number(twoPow64); const isSampled = hash <= threshold; From 6db053658a33b1245f82d9ba11374459a16b339c Mon Sep 17 00:00:00 2001 From: "Xavier F. Gouchet" Date: Mon, 28 Apr 2025 13:55:23 +0200 Subject: [PATCH 013/168] RUM-7747 update default tracing sampling rate --- packages/core/ios/Sources/RNDdSdkConfiguration.swift | 2 +- packages/core/src/DdSdkReactNativeConfiguration.tsx | 2 +- .../src/__tests__/DdSdkReactNativeConfiguration.test.ts | 6 +++--- .../sdk/DatadogProvider/__tests__/initialization.test.tsx | 2 +- .../__tests__/FileBasedConfiguration.test.ts | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core/ios/Sources/RNDdSdkConfiguration.swift b/packages/core/ios/Sources/RNDdSdkConfiguration.swift index a765e9759..66437c481 100644 --- a/packages/core/ios/Sources/RNDdSdkConfiguration.swift +++ b/packages/core/ios/Sources/RNDdSdkConfiguration.swift @@ -194,7 +194,7 @@ extension NSArray { internal struct DefaultConfiguration { static let nativeCrashReportEnabled = false static let sessionSamplingRate = 100.0 - static let resourceTracingSamplingRate = 20.0 + static let resourceTracingSamplingRate = 100.0 static let longTaskThresholdMs = 0.0 static let nativeLongTaskThresholdMs = 200.0 static let nativeViewTracking = false diff --git a/packages/core/src/DdSdkReactNativeConfiguration.tsx b/packages/core/src/DdSdkReactNativeConfiguration.tsx index 158d258eb..9be08fa28 100644 --- a/packages/core/src/DdSdkReactNativeConfiguration.tsx +++ b/packages/core/src/DdSdkReactNativeConfiguration.tsx @@ -109,7 +109,7 @@ export const formatFirstPartyHosts = ( export const DEFAULTS = { nativeCrashReportEnabled: false, sessionSamplingRate: 100.0, - resourceTracingSamplingRate: 20.0, + resourceTracingSamplingRate: 100.0, site: 'US1', longTaskThresholdMs: 0, nativeLongTaskThresholdMs: 200, diff --git a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts index 6d9d081af..b1522ef7f 100644 --- a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts +++ b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts @@ -51,7 +51,7 @@ describe('DdSdkReactNativeConfiguration', () => { "nativeViewTracking": false, "proxyConfig": undefined, "resourceEventMapper": null, - "resourceTracingSamplingRate": 20, + "resourceTracingSamplingRate": 100, "serviceName": undefined, "sessionSamplingRate": 100, "site": "US1", @@ -79,7 +79,7 @@ describe('DdSdkReactNativeConfiguration', () => { trackInteractions: true, trackResources: true, firstPartyHosts: ['api.com'], - resourceTracingSamplingRate: 100, + resourceTracingSamplingRate: 80, logEventMapper: event => event, errorEventMapper: event => event, resourceEventMapper: event => event, @@ -161,7 +161,7 @@ describe('DdSdkReactNativeConfiguration', () => { "type": "https", }, "resourceEventMapper": [Function], - "resourceTracingSamplingRate": 100, + "resourceTracingSamplingRate": 80, "serviceName": "com.test.app", "sessionSamplingRate": 80, "site": "EU", diff --git a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx index 590c34b98..c654cd24b 100644 --- a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx +++ b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx @@ -93,7 +93,7 @@ describe('DatadogProvider', () => { "nativeLongTaskThresholdMs": 200, "nativeViewTracking": false, "proxyConfig": undefined, - "resourceTracingSamplingRate": 20, + "resourceTracingSamplingRate": 100, "sampleRate": 100, "serviceName": undefined, "site": "US1", diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts index 523cf67cc..716243e86 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts +++ b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts @@ -134,7 +134,7 @@ describe('FileBasedConfiguration', () => { "nativeViewTracking": false, "proxyConfig": undefined, "resourceEventMapper": null, - "resourceTracingSamplingRate": 20, + "resourceTracingSamplingRate": 100, "serviceName": undefined, "sessionSamplingRate": 100, "site": "US1", From 7f0a9e47a816fe58e3b40754c11e7431b5f30788 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Tue, 2 Sep 2025 10:49:34 +0200 Subject: [PATCH 014/168] Remove fatal errors from logs --- .../DdRumErrorTracking.test.tsx | 202 +----------------- .../core/src/logs/__tests__/DdLogs.test.ts | 4 +- .../instrumentation/DdRumErrorTracking.tsx | 31 +-- 3 files changed, 9 insertions(+), 228 deletions(-) diff --git a/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx b/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx index 7f66bb58e..fc34de5b0 100644 --- a/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx +++ b/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx @@ -6,17 +6,13 @@ import { NativeModules } from 'react-native'; -import type { - DdNativeLogsType, - DdNativeRumType -} from '../../../nativeModulesTypes'; +import type { DdNativeRumType } from '../../../nativeModulesTypes'; import { DdRumErrorTracking } from '../../../rum/instrumentation/DdRumErrorTracking'; import { BufferSingleton } from '../../../sdk/DatadogProvider/Buffer/BufferSingleton'; jest.mock('../../../utils/jsUtils'); const DdRum = NativeModules.DdRum as DdNativeRumType; -const DdLogs = NativeModules.DdLogs as DdNativeLogsType; let baseErrorHandlerCalled = false; const baseErrorHandler = (error: any, isFatal?: boolean) => { @@ -77,19 +73,6 @@ it('M intercept and send a RUM event W onGlobalError() {no message}', async () = '' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - '[object Object]', - 'Error', - '[object Object]', - 'doSomething() at ./path/to/file.js:67:3', - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onGlobalError() {empty stack trace}', async () => { @@ -119,19 +102,6 @@ it('M intercept and send a RUM event W onGlobalError() {empty stack trace}', asy '' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - '', - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onGlobalError() {Error object}', async () => { @@ -162,19 +132,6 @@ it('M intercept and send a RUM event W onGlobalError() {Error object}', async () '/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - expect.stringContaining('Error: Something bad happened'), - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onGlobalError() {CustomError object}', async () => { @@ -209,19 +166,6 @@ it('M intercept and send a RUM event W onGlobalError() {CustomError object}', as '/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'CustomError', - 'Something bad happened', - expect.stringContaining('Error: Something bad happened'), - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onGlobalError() {with source file info}', async () => { @@ -254,19 +198,6 @@ it('M intercept and send a RUM event W onGlobalError() {with source file info}', '' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - 'at ./path/to/file.js:1038:57', - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onGlobalError() {with component stack}', async () => { @@ -301,19 +232,6 @@ it('M intercept and send a RUM event W onGlobalError() {with component stack}', '' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onGlobalError() {with stack}', async () => { @@ -348,19 +266,6 @@ it('M intercept and send a RUM event W onGlobalError() {with stack}', async () = '' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onGlobalError() {with stacktrace}', async () => { @@ -396,19 +301,6 @@ it('M intercept and send a RUM event W onGlobalError() {with stacktrace}', async ); expect(baseErrorHandlerCalled).toStrictEqual(true); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M not report error in console handler W onGlobalError() {with console reporting handler}', async () => { @@ -450,19 +342,6 @@ it('M not report error in console handler W onGlobalError() {with console report expect(consoleReportingErrorHandler).toBeCalledTimes(1); expect(baseConsoleErrorCalled).toStrictEqual(false); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onConsole() {Error with source file info}', async () => { @@ -493,17 +372,6 @@ it('M intercept and send a RUM event W onConsole() {Error with source file info} '' ); expect(baseConsoleErrorCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Oops I did it again! Something bad happened', - 'Error', - 'Oops I did it again! Something bad happened', - 'at ./path/to/file.js:1038:57', - { - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onConsole() {Error with component stack}', async () => { @@ -536,17 +404,6 @@ it('M intercept and send a RUM event W onConsole() {Error with component stack}' '' ); expect(baseConsoleErrorCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Oops I did it again! Something bad happened', - 'Error', - 'Oops I did it again! Something bad happened', - 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', - { - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onConsole() {message only}', async () => { @@ -571,17 +428,6 @@ it('M intercept and send a RUM event W onConsole() {message only}', async () => '' ); expect(baseConsoleErrorCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - '', - { - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onConsole() {Error with source file and name}', async () => { @@ -613,17 +459,6 @@ it('M intercept and send a RUM event W onConsole() {Error with source file and n '' ); expect(baseConsoleErrorCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Oops I did it again! Something bad happened', - 'CustomConsoleError', - 'Oops I did it again! Something bad happened', - 'at ./path/to/file.js:1038:57', - { - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); describe.each([ @@ -661,17 +496,6 @@ describe.each([ '' ); expect(baseConsoleErrorCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - errorMessage, - 'Error', - errorMessage, - '', - { - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); }); @@ -704,19 +528,6 @@ it('M intercept and send a RUM event W on error() {called from RNErrorHandler}', '/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx' ); expect(baseErrorHandlerCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Something bad happened', - 'Error', - 'Something bad happened', - expect.stringContaining('Error: Something bad happened'), - { - '_dd.error.raw': error, - '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); it('M intercept and send a RUM event W onConsole() {called from RNErrorHandler}', async () => { @@ -742,17 +553,6 @@ it('M intercept and send a RUM event W onConsole() {called from RNErrorHandler}' '' ); expect(baseConsoleErrorCalled).toStrictEqual(true); - expect(DdLogs.errorWithError).toHaveBeenCalledTimes(1); - expect(DdLogs.errorWithError).toHaveBeenCalledWith( - 'Oops I did it again!', - 'Error', - 'Oops I did it again!', - '', - { - '_dd.error.source_type': 'react-native', - '_dd.error_log.is_crash': true - } - ); }); /** diff --git a/packages/core/src/logs/__tests__/DdLogs.test.ts b/packages/core/src/logs/__tests__/DdLogs.test.ts index 42f96bae3..b8d0f4121 100644 --- a/packages/core/src/logs/__tests__/DdLogs.test.ts +++ b/packages/core/src/logs/__tests__/DdLogs.test.ts @@ -225,7 +225,7 @@ describe('DdLogs', () => { console.error('console-error-message'); expect(NativeModules.DdLogs.error).not.toHaveBeenCalled(); expect(InternalLog.log).toHaveBeenCalledWith( - 'error log dropped by log mapper: "console-error-message"', + 'Adding RUM Error “console-error-message”', 'debug' ); @@ -278,7 +278,7 @@ describe('DdLogs', () => { console.error('console-error-message'); expect(NativeModules.DdLogs.error).not.toHaveBeenCalled(); expect(InternalLog.log).toHaveBeenCalledWith( - 'Tracking error log "console-error-message"', + 'Adding RUM Error “console-error-message”', 'debug' ); }); diff --git a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx index ecdc99228..774fa70f5 100644 --- a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx +++ b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx @@ -8,7 +8,6 @@ import type { ErrorHandlerCallback } from 'react-native'; import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; -import { DdLogs } from '../../logs/DdLogs'; import { getErrorMessage, getErrorStackTrace, @@ -70,8 +69,7 @@ export class DdRumErrorTracking { static onGlobalError = (error: any, isFatal?: boolean): void => { const message = getErrorMessage(error); const stacktrace = getErrorStackTrace(error); - const errorName = getErrorName(error); - this.reportError(message, ErrorSource.SOURCE, stacktrace, errorName, { + this.reportError(message, ErrorSource.SOURCE, stacktrace, { '_dd.error.is_crash': isFatal, '_dd.error.raw': error }).then(async () => { @@ -130,34 +128,17 @@ export class DdRumErrorTracking { }) .join(' '); - this.reportError(message, ErrorSource.CONSOLE, stack, errorName).then( - () => { - DdRumErrorTracking.defaultConsoleError.apply(console, params); - } - ); + this.reportError(message, ErrorSource.CONSOLE, stack).then(() => { + DdRumErrorTracking.defaultConsoleError.apply(console, params); + }); }; private static reportError = ( message: string, source: ErrorSource, stacktrace: string, - errorName: string, context: object = {} - ): Promise<[void, void]> => { - return Promise.all([ - DdRum.addError(message, source, stacktrace, context), - DdLogs.error( - message, - errorName, - message, - stacktrace, - { - ...context, - '_dd.error_log.is_crash': true - }, - undefined, - source - ) - ]); + ): Promise => { + return DdRum.addError(message, source, stacktrace, context); }; } From 356c1d650a6a711db9ee6445d0947a8abf3096d0 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Tue, 2 Sep 2025 17:06:28 +0200 Subject: [PATCH 015/168] Improve module wrapper singleton creation --- packages/core/src/logs/DdLogs.ts | 4 +- packages/core/src/rum/DdRum.ts | 5 +- packages/core/src/trace/DdTrace.ts | 10 ++- .../utils/__tests__/singletonUtils.test.ts | 75 +++++++++++++++++++ packages/core/src/utils/singletonUtils.ts | 12 +++ 5 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 packages/core/src/utils/__tests__/singletonUtils.test.ts create mode 100644 packages/core/src/utils/singletonUtils.ts diff --git a/packages/core/src/logs/DdLogs.ts b/packages/core/src/logs/DdLogs.ts index 2a77fc453..17ec3af1b 100644 --- a/packages/core/src/logs/DdLogs.ts +++ b/packages/core/src/logs/DdLogs.ts @@ -11,6 +11,7 @@ import type { DdNativeLogsType } from '../nativeModulesTypes'; import { DdAttributes } from '../rum/DdAttributes'; import type { ErrorSource } from '../rum/types'; import { validateContext } from '../utils/argsUtils'; +import { getGlobalInstance } from '../utils/singletonUtils'; import { generateEventMapper } from './eventMapper'; import type { @@ -22,6 +23,7 @@ import type { RawLogWithError } from './types'; +const LOGS_MODULE = 'com.datadog.reactnative.logs'; const SDK_NOT_INITIALIZED_MESSAGE = 'DD_INTERNAL_LOG_SENT_BEFORE_SDK_INIT'; const generateEmptyPromise = () => new Promise(resolve => resolve()); @@ -250,4 +252,4 @@ class DdLogsWrapper implements DdLogsType { } } -export const DdLogs = new DdLogsWrapper(); +export const DdLogs = getGlobalInstance(LOGS_MODULE, () => new DdLogsWrapper()); diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index 37ae3a30e..1241c73a4 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -13,6 +13,7 @@ import { bufferVoidNativeCall } from '../sdk/DatadogProvider/Buffer/bufferNative import { DdSdk } from '../sdk/DdSdk'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; import { validateContext } from '../utils/argsUtils'; +import { getGlobalInstance } from '../utils/singletonUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; import type { TimeProvider } from '../utils/time-provider/TimeProvider'; @@ -44,6 +45,8 @@ import type { PropagatorType } from './types'; +const RUM_MODULE = 'com.datadog.reactnative.rum'; + const generateEmptyPromise = () => new Promise(resolve => resolve()); class DdRumWrapper implements DdRumType { @@ -518,4 +521,4 @@ const isOldStopActionAPI = ( return typeof args[0] === 'object' || typeof args[0] === 'undefined'; }; -export const DdRum = new DdRumWrapper(); +export const DdRum = getGlobalInstance(RUM_MODULE, () => new DdRumWrapper()); diff --git a/packages/core/src/trace/DdTrace.ts b/packages/core/src/trace/DdTrace.ts index b106a97d8..119716914 100644 --- a/packages/core/src/trace/DdTrace.ts +++ b/packages/core/src/trace/DdTrace.ts @@ -13,8 +13,11 @@ import { } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; import type { DdTraceType } from '../types'; import { validateContext } from '../utils/argsUtils'; +import { getGlobalInstance } from '../utils/singletonUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; +const TRACE_MODULE = 'com.datadog.reactnative.trace'; + const timeProvider = new DefaultTimeProvider(); class DdTraceWrapper implements DdTraceType { @@ -59,6 +62,7 @@ class DdTraceWrapper implements DdTraceType { }; } -const DdTrace: DdTraceType = new DdTraceWrapper(); - -export { DdTrace }; +export const DdTrace: DdTraceType = getGlobalInstance( + TRACE_MODULE, + () => new DdTraceWrapper() +); diff --git a/packages/core/src/utils/__tests__/singletonUtils.test.ts b/packages/core/src/utils/__tests__/singletonUtils.test.ts new file mode 100644 index 000000000..f424562c6 --- /dev/null +++ b/packages/core/src/utils/__tests__/singletonUtils.test.ts @@ -0,0 +1,75 @@ +import { getGlobalInstance } from '../singletonUtils'; + +describe('singletonUtils', () => { + const createdSymbols: symbol[] = []; + const g = (globalThis as unknown) as Record; + + afterEach(() => { + for (const symbol of createdSymbols) { + delete g[symbol]; + } + + createdSymbols.length = 0; + jest.restoreAllMocks(); + }); + + it('only creates one instance for the same key', () => { + const key = 'com.datadog.reactnative.test'; + const symbol = Symbol.for(key); + createdSymbols.push(symbol); + + const objectConstructor = jest.fn(() => ({ id: 1 })); + const a = getGlobalInstance(key, objectConstructor); + const b = getGlobalInstance(key, objectConstructor); + + expect(a).toBe(b); + expect(objectConstructor).toHaveBeenCalledTimes(1); + expect(g[symbol]).toBe(a); + }); + + it('returns a pre-existing instance without creating a new one for the same key', () => { + const key = 'com.datadog.reactnative.test'; + const symbol = Symbol.for(key); + createdSymbols.push(symbol); + + const existing = { pre: true }; + g[symbol] = existing; + + const objectConstructor = jest.fn(() => ({ created: true })); + const result = getGlobalInstance(key, objectConstructor); + + expect(result).toBe(existing); + expect(objectConstructor).not.toHaveBeenCalled(); + }); + + it('creates a new instance for a different key', () => { + const keyA = 'com.datadog.reactnative.test.a'; + const keyB = 'com.datadog.reactnative.test.b'; + const symbolA = Symbol.for(keyA); + const symbolB = Symbol.for(keyB); + createdSymbols.push(symbolA, symbolB); + + const a = getGlobalInstance(keyA, () => ({ id: 'A' })); + const b = getGlobalInstance(keyB, () => ({ id: 'B' })); + + expect(a).not.toBe(b); + expect((a as any).id).toBe('A'); + expect((b as any).id).toBe('B'); + }); + + it('does not overwrite existing instance if called with a different constructor', () => { + const key = 'com.datadog.reactnative.test'; + const symbol = Symbol.for(key); + createdSymbols.push(symbol); + + const firstObjectConstructor = jest.fn(() => ({ id: 1 })); + const first = getGlobalInstance(key, firstObjectConstructor); + + const secondObjectConstructor = jest.fn(() => ({ id: 2 })); + const second = getGlobalInstance(key, secondObjectConstructor); + + expect(first).toBe(second); + expect(firstObjectConstructor).toHaveBeenCalledTimes(1); + expect(secondObjectConstructor).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/core/src/utils/singletonUtils.ts b/packages/core/src/utils/singletonUtils.ts new file mode 100644 index 000000000..9f00c2cd0 --- /dev/null +++ b/packages/core/src/utils/singletonUtils.ts @@ -0,0 +1,12 @@ +export const getGlobalInstance = ( + key: string, + objectConstructor: () => T +): T => { + const symbol = Symbol.for(key); + const g = (globalThis as unknown) as Record; + + if (!(symbol in g)) { + g[symbol] = objectConstructor(); + } + return g[symbol] as T; +}; From 01004f2b8eff9680e9c4ac40495e70d13c9929f5 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 5 Sep 2025 17:47:29 +0200 Subject: [PATCH 016/168] Use native sdk's core instance instead of the one inside RN SDK wrapper --- .../datadog/reactnative/DatadogSDKWrapper.kt | 90 +------------------ .../com/datadog/reactnative/DatadogWrapper.kt | 48 ---------- .../reactnative/DdLogsImplementation.kt | 3 +- .../reactnative/DdSdkImplementation.kt | 9 +- .../reactnative/DdSdkNativeInitialization.kt | 13 ++- .../reactnative/DdSdkReactNativePackage.kt | 3 +- .../com/datadog/reactnative/DdTelemetry.kt | 56 ++++++++++++ .../kotlin/com/datadog/reactnative/DdSdk.kt | 3 +- .../kotlin/com/datadog/reactnative/DdSdk.kt | 5 +- .../DdSdkNativeInitializationTest.kt | 4 + .../com/datadog/reactnative/DdSdkTest.kt | 6 +- .../DdInternalTestingImplementation.kt | 2 +- .../DdInternalTestingImplementationTest.kt | 74 ++++++++------- .../DdSessionReplayImplementation.kt | 8 +- 14 files changed, 140 insertions(+), 184 deletions(-) create mode 100644 packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index c9865a5d7..da0841ac4 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -8,23 +8,14 @@ package com.datadog.reactnative import android.content.Context -import android.util.Log import com.datadog.android.Datadog -import com.datadog.android._InternalProxy import com.datadog.android.api.InternalLogger -import com.datadog.android.api.SdkCore import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.core.InternalSdkCore import com.datadog.android.core.configuration.Configuration -import com.datadog.android.log.Logs -import com.datadog.android.log.LogsConfiguration import com.datadog.android.privacy.TrackingConsent import com.datadog.android.rum.GlobalRumMonitor -import com.datadog.android.rum.Rum -import com.datadog.android.rum.RumConfiguration import com.datadog.android.rum.RumMonitor -import com.datadog.android.trace.Trace -import com.datadog.android.trace.TraceConfiguration import com.datadog.android.webview.WebViewTracking import com.facebook.react.bridge.ReadableMap @@ -50,50 +41,18 @@ object DatadogSDKWrapperStorage { listener(ddCore) } } - - /** - * Sets instance of core SDK to be used to initialize features. - */ - fun setSdkCore(core: InternalSdkCore?) { - this.core = core - } - - /** - * Returns the core set by setSdkCore or the default core instance by default. - */ - fun getSdkCore(): SdkCore { - core?.let { - return it - } - Log.d( - DatadogSDKWrapperStorage::class.java.canonicalName, - "SdkCore was not set in DatadogSDKWrapperStorage, using default instance." - ) - return Datadog.getInstance() - } } internal class DatadogSDKWrapper : DatadogWrapper { override var bundleLogsWithRum = DefaultConfiguration.bundleLogsWithRum override var bundleLogsWithTraces = DefaultConfiguration.bundleLogsWithTraces - // We use Kotlin backing field here to initialize once the telemetry proxy - // and make sure it is only after SDK is initialized. - private var telemetryProxy: _InternalProxy._TelemetryProxy? = null - get() { - if (field == null && isInitialized()) { - field = Datadog._internalProxy()._telemetry - } - - return field - } - // We use Kotlin backing field here to initialize once the telemetry proxy // and make sure it is only after SDK is initialized. private var webViewProxy: WebViewTracking._InternalWebViewProxy? = null get() { if (field == null && isInitialized()) { - field = WebViewTracking._InternalWebViewProxy(DatadogSDKWrapperStorage.getSdkCore()) + field = WebViewTracking._InternalWebViewProxy(Datadog.getInstance()) } return field @@ -109,20 +68,7 @@ internal class DatadogSDKWrapper : DatadogWrapper { consent: TrackingConsent ) { val core = Datadog.initialize(context, configuration, consent) - DatadogSDKWrapperStorage.setSdkCore(core as InternalSdkCore) - DatadogSDKWrapperStorage.notifyOnInitializedListeners(core) - } - - override fun enableRum(configuration: RumConfiguration) { - Rum.enable(configuration, DatadogSDKWrapperStorage.getSdkCore()) - } - - override fun enableLogs(configuration: LogsConfiguration) { - Logs.enable(configuration, DatadogSDKWrapperStorage.getSdkCore()) - } - - override fun enableTrace(configuration: TraceConfiguration) { - Trace.enable(configuration, DatadogSDKWrapperStorage.getSdkCore()) + DatadogSDKWrapperStorage.notifyOnInitializedListeners(core as InternalSdkCore) } @Deprecated("Use setUserInfo instead; the user ID is now required.") @@ -161,34 +107,6 @@ internal class DatadogSDKWrapper : DatadogWrapper { Datadog.setTrackingConsent(trackingConsent) } - override fun sendTelemetryLog(message: String, attributes: ReadableMap, config: ReadableMap) { - val core = DatadogSDKWrapperStorage.getSdkCore() as FeatureSdkCore? - val logger = core?.internalLogger; - - val additionalProperties = attributes.toMap() - val telemetryConfig = config.toMap() - - logger?.log( - level = InternalLogger.Level.INFO, - target = InternalLogger.Target.TELEMETRY, - messageBuilder = { message }, - onlyOnce = (telemetryConfig["onlyOnce"] as? Boolean) ?: true, - additionalProperties = additionalProperties - ) - } - - override fun telemetryDebug(message: String) { - telemetryProxy?.debug(message) - } - - override fun telemetryError(message: String, stack: String?, kind: String?) { - telemetryProxy?.error(message, stack, kind) - } - - override fun telemetryError(message: String, throwable: Throwable?) { - telemetryProxy?.error(message, throwable) - } - override fun consumeWebviewEvent(message: String) { webViewProxy?.consumeWebviewEvent(message) } @@ -198,11 +116,11 @@ internal class DatadogSDKWrapper : DatadogWrapper { } override fun getRumMonitor(): RumMonitor { - return GlobalRumMonitor.get(DatadogSDKWrapperStorage.getSdkCore()) + return GlobalRumMonitor.get(Datadog.getInstance()) } override fun clearAllData() { - return Datadog.clearAllData(DatadogSDKWrapperStorage.getSdkCore()) + return Datadog.clearAllData(Datadog.getInstance()) } } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index 41e86f4d5..9ac591d80 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -64,33 +64,6 @@ interface DatadogWrapper { consent: TrackingConsent ) - /** - * Enables the RUM feature of the SDK. - * - * @param configuration the configuration for the RUM feature - */ - fun enableRum( - configuration: RumConfiguration - ) - - /** - * Enables the Logs feature of the SDK. - * - * @param configuration the configuration for the Logs feature - */ - fun enableLogs( - configuration: LogsConfiguration - ) - - /** - * Enables the Trace feature of the SDK. - * - * @param configuration the configuration for the Trace feature - */ - fun enableTrace( - configuration: TraceConfiguration - ) - /** * Sets the user information. * @@ -144,27 +117,6 @@ interface DatadogWrapper { */ fun setTrackingConsent(trackingConsent: TrackingConsent) - - /** - * Sends telemetry event with attributes. - */ - fun sendTelemetryLog(message: String, attributes: ReadableMap, config: ReadableMap) - - /** - * Sends telemetry debug event. - */ - fun telemetryDebug(message: String) - - /** - * Sends telemetry error. - */ - fun telemetryError(message: String, stack: String?, kind: String?) - - /** - * Sends telemetry error. - */ - fun telemetryError(message: String, throwable: Throwable?) - /** * Sends Webview events. */ diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt index 9a84496ec..2f9bceff2 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdLogsImplementation.kt @@ -7,6 +7,7 @@ package com.datadog.reactnative import android.util.Log as AndroidLog +import com.datadog.android.Datadog import com.datadog.android.log.Logger import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReadableMap @@ -22,7 +23,7 @@ class DdLogsImplementation( val bundleLogsWithRum = datadog.bundleLogsWithRum val bundleLogsWithTraces = datadog.bundleLogsWithTraces - logger ?: Logger.Builder(DatadogSDKWrapperStorage.getSdkCore()) + logger ?: Logger.Builder(Datadog.getInstance()) .setLogcatLogsEnabled(true) .setBundleWithRumEnabled(bundleLogsWithRum) .setBundleWithTraceEnabled(bundleLogsWithTraces) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index cdd6b0614..b04a2ddf3 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -24,6 +24,7 @@ import java.util.concurrent.atomic.AtomicBoolean class DdSdkImplementation( private val reactContext: ReactApplicationContext, private val datadog: DatadogWrapper = DatadogSDKWrapper(), + private val ddTelemetry: DdTelemetry = DdTelemetry(), private val uiThreadExecutor: UiThreadExecutor = ReactUiThreadExecutor() ) { internal val appContext: Context = reactContext.applicationContext @@ -39,7 +40,7 @@ class DdSdkImplementation( fun initialize(configuration: ReadableMap, promise: Promise) { val ddSdkConfiguration = configuration.asDdSdkConfiguration() - val nativeInitialization = DdSdkNativeInitialization(appContext, datadog) + val nativeInitialization = DdSdkNativeInitialization(appContext, datadog, ddTelemetry) nativeInitialization.initialize(ddSdkConfiguration) this.frameRateProvider = createFrameRateProvider(ddSdkConfiguration) @@ -145,7 +146,7 @@ class DdSdkImplementation( * @param config Configuration object, can take 'onlyOnce: Boolean' */ fun sendTelemetryLog(message: String, attributes: ReadableMap, config: ReadableMap, promise: Promise) { - datadog.sendTelemetryLog(message, attributes, config) + ddTelemetry.sendTelemetryLog(message, attributes, config) promise.resolve(null) } @@ -154,7 +155,7 @@ class DdSdkImplementation( * @param message Debug message. */ fun telemetryDebug(message: String, promise: Promise) { - datadog.telemetryDebug(message) + ddTelemetry.telemetryDebug(message) promise.resolve(null) } @@ -165,7 +166,7 @@ class DdSdkImplementation( * @param kind Error kind. */ fun telemetryError(message: String, stack: String, kind: String, promise: Promise) { - datadog.telemetryError(message, stack, kind) + ddTelemetry.telemetryError(message, stack, kind) promise.resolve(null) } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index 9c8e8370d..ee55d08fe 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -9,14 +9,17 @@ package com.datadog.reactnative import android.content.Context import android.content.pm.PackageManager import android.util.Log +import com.datadog.android.Datadog import com.datadog.android.DatadogSite import com.datadog.android.core.configuration.BatchProcessingLevel import com.datadog.android.core.configuration.BatchSize import com.datadog.android.core.configuration.Configuration import com.datadog.android.core.configuration.UploadFrequency import com.datadog.android.event.EventMapper +import com.datadog.android.log.Logs import com.datadog.android.log.LogsConfiguration import com.datadog.android.privacy.TrackingConsent +import com.datadog.android.rum.Rum import com.datadog.android.rum.RumConfiguration import com.datadog.android.rum._RumInternalProxy import com.datadog.android.rum.configuration.VitalsUpdateFrequency @@ -25,6 +28,7 @@ import com.datadog.android.rum.model.ActionEvent import com.datadog.android.rum.model.ResourceEvent import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy import com.datadog.android.telemetry.model.TelemetryConfigurationEvent +import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration import com.google.gson.Gson import java.util.Locale @@ -37,6 +41,7 @@ import kotlin.time.Duration.Companion.seconds class DdSdkNativeInitialization internal constructor( private val appContext: Context, private val datadog: DatadogWrapper = DatadogSDKWrapper(), + private val ddTelemetry: DdTelemetry = DdTelemetry(), private val jsonFileReader: JSONFileReader = JSONFileReader() ) { internal fun initialize(ddSdkConfiguration: DdSdkConfiguration) { @@ -59,11 +64,11 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) - datadog.enableRum(rumConfiguration) + Rum.enable(rumConfiguration, Datadog.getInstance()) - datadog.enableTrace(traceConfiguration) + Logs.enable(logsConfiguration, Datadog.getInstance()) - datadog.enableLogs(logsConfiguration) + Trace.enable(traceConfiguration, Datadog.getInstance()) } private fun configureRumAndTracesForLogs(configuration: DdSdkConfiguration) { @@ -95,7 +100,7 @@ class DdSdkNativeInitialization internal constructor( try { appContext.packageManager.getPackageInfo(packageName, 0) } catch (e: PackageManager.NameNotFoundException) { - datadog.telemetryError(e.message ?: DdSdkImplementation.PACKAGE_INFO_NOT_FOUND_ERROR_MESSAGE, e) + ddTelemetry.telemetryError(e.message ?: DdSdkImplementation.PACKAGE_INFO_NOT_FOUND_ERROR_MESSAGE, e) return DdSdkImplementation.DEFAULT_APP_VERSION } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkReactNativePackage.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkReactNativePackage.kt index bac9f49f5..3a5b022c1 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkReactNativePackage.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkReactNativePackage.kt @@ -18,9 +18,10 @@ import com.facebook.react.module.model.ReactModuleInfoProvider */ class DdSdkReactNativePackage : TurboReactPackage() { private val sdkWrapper = DatadogSDKWrapper() + private val ddTelemetry = DdTelemetry() override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { return when (name) { - DdSdkImplementation.NAME -> DdSdk(reactContext, sdkWrapper) + DdSdkImplementation.NAME -> DdSdk(reactContext, sdkWrapper, ddTelemetry) DdRumImplementation.NAME -> DdRum(reactContext, sdkWrapper) DdTraceImplementation.NAME -> DdTrace(reactContext) DdLogsImplementation.NAME -> DdLogs(reactContext, sdkWrapper) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt new file mode 100644 index 000000000..2d60df004 --- /dev/null +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt @@ -0,0 +1,56 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +package com.datadog.reactnative + +import com.datadog.android.Datadog +import com.datadog.android._InternalProxy +import com.datadog.android.api.InternalLogger +import com.datadog.android.api.feature.FeatureSdkCore +import com.facebook.react.bridge.ReadableMap + +class DdTelemetry { + + // We use Kotlin backing field here to initialize once the telemetry proxy + // and make sure it is only after SDK is initialized. + private var telemetryProxy: _InternalProxy._TelemetryProxy? = null + get() { + if (field == null && Datadog.isInitialized()) { + field = Datadog._internalProxy()._telemetry + } + + return field + } + + fun sendTelemetryLog(message: String, attributes: ReadableMap, config: ReadableMap) { + val core = Datadog.getInstance() as FeatureSdkCore? + val logger = core?.internalLogger; + + val additionalProperties = attributes.toMap() + val telemetryConfig = config.toMap() + + logger?.log( + level = InternalLogger.Level.INFO, + target = InternalLogger.Target.TELEMETRY, + messageBuilder = { message }, + onlyOnce = (telemetryConfig["onlyOnce"] as? Boolean) ?: true, + additionalProperties = additionalProperties + ) + } + + fun telemetryDebug(message: String) { + telemetryProxy?.debug(message) + } + + fun telemetryError(message: String, stack: String?, kind: String?) { + telemetryProxy?.error(message, stack, kind) + } + + fun telemetryError(message: String, throwable: Throwable?) { + telemetryProxy?.error(message, throwable) + } +} + diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt index d46e53ade..5bc470947 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -19,9 +19,10 @@ import com.facebook.react.modules.core.DeviceEventManagerModule class DdSdk( reactContext: ReactApplicationContext, datadogWrapper: DatadogWrapper = DatadogSDKWrapper() + ddTelemetry: DdTelemetry = DdTelemetry() ) : NativeDdSdkSpec(reactContext) { - private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper) + private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index b41eff1db..af8f87c29 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -17,10 +17,11 @@ import com.facebook.react.bridge.ReadableMap /** The entry point to initialize Datadog's features. */ class DdSdk( reactContext: ReactApplicationContext, - datadogWrapper: DatadogWrapper = DatadogSDKWrapper() + datadogWrapper: DatadogWrapper = DatadogSDKWrapper(), + ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper) + private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt index d05d43c57..e25bfe999 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkNativeInitializationTest.kt @@ -48,6 +48,9 @@ internal class DdSdkNativeInitializationTest { @Mock lateinit var mockDatadog: DatadogWrapper + @Mock + lateinit var mockDdTelemetry: DdTelemetry + @Mock lateinit var mockJSONFileReader: JSONFileReader @@ -64,6 +67,7 @@ internal class DdSdkNativeInitializationTest { testedNativeInitialization = DdSdkNativeInitialization( mockContext, mockDatadog, + mockDdTelemetry, mockJSONFileReader ) } diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 5cda53616..9e7d671fd 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -120,6 +120,9 @@ internal class DdSdkTest { @Mock lateinit var mockDatadog: DatadogWrapper + @Mock + lateinit var mockDdTelemetry: DdTelemetry + @Forgery lateinit var fakeConfiguration: DdSdkConfiguration @@ -157,9 +160,8 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, TestUiThreadExecutor()) + testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) - DatadogSDKWrapperStorage.setSdkCore(null) DatadogSDKWrapperStorage.onInitializedListeners.clear() } diff --git a/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt b/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt index 197a9df75..df0030fb5 100644 --- a/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt +++ b/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt @@ -7,6 +7,7 @@ package com.datadog.reactnative.internaltesting import com.datadog.android.api.InternalLogger +import com.datadog.android.Datadog import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.TimeInfo @@ -53,7 +54,6 @@ class DdInternalTestingImplementation { fun enable(promise: Promise) { DatadogSDKWrapperStorage.addOnInitializedListener { ddCore -> this.wrappedCore = StubSDKCore(ddCore) - DatadogSDKWrapperStorage.setSdkCore(this.wrappedCore) } promise.resolve(null) } diff --git a/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt b/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt index 6c278026a..c542ed6bc 100644 --- a/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt +++ b/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt @@ -7,6 +7,8 @@ package com.datadog.reactnative.internaltesting import android.content.Context +import com.datadog.android.Datadog +import com.datadog.android.api.SdkCore import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope @@ -24,6 +26,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.extension.ExtendWith import org.junit.jupiter.api.extension.Extensions import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.junit.jupiter.MockitoExtension import org.mockito.junit.jupiter.MockitoSettings @@ -57,37 +60,47 @@ internal class DdInternalTestingImplementationTest { @Test fun `M return captured events W enable()`() { - // Given - val mockFeature = MockFeature("mockFeature") - val mockFeatureScope = MockFeatureScope(mockFeature) - whenever(mockCore.getFeature(mockFeature.name)).doReturn( - mockFeatureScope - ) - whenever(mockCore.getDatadogContext()).doReturn( - mockContext - ) - - // When - testedInternalTesting.enable(mockPromise) - // Simulating DdSdkImplementation initialization - DatadogSDKWrapperStorage.setSdkCore(mockCore) - DatadogSDKWrapperStorage.notifyOnInitializedListeners(mockCore) - - val wrappedCore = DatadogSDKWrapperStorage.getSdkCore() as StubSDKCore - wrappedCore.registerFeature(mockFeature) - requireNotNull(wrappedCore.getFeature(mockFeature.name)) - .withWriteContext { _, eventBatchWriter -> - eventBatchWriter.write( - RawBatchEvent(data = "mock event for test".toByteArray()), - batchMetadata = null, - eventType = EventType.DEFAULT + Mockito.mockStatic(Datadog::class.java).use { datadogStatic -> + // Given + datadogStatic.`when` { + Datadog.getInstance() + }.thenReturn(mockCore) + + val mockFeature = MockFeature("mockFeature") + val mockFeatureScope = MockFeatureScope(mockFeature) + whenever(mockCore.getFeature(mockFeature.name)).doReturn( + mockFeatureScope + ) + whenever(mockCore.getDatadogContext()).doReturn( + mockContext + ) + + // When + testedInternalTesting.enable(mockPromise) + // Simulating DdSdkImplementation initialization + DatadogSDKWrapperStorage.notifyOnInitializedListeners(mockCore) + + val wrappedCore = Datadog.getInstance() as StubSDKCore + wrappedCore.registerFeature(mockFeature) + requireNotNull(wrappedCore.getFeature(mockFeature.name)) + .withWriteContext { _, eventBatchWriter -> + eventBatchWriter.write( + RawBatchEvent(data = "mock event for test".toByteArray()), + batchMetadata = null, + eventType = EventType.DEFAULT + ) + } + + // Then + assertThat( + wrappedCore.featureScopes[mockFeature.name] + ?.eventsWritten() + ?.first() + ) + .isEqualTo( + "mock event for test" ) - } - - // Then - assertThat(wrappedCore.featureScopes[mockFeature.name]?.eventsWritten()?.first()).isEqualTo( - "mock event for test" - ) + } } } @@ -96,6 +109,7 @@ internal class MockFeatureScope(private val feature: Feature) : FeatureScope { override fun sendEvent(event: Any) {} + @Suppress("UNCHECKED_CAST") override fun unwrap(): T { return feature as T } diff --git a/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt b/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt index ef5467bc4..cc2fd64dc 100644 --- a/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt +++ b/packages/react-native-session-replay/android/src/main/kotlin/com/datadog/reactnative/sessionreplay/DdSessionReplayImplementation.kt @@ -7,10 +7,10 @@ package com.datadog.reactnative.sessionreplay import android.annotation.SuppressLint +import com.datadog.android.Datadog import com.datadog.android.api.feature.FeatureSdkCore import com.datadog.android.sessionreplay.SessionReplayConfiguration import com.datadog.android.sessionreplay._SessionReplayInternalProxy -import com.datadog.reactnative.DatadogSDKWrapperStorage import com.datadog.reactnative.sessionreplay.utils.text.TextViewUtils import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactContext @@ -40,7 +40,7 @@ class DdSessionReplayImplementation( startRecordingImmediately: Boolean, promise: Promise ) { - val sdkCore = DatadogSDKWrapperStorage.getSdkCore() as FeatureSdkCore + val sdkCore = Datadog.getInstance() as FeatureSdkCore val logger = sdkCore.internalLogger val textViewUtils = TextViewUtils.create(reactContext, logger) val internalCallback = ReactNativeInternalCallback(reactContext) @@ -68,7 +68,7 @@ class DdSessionReplayImplementation( */ fun startRecording(promise: Promise) { sessionReplayProvider().startRecording( - DatadogSDKWrapperStorage.getSdkCore() as FeatureSdkCore + Datadog.getInstance() as FeatureSdkCore ) promise.resolve(null) } @@ -78,7 +78,7 @@ class DdSessionReplayImplementation( */ fun stopRecording(promise: Promise) { sessionReplayProvider().stopRecording( - DatadogSDKWrapperStorage.getSdkCore() as FeatureSdkCore + Datadog.getInstance() as FeatureSdkCore ) promise.resolve(null) } From 74b0dcf7bd314d9296cf8b3d7e47750e407dc0a0 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Mon, 22 Sep 2025 16:20:27 +0200 Subject: [PATCH 017/168] Fixed internal testing tools and unit tests --- .../com/datadog/reactnative/DatadogWrapper.kt | 8 +- .../reactnative/DdSdkNativeInitialization.kt | 2 - .../com/datadog/reactnative/DdTelemetry.kt | 40 + .../kotlin/com/datadog/reactnative/DdSdk.kt | 6 +- .../com/datadog/reactnative/DdSdkTest.kt | 2628 ++++++++++------- .../DdInternalTestingImplementation.kt | 6 + .../DdInternalTestingImplementationTest.kt | 4 +- 7 files changed, 1695 insertions(+), 999 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index 9ac591d80..19b25e587 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -18,7 +18,7 @@ import com.facebook.react.bridge.ReadableMap import java.lang.IllegalArgumentException /** - * Wrapper around [Datadog]. + * Wrapper around [com.datadog.android.Datadog]. */ @Suppress("ComplexInterface", "TooManyFunctions") interface DatadogWrapper { @@ -49,10 +49,8 @@ interface DatadogWrapper { /** * Initializes the Datadog SDK. * @param context your application context - * @param credentials your organization credentials * @param configuration the configuration for the SDK library - * @param trackingConsent as the initial state of the tracking consent flag. - * @see [Credentials] + * @param consent as the initial state of the tracking consent flag. * @see [Configuration] * @see [TrackingConsent] * @throws IllegalArgumentException if the env name is using illegal characters and your @@ -99,7 +97,7 @@ interface DatadogWrapper { /** * Sets the user information. - * @param extraUserInfo: The additional information. (To set the id, name or email please user setUserInfo). + * @param extraInfo: The additional information. (To set the id, name or email please user setUserInfo). */ fun addUserExtraInfo( extraInfo: Map diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index ee55d08fe..4388ad5f6 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,9 +65,7 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) - Logs.enable(logsConfiguration, Datadog.getInstance()) - Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt index 2d60df004..24354ce78 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTelemetry.kt @@ -12,6 +12,13 @@ import com.datadog.android.api.InternalLogger import com.datadog.android.api.feature.FeatureSdkCore import com.facebook.react.bridge.ReadableMap +/** + * **[INTERNAL USAGE]** + * + * Utility class used by React Native modules to forward telemetry events to the Datadog SDK. + * + * This class is **public only for Datadog internal package visibility** and should not be used. + */ class DdTelemetry { // We use Kotlin backing field here to initialize once the telemetry proxy @@ -25,6 +32,15 @@ class DdTelemetry { return field } + /** + * **[INTERNAL USAGE]** + * + * Sends a telemetry log message with additional attributes and configuration options. + * + * @param message the message to log + * @param attributes additional key–value properties to include in the log + * @param config configuration options for the telemetry log (e.g. `onlyOnce` flag) + */ fun sendTelemetryLog(message: String, attributes: ReadableMap, config: ReadableMap) { val core = Datadog.getInstance() as FeatureSdkCore? val logger = core?.internalLogger; @@ -41,14 +57,38 @@ class DdTelemetry { ) } + /** + * **[INTERNAL USAGE]** + * + * Sends a debug-level telemetry message. + * + * @param message the debug message + */ fun telemetryDebug(message: String) { telemetryProxy?.debug(message) } + /** + * **[INTERNAL USAGE]** + * + * Sends an error-level telemetry message with optional details. + * + * @param message the error message + * @param stack an optional stack trace string + * @param kind an optional error kind or category + */ fun telemetryError(message: String, stack: String?, kind: String?) { telemetryProxy?.error(message, stack, kind) } + /** + * **[INTERNAL USAGE]** + * + * Sends an error-level telemetry message with an attached [Throwable]. + * + * @param message the error message + * @param throwable the throwable associated with the error + */ fun telemetryError(message: String, throwable: Throwable?) { telemetryProxy?.error(message, throwable) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index af8f87c29..17acd6d20 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -21,7 +21,11 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) + private val implementation = DdSdkImplementation( + reactContext, + datadog = datadogWrapper, + ddTelemetry + ) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 9e7d671fd..a39485dae 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -16,8 +16,10 @@ import com.datadog.android.core.configuration.BatchSize import com.datadog.android.core.configuration.Configuration import com.datadog.android.core.configuration.UploadFrequency import com.datadog.android.event.EventMapper +import com.datadog.android.log.Logs import com.datadog.android.log.LogsConfiguration import com.datadog.android.privacy.TrackingConsent +import com.datadog.android.rum.Rum import com.datadog.android.rum.RumConfiguration import com.datadog.android.rum.RumPerformanceMetric import com.datadog.android.rum._RumInternalProxy @@ -27,6 +29,7 @@ import com.datadog.android.rum.model.ActionEvent import com.datadog.android.rum.model.ResourceEvent import com.datadog.android.rum.tracking.ActivityViewTrackingStrategy import com.datadog.android.telemetry.model.TelemetryConfigurationEvent +import com.datadog.android.trace.Trace import com.datadog.android.trace.TraceConfiguration import com.datadog.android.trace.TracingHeaderType import com.datadog.tools.unit.GenericAssert.Companion.assertThat @@ -160,7 +163,12 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) + testedBridgeSdk = DdSdkImplementation( + mockReactContext, + mockDatadog, + mockDdTelemetry, + TestUiThreadExecutor() + ) DatadogSDKWrapperStorage.onInitializedListeners.clear() } @@ -181,35 +189,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo("crashReportsEnabled", true) - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("crashReportsEnabled", true) + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -221,75 +243,104 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo("crashReportsEnabled", false) - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("crashReportsEnabled", false) + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test fun `𝕄 initialize native SDK 𝕎 initialize() {nativeCrashReportEnabled=null}`() { // Given fakeConfiguration = fakeConfiguration.copy(nativeCrashReportEnabled = false, site = null) + val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo("crashReportsEnabled", false) - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("crashReportsEnabled", false) + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -305,37 +356,50 @@ internal class DdSdkTest { val traceConfigCaptor = argumentCaptor() val expectedRumSampleRate = fakeConfiguration.sampleRate?.toFloat() ?: 100f - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("sampleRate", expectedRumSampleRate) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("sampleRate", expectedRumSampleRate) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -351,37 +415,50 @@ internal class DdSdkTest { val traceConfigCaptor = argumentCaptor() val expectedTelemetrySampleRate = fakeConfiguration.telemetrySampleRate?.toFloat() ?: 20f - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("telemetrySampleRate", expectedTelemetrySampleRate) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("telemetrySampleRate", expectedTelemetrySampleRate) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -397,31 +474,44 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo("additionalConfig", emptyMap()) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("additionalConfig", emptyMap()) + assertThat(rumConfigCaptor.firstValue) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -432,34 +522,47 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -477,35 +580,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.US1) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US1) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -520,35 +637,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.US1) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US1) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -563,35 +694,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.US3) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US3) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -606,35 +751,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.US5) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US5) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -649,35 +808,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.US1_FED) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.US1_FED) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -692,35 +865,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.EU1) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.EU1) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -735,35 +922,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.AP1) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.AP1) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -778,35 +979,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) - it.hasFieldEqualTo("site", DatadogSite.AP2) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) - .hasFieldEqualTo("env", fakeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo( - "additionalConfig", - fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + it.hasFieldEqualTo("site", DatadogSite.AP2) + } + .hasFieldEqualTo("clientToken", fakeConfiguration.clientToken) + .hasFieldEqualTo("env", fakeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo( + "additionalConfig", + fakeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", fakeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -822,19 +1037,33 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - eq(TrackingConsent.PENDING) - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + eq(TrackingConsent.PENDING) + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() } } @@ -850,19 +1079,33 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - eq(TrackingConsent.PENDING) - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + eq(TrackingConsent.PENDING) + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() } } @@ -878,19 +1121,33 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - eq(TrackingConsent.GRANTED) - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + eq(TrackingConsent.GRANTED) + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() } } @@ -906,19 +1163,33 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - eq(TrackingConsent.NOT_GRANTED) - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + eq(TrackingConsent.NOT_GRANTED) + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() } } @@ -937,24 +1208,38 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("viewTrackingStrategy", NoOpViewTrackingStrategy) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("viewTrackingStrategy", NoOpViewTrackingStrategy) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -970,24 +1255,38 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("viewTrackingStrategy", ActivityViewTrackingStrategy(false)) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("viewTrackingStrategy", ActivityViewTrackingStrategy(false)) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1003,24 +1302,38 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("userActionTracking", false) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("userActionTracking", false) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1036,24 +1349,38 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("trackFrustrations", true) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("trackFrustrations", true) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1064,29 +1391,44 @@ internal class DdSdkTest { val bridgeConfiguration = configuration.copy( trackFrustrations = false ) + val sdkConfigCaptor = argumentCaptor() val rumConfigCaptor = argumentCaptor() val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("trackFrustrations", false) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("trackFrustrations", false) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1102,24 +1444,39 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("userActionTracking", true) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("userActionTracking", true) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1177,35 +1534,49 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { - it.hasFieldEqualTo("needsClearTextHttp", false) - it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - .hasFieldEqualTo("clientToken", bridgeConfiguration.clientToken) - .hasFieldEqualTo("env", bridgeConfiguration.env) - .hasFieldEqualTo("variant", "") - .hasFieldEqualTo("service", serviceName) - .hasFieldEqualTo( - "additionalConfig", - bridgeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() - ) - assertThat(rumConfigCaptor.firstValue) - .hasFieldEqualTo("applicationId", bridgeConfiguration.applicationId) + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { + it.hasFieldEqualTo("needsClearTextHttp", false) + it.hasFieldEqualTo("firstPartyHostsWithHeaderTypes", emptyMap()) + } + .hasFieldEqualTo("clientToken", bridgeConfiguration.clientToken) + .hasFieldEqualTo("env", bridgeConfiguration.env) + .hasFieldEqualTo("variant", "") + .hasFieldEqualTo("service", serviceName) + .hasFieldEqualTo( + "additionalConfig", + bridgeConfiguration.additionalConfig?.filterValues { it != null }.orEmpty() + ) + assertThat(rumConfigCaptor.firstValue) + .hasFieldEqualTo("applicationId", bridgeConfiguration.applicationId) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1224,31 +1595,45 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { rumConfig -> - rumConfig.hasField("longTaskTrackingStrategy") { longTaskTrackingStrategy -> - longTaskTrackingStrategy - .isInstanceOf( - "com.datadog.android.rum.internal.instrumentation." + - "MainLooperLongTaskStrategy" - ) - .hasFieldEqualTo("thresholdMs", threshold.toLong()) - } + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { rumConfig -> + rumConfig.hasField("longTaskTrackingStrategy") { longTaskTrackingStrategy -> + longTaskTrackingStrategy + .isInstanceOf( + "com.datadog.android.rum.internal.instrumentation." + + "MainLooperLongTaskStrategy" + ) + .hasFieldEqualTo("thresholdMs", threshold.toLong()) + } + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1265,24 +1650,38 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { rumConfig -> - rumConfig.doesNotHaveField("longTaskTrackingStrategy") + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { rumConfig -> + rumConfig.doesNotHaveField("longTaskTrackingStrategy") + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1326,27 +1725,41 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { coreConfig -> - coreConfig.hasFieldEqualTo( - "firstPartyHostsWithHeaderTypes", - tracingHosts + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "firstPartyHostsWithHeaderTypes", + tracingHosts + ) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1385,27 +1798,41 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { coreConfig -> - coreConfig.hasFieldEqualTo( - "firstPartyHostsWithHeaderTypes", - tracingHosts + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "firstPartyHostsWithHeaderTypes", + tracingHosts + ) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1451,27 +1878,41 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { coreConfig -> - coreConfig.hasFieldEqualTo( - "firstPartyHostsWithHeaderTypes", - tracingHosts + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "firstPartyHostsWithHeaderTypes", + tracingHosts + ) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @ParameterizedTest @@ -1490,27 +1931,41 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { coreConfig -> - coreConfig.hasFieldEqualTo( - "uploadFrequency", - expectedUploadFrequency + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "uploadFrequency", + expectedUploadFrequency + ) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @ParameterizedTest @@ -1529,27 +1984,41 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { coreConfig -> - coreConfig.hasFieldEqualTo( - "batchSize", - expectedBatchSize + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "batchSize", + expectedBatchSize + ) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @ParameterizedTest @@ -1568,27 +2037,41 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasField("coreConfig") { coreConfig -> - coreConfig.hasFieldEqualTo( - "batchProcessingLevel", - expectedBatchSize + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(sdkConfigCaptor.firstValue) + .hasField("coreConfig") { coreConfig -> + coreConfig.hasFieldEqualTo( + "batchProcessingLevel", + expectedBatchSize + ) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1606,24 +2089,38 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("backgroundEventTracking", trackBackgroundEvents ?: false) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("backgroundEventTracking", trackBackgroundEvents ?: false) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1639,28 +2136,42 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.RARE) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } + + // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.RARE) + } - argumentCaptor { - verify(mockChoreographer).postFrameCallback(capture()) - assertThat(firstValue).isInstanceOf(FpsFrameCallback::class.java) + argumentCaptor { + verify(mockChoreographer).postFrameCallback(capture()) + assertThat(firstValue).isInstanceOf(FpsFrameCallback::class.java) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() } } @@ -1679,25 +2190,37 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.NEVER) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - verifyNoInteractions(mockChoreographer) + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.NEVER) + } + verifyNoInteractions(mockChoreographer) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1719,41 +2242,56 @@ internal class DdSdkTest { val traceConfigCaptor = argumentCaptor() val frameDurationNs = threshold + frameDurationOverThreshold - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) - - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("vitalsMonitorUpdateFrequency", VitalsUpdateFrequency.AVERAGE) - } - argumentCaptor { - verify(mockChoreographer).postFrameCallback(capture()) - assertThat(firstValue).isInstanceOf(FpsFrameCallback::class.java) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // When - firstValue.doFrame(timestampNs) - firstValue.doFrame(timestampNs + frameDurationNs) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) - // then - verify(mockRumMonitor._getInternal()!!).updatePerformanceMetric( - RumPerformanceMetric.JS_FRAME_TIME, - frameDurationNs.toDouble() - ) - verify(mockRumMonitor._getInternal()!!, never()).addLongTask( - frameDurationNs, - "javascript" - ) + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } + } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo( + "vitalsMonitorUpdateFrequency", + VitalsUpdateFrequency.AVERAGE + ) + } + argumentCaptor { + verify(mockChoreographer).postFrameCallback(capture()) + assertThat(firstValue).isInstanceOf(FpsFrameCallback::class.java) + + // When + firstValue.doFrame(timestampNs) + firstValue.doFrame(timestampNs + frameDurationNs) + + // then + verify(mockRumMonitor._getInternal()!!).updatePerformanceMetric( + RumPerformanceMetric.JS_FRAME_TIME, + frameDurationNs.toDouble() + ) + verify(mockRumMonitor._getInternal()!!, never()).addLongTask( + frameDurationNs, + "javascript" + ) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() } } @@ -1846,25 +2384,37 @@ internal class DdSdkTest { val traceConfigCaptor = argumentCaptor() val defaultTimeBasedIdentifier = TimeBasedInitialResourceIdentifier(100) - // When - testedBridgeSdk.initialize(configuration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(configuration.toReadableJavaOnlyMap(), mockPromise) - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("initialResourceIdentifier", defaultTimeBasedIdentifier) + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("initialResourceIdentifier", defaultTimeBasedIdentifier) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -1884,25 +2434,37 @@ internal class DdSdkTest { thresholdInSeconds.seconds.inWholeMilliseconds ) - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("initialResourceIdentifier", timeBasedIdentifier) + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("initialResourceIdentifier", timeBasedIdentifier) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -1925,28 +2487,42 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(sdkConfigCaptor.firstValue) - .hasFieldEqualTo( - "additionalConfig", - mapOf( - DdSdkImplementation.DD_VERSION_SUFFIX to versionSuffix, - DdSdkImplementation.DD_VERSION to mockPackageInfo.versionName + versionSuffix + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() ) - ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } + } + assertThat(sdkConfigCaptor.firstValue) + .hasFieldEqualTo( + "additionalConfig", + mapOf( + DdSdkImplementation.DD_VERSION_SUFFIX to versionSuffix, + DdSdkImplementation.DD_VERSION to ( + mockPackageInfo.versionName + versionSuffix + ) + ) + ) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -1985,47 +2561,59 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - val configurationMapper = it - .getActualValue>( - "telemetryConfigurationMapper" - ) - val result = configurationMapper.map(telemetryConfigurationEvent)!! - assertThat(result.telemetry.configuration.trackNativeErrors!!).isEqualTo( - trackNativeErrors - ) - assertThat(result.telemetry.configuration.trackCrossPlatformLongTasks!!) - .isEqualTo(false) - assertThat(result.telemetry.configuration.trackLongTask!!) - .isEqualTo(false) - assertThat(result.telemetry.configuration.trackNativeLongTasks!!) - .isEqualTo(false) - - assertThat(result.telemetry.configuration.initializationType!!) - .isEqualTo(initializationType) - assertThat(result.telemetry.configuration.trackInteractions!!) - .isEqualTo(trackInteractions) - assertThat(result.telemetry.configuration.trackErrors!!).isEqualTo(trackErrors) - assertThat(result.telemetry.configuration.trackResources!!) - .isEqualTo(trackNetworkRequests) - assertThat(result.telemetry.configuration.trackNetworkRequests!!) - .isEqualTo(trackNetworkRequests) + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + val configurationMapper = it + .getActualValue>( + "telemetryConfigurationMapper" + ) + val result = configurationMapper.map(telemetryConfigurationEvent)!! + assertThat(result.telemetry.configuration.trackNativeErrors!!).isEqualTo( + trackNativeErrors + ) + assertThat(result.telemetry.configuration.trackCrossPlatformLongTasks!!) + .isEqualTo(false) + assertThat(result.telemetry.configuration.trackLongTask!!) + .isEqualTo(false) + assertThat(result.telemetry.configuration.trackNativeLongTasks!!) + .isEqualTo(false) + + assertThat(result.telemetry.configuration.initializationType!!) + .isEqualTo(initializationType) + assertThat(result.telemetry.configuration.trackInteractions!!) + .isEqualTo(trackInteractions) + assertThat(result.telemetry.configuration.trackErrors!!).isEqualTo(trackErrors) + assertThat(result.telemetry.configuration.trackResources!!) + .isEqualTo(trackNetworkRequests) + assertThat(result.telemetry.configuration.trackNetworkRequests!!) + .isEqualTo(trackNetworkRequests) + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -2042,27 +2630,39 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - val resourceMapper = it - .getActualValue>("resourceEventMapper") - val notDroppedEvent = resourceMapper.map(resourceEvent) - assertThat(notDroppedEvent).isNotNull + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + val resourceMapper = it + .getActualValue>("resourceEventMapper") + val notDroppedEvent = resourceMapper.map(resourceEvent) + assertThat(notDroppedEvent).isNotNull + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -2076,27 +2676,39 @@ internal class DdSdkTest { val traceConfigCaptor = argumentCaptor() resourceEvent.context?.additionalProperties?.put("_dd.resource.drop_resource", true) - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - val resourceMapper = it - .getActualValue>("resourceEventMapper") - val droppedEvent = resourceMapper.map(resourceEvent) - assertThat(droppedEvent).isNull() + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + val resourceMapper = it + .getActualValue>("resourceEventMapper") + val droppedEvent = resourceMapper.map(resourceEvent) + assertThat(droppedEvent).isNull() + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -2113,27 +2725,39 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - val actionMapper = it - .getActualValue>("actionEventMapper") - val notDroppedEvent = actionMapper.map(actionEvent) - assertThat(notDroppedEvent).isNotNull + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + val actionMapper = it + .getActualValue>("actionEventMapper") + val notDroppedEvent = actionMapper.map(actionEvent) + assertThat(notDroppedEvent).isNotNull + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test @@ -2147,27 +2771,39 @@ internal class DdSdkTest { val traceConfigCaptor = argumentCaptor() actionEvent.context?.additionalProperties?.put("_dd.action.drop_action", true) - // When - testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).initialize( - same(mockContext), - sdkConfigCaptor.capture(), - any() - ) - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - val actionMapper = it - .getActualValue>("actionEventMapper") - val droppedEvent = actionMapper.map(actionEvent) - assertThat(droppedEvent).isNull() + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(fakeConfiguration.toReadableJavaOnlyMap(), mockPromise) + + // Then + inOrder(mockDatadog) { + verify(mockDatadog).initialize( + same(mockContext), + sdkConfigCaptor.capture(), + any() + ) + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + val actionMapper = it + .getActualValue>("actionEventMapper") + val droppedEvent = actionMapper.map(actionEvent) + assertThat(droppedEvent).isNull() + } + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } // endregion @@ -2580,24 +3216,36 @@ internal class DdSdkTest { val logsConfigCaptor = argumentCaptor() val traceConfigCaptor = argumentCaptor() - // When - testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) + val rumMock = org.mockito.Mockito.mockStatic(Rum::class.java) + val traceMock = org.mockito.Mockito.mockStatic(Trace::class.java) + val logsMock = org.mockito.Mockito.mockStatic(Logs::class.java) - // Then - inOrder(mockDatadog) { - verify(mockDatadog).enableRum(rumConfigCaptor.capture()) - verify(mockDatadog).enableTrace(traceConfigCaptor.capture()) - verify(mockDatadog).enableLogs(logsConfigCaptor.capture()) - } + try { + rumMock.`when` { Rum.enable(any(), any()) }.then { } + logsMock.`when` { Logs.enable(any(), any()) }.then { } + traceMock.`when` { Trace.enable(any(), any()) }.then { } // When + testedBridgeSdk.initialize(bridgeConfiguration.toReadableJavaOnlyMap(), mockPromise) - assertThat(rumConfigCaptor.firstValue) - .hasField("featureConfiguration") { - it.hasFieldEqualTo("customEndpointUrl", customRumEndpoint) + // Then + inOrder(mockDatadog) { + rumMock.verify { Rum.enable(rumConfigCaptor.capture(), any()) } + traceMock.verify { Trace.enable(traceConfigCaptor.capture(), any()) } + logsMock.verify { Logs.enable(logsConfigCaptor.capture(), any()) } } - assertThat(logsConfigCaptor.firstValue) - .hasFieldEqualTo("customEndpointUrl", customLogsEndpoint) - assertThat(traceConfigCaptor.firstValue) - .hasFieldEqualTo("customEndpointUrl", customTraceEndpoint) + + assertThat(rumConfigCaptor.firstValue) + .hasField("featureConfiguration") { + it.hasFieldEqualTo("customEndpointUrl", customRumEndpoint) + } + assertThat(logsConfigCaptor.firstValue) + .hasFieldEqualTo("customEndpointUrl", customLogsEndpoint) + assertThat(traceConfigCaptor.firstValue) + .hasFieldEqualTo("customEndpointUrl", customTraceEndpoint) + } finally { + rumMock.close() + logsMock.close() + traceMock.close() + } } @Test diff --git a/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt b/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt index df0030fb5..b33bef6de 100644 --- a/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt +++ b/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt @@ -58,6 +58,12 @@ class DdInternalTestingImplementation { promise.resolve(null) } + /** + * Get wrapped core instance. + */ + internal fun getWrappedCore(): StubSDKCore? { + return wrappedCore + } internal companion object { internal const val NAME = "DdInternalTesting" diff --git a/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt b/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt index c542ed6bc..4a6938f9b 100644 --- a/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt +++ b/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt @@ -80,7 +80,9 @@ internal class DdInternalTestingImplementationTest { // Simulating DdSdkImplementation initialization DatadogSDKWrapperStorage.notifyOnInitializedListeners(mockCore) - val wrappedCore = Datadog.getInstance() as StubSDKCore + val wrappedCore = testedInternalTesting.getWrappedCore() + requireNotNull(wrappedCore) + wrappedCore.registerFeature(mockFeature) requireNotNull(wrappedCore.getFeature(mockFeature.name)) .withWriteContext { _, eventBatchWriter -> From 1759eb7bcb1b348bdb6bfedd5967722e5c074f42 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 5 Sep 2025 15:29:37 +0200 Subject: [PATCH 018/168] Remove type interdependencies between modules --- packages/core/src/{rum => }/DdAttributes.ts | 0 .../src/DdSdkReactNativeConfiguration.tsx | 2 +- .../src/__tests__/DdSdkReactNative.test.tsx | 3 +- packages/core/src/index.tsx | 3 +- packages/core/src/logs/DdLogs.ts | 5 +- .../core/src/logs/__tests__/DdLogs.test.ts | 4 +- .../src/logs/__tests__/eventMapper.test.ts | 2 +- packages/core/src/logs/eventMapper.ts | 3 +- packages/core/src/logs/types.ts | 21 +------- packages/core/src/rum/DdRum.ts | 4 +- packages/core/src/rum/__tests__/DdRum.test.ts | 3 +- .../src/rum/eventMappers/errorEventMapper.ts | 2 +- .../instrumentation/DdRumErrorTracking.tsx | 2 +- packages/core/src/rum/types.ts | 10 +--- packages/core/src/types.tsx | 49 ++++++++++++++++--- 15 files changed, 62 insertions(+), 51 deletions(-) rename packages/core/src/{rum => }/DdAttributes.ts (100%) diff --git a/packages/core/src/rum/DdAttributes.ts b/packages/core/src/DdAttributes.ts similarity index 100% rename from packages/core/src/rum/DdAttributes.ts rename to packages/core/src/DdAttributes.ts diff --git a/packages/core/src/DdSdkReactNativeConfiguration.tsx b/packages/core/src/DdSdkReactNativeConfiguration.tsx index 9be08fa28..44debb2d2 100644 --- a/packages/core/src/DdSdkReactNativeConfiguration.tsx +++ b/packages/core/src/DdSdkReactNativeConfiguration.tsx @@ -7,12 +7,12 @@ import type { ProxyConfiguration } from './ProxyConfiguration'; import type { SdkVerbosity } from './SdkVerbosity'; import { TrackingConsent } from './TrackingConsent'; -import type { LogEventMapper } from './logs/types'; import type { ActionEventMapper } from './rum/eventMappers/actionEventMapper'; import type { ErrorEventMapper } from './rum/eventMappers/errorEventMapper'; import type { ResourceEventMapper } from './rum/eventMappers/resourceEventMapper'; import type { FirstPartyHost } from './rum/types'; import { PropagatorType } from './rum/types'; +import type { LogEventMapper } from './types'; export enum VitalsUpdateFrequency { FREQUENT = 'FREQUENT', diff --git a/packages/core/src/__tests__/DdSdkReactNative.test.tsx b/packages/core/src/__tests__/DdSdkReactNative.test.tsx index 49c0bd1f2..18bf060ce 100644 --- a/packages/core/src/__tests__/DdSdkReactNative.test.tsx +++ b/packages/core/src/__tests__/DdSdkReactNative.test.tsx @@ -17,11 +17,12 @@ import { DdRum } from '../rum/DdRum'; import { DdRumErrorTracking } from '../rum/instrumentation/DdRumErrorTracking'; import { DdRumUserInteractionTracking } from '../rum/instrumentation/interactionTracking/DdRumUserInteractionTracking'; import { DdRumResourceTracking } from '../rum/instrumentation/resourceTracking/DdRumResourceTracking'; -import { ErrorSource, PropagatorType, RumActionType } from '../rum/types'; +import { PropagatorType, RumActionType } from '../rum/types'; import { AttributesSingleton } from '../sdk/AttributesSingleton/AttributesSingleton'; import { DdSdk } from '../sdk/DdSdk'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; import { UserInfoSingleton } from '../sdk/UserInfoSingleton/UserInfoSingleton'; +import { ErrorSource } from '../types'; import type { DdSdkConfiguration } from '../types'; import { version as sdkVersion } from '../version'; diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index 9332354dc..062fecc90 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -37,11 +37,12 @@ import { DATADOG_GRAPH_QL_VARIABLES_HEADER } from './rum/instrumentation/resourceTracking/graphql/graphqlHeaders'; import type { FirstPartyHost } from './rum/types'; -import { ErrorSource, PropagatorType, RumActionType } from './rum/types'; +import { PropagatorType, RumActionType } from './rum/types'; import { DatadogProvider } from './sdk/DatadogProvider/DatadogProvider'; import { DdSdk } from './sdk/DdSdk'; import { FileBasedConfiguration } from './sdk/FileBasedConfiguration/FileBasedConfiguration'; import { DdTrace } from './trace/DdTrace'; +import { ErrorSource } from './types'; import { DefaultTimeProvider } from './utils/time-provider/DefaultTimeProvider'; import type { Timestamp } from './utils/time-provider/TimeProvider'; import { TimeProvider } from './utils/time-provider/TimeProvider'; diff --git a/packages/core/src/logs/DdLogs.ts b/packages/core/src/logs/DdLogs.ts index 17ec3af1b..32d3dac59 100644 --- a/packages/core/src/logs/DdLogs.ts +++ b/packages/core/src/logs/DdLogs.ts @@ -4,12 +4,12 @@ * Copyright 2016-Present Datadog, Inc. */ +import { DdAttributes } from '../DdAttributes'; import { DATADOG_MESSAGE_PREFIX, InternalLog } from '../InternalLog'; import { SdkVerbosity } from '../SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; import type { DdNativeLogsType } from '../nativeModulesTypes'; -import { DdAttributes } from '../rum/DdAttributes'; -import type { ErrorSource } from '../rum/types'; +import type { ErrorSource, LogEventMapper } from '../types'; import { validateContext } from '../utils/argsUtils'; import { getGlobalInstance } from '../utils/singletonUtils'; @@ -17,7 +17,6 @@ import { generateEventMapper } from './eventMapper'; import type { DdLogsType, LogArguments, - LogEventMapper, LogWithErrorArguments, NativeLogWithError, RawLogWithError diff --git a/packages/core/src/logs/__tests__/DdLogs.test.ts b/packages/core/src/logs/__tests__/DdLogs.test.ts index b8d0f4121..2dd15f46a 100644 --- a/packages/core/src/logs/__tests__/DdLogs.test.ts +++ b/packages/core/src/logs/__tests__/DdLogs.test.ts @@ -11,9 +11,9 @@ import { DdSdkReactNative } from '../../DdSdkReactNative'; import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; import type { DdNativeLogsType } from '../../nativeModulesTypes'; -import { ErrorSource } from '../../rum/types'; +import { ErrorSource } from '../../types'; +import type { LogEventMapper } from '../../types'; import { DdLogs } from '../DdLogs'; -import type { LogEventMapper } from '../types'; jest.mock('../../InternalLog', () => { return { diff --git a/packages/core/src/logs/__tests__/eventMapper.test.ts b/packages/core/src/logs/__tests__/eventMapper.test.ts index 0999a6058..cd505f811 100644 --- a/packages/core/src/logs/__tests__/eventMapper.test.ts +++ b/packages/core/src/logs/__tests__/eventMapper.test.ts @@ -5,7 +5,7 @@ */ /* eslint-disable @typescript-eslint/ban-ts-comment */ -import { ErrorSource } from '../../rum/types'; +import { ErrorSource } from '../../types'; import { formatRawLogToLogEvent } from '../eventMapper'; describe('formatRawLogToLogEvent', () => { diff --git a/packages/core/src/logs/eventMapper.ts b/packages/core/src/logs/eventMapper.ts index 2bbd398cb..eb7b5f22c 100644 --- a/packages/core/src/logs/eventMapper.ts +++ b/packages/core/src/logs/eventMapper.ts @@ -7,10 +7,9 @@ import type { Attributes } from '../sdk/AttributesSingleton/types'; import { EventMapper } from '../sdk/EventMappers/EventMapper'; import type { UserInfo } from '../sdk/UserInfoSingleton/types'; +import type { LogEvent, LogEventMapper } from '../types'; import type { - LogEvent, - LogEventMapper, NativeLog, NativeLogWithError, RawLog, diff --git a/packages/core/src/logs/types.ts b/packages/core/src/logs/types.ts index 9c1b3cb09..18cdbc533 100644 --- a/packages/core/src/logs/types.ts +++ b/packages/core/src/logs/types.ts @@ -4,8 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ -import type { ErrorSource } from '../rum/types'; -import type { UserInfo } from '../sdk/UserInfoSingleton/types'; +import type { LogStatus, ErrorSource } from '../types'; /** * The entry point to use Datadog's Logs feature. @@ -75,24 +74,6 @@ export type NativeLogWithError = { fingerprint?: string; }; -export type LogStatus = 'debug' | 'info' | 'warn' | 'error'; - -export type LogEvent = { - message: string; - context: object; - errorKind?: string; - errorMessage?: string; - stacktrace?: string; - fingerprint?: string; - readonly source?: ErrorSource; - // readonly date: number; // TODO: RUMM-2446 & RUMM-2447 - readonly status: LogStatus; - readonly userInfo: UserInfo; - readonly attributes?: object; -}; - -export type LogEventMapper = (logEvent: LogEvent) => LogEvent | null; - export type LogArguments = [message: string, context?: object]; export type LogWithErrorArguments = [ diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index 1241c73a4..b4a8632d7 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -5,6 +5,7 @@ */ import type { GestureResponderEvent } from 'react-native'; +import { DdAttributes } from '../DdAttributes'; import { InternalLog } from '../InternalLog'; import { SdkVerbosity } from '../SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; @@ -12,12 +13,12 @@ import type { DdNativeRumType } from '../nativeModulesTypes'; import { bufferVoidNativeCall } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; import { DdSdk } from '../sdk/DdSdk'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; +import type { ErrorSource } from '../types'; import { validateContext } from '../utils/argsUtils'; import { getGlobalInstance } from '../utils/singletonUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; import type { TimeProvider } from '../utils/time-provider/TimeProvider'; -import { DdAttributes } from './DdAttributes'; import { generateActionEventMapper } from './eventMappers/actionEventMapper'; import type { ActionEventMapper } from './eventMappers/actionEventMapper'; import { generateErrorEventMapper } from './eventMappers/errorEventMapper'; @@ -37,7 +38,6 @@ import { setCachedSessionId } from './sessionId/sessionIdHelper'; import type { - ErrorSource, DdRumType, RumActionType, ResourceKind, diff --git a/packages/core/src/rum/__tests__/DdRum.test.ts b/packages/core/src/rum/__tests__/DdRum.test.ts index a3b9ca2b7..ec765ed5d 100644 --- a/packages/core/src/rum/__tests__/DdRum.test.ts +++ b/packages/core/src/rum/__tests__/DdRum.test.ts @@ -12,6 +12,7 @@ import { SdkVerbosity } from '../../SdkVerbosity'; import { BufferSingleton } from '../../sdk/DatadogProvider/Buffer/BufferSingleton'; import { DdSdk } from '../../sdk/DdSdk'; import { GlobalState } from '../../sdk/GlobalState/GlobalState'; +import { ErrorSource } from '../../types'; import { DdRum } from '../DdRum'; import type { ActionEventMapper } from '../eventMappers/actionEventMapper'; import type { ErrorEventMapper } from '../eventMappers/errorEventMapper'; @@ -22,7 +23,7 @@ import { TracingIdFormat } from '../instrumentation/resourceTracking/distributed import { TracingIdentifierUtils } from '../instrumentation/resourceTracking/distributedTracing/__tests__/__utils__/TracingIdentifierUtils'; import { setCachedSessionId } from '../sessionId/sessionIdHelper'; import type { FirstPartyHost } from '../types'; -import { ErrorSource, PropagatorType, RumActionType } from '../types'; +import { PropagatorType, RumActionType } from '../types'; import * as TracingContextUtils from './__utils__/TracingHeadersUtils'; diff --git a/packages/core/src/rum/eventMappers/errorEventMapper.ts b/packages/core/src/rum/eventMappers/errorEventMapper.ts index 462754d2c..6630d59a4 100644 --- a/packages/core/src/rum/eventMappers/errorEventMapper.ts +++ b/packages/core/src/rum/eventMappers/errorEventMapper.ts @@ -6,7 +6,7 @@ import type { AdditionalEventDataForMapper } from '../../sdk/EventMappers/EventMapper'; import { EventMapper } from '../../sdk/EventMappers/EventMapper'; -import type { ErrorSource } from '../types'; +import type { ErrorSource } from '../../types'; type RawError = { message: string; diff --git a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx index 774fa70f5..09cd447fb 100644 --- a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx +++ b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx @@ -8,6 +8,7 @@ import type { ErrorHandlerCallback } from 'react-native'; import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; +import { ErrorSource } from '../../types'; import { getErrorMessage, getErrorStackTrace, @@ -17,7 +18,6 @@ import { } from '../../utils/errorUtils'; import { executeWithDelay } from '../../utils/jsUtils'; import { DdRum } from '../DdRum'; -import { ErrorSource } from '../types'; /** * Provides RUM auto-instrumentation feature to track errors as RUM events. diff --git a/packages/core/src/rum/types.ts b/packages/core/src/rum/types.ts index 5834123a6..fc8d07c02 100644 --- a/packages/core/src/rum/types.ts +++ b/packages/core/src/rum/types.ts @@ -4,6 +4,8 @@ * Copyright 2016-Present Datadog, Inc. */ +import type { ErrorSource } from '../types'; + import type { DatadogTracingContext } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingContext'; import type { DatadogTracingIdentifier } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingIdentifier'; @@ -233,14 +235,6 @@ export type ResourceKind = | 'other' | 'native'; -export enum ErrorSource { - NETWORK = 'NETWORK', - SOURCE = 'SOURCE', - CONSOLE = 'CONSOLE', - WEBVIEW = 'WEBVIEW', - CUSTOM = 'CUSTOM' -} - /** * Type of instrumentation on the host. * - DATADOG: Datadog’s propagator (`x-datadog-*`) diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 4db877469..e1c5096fb 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -5,6 +5,7 @@ */ import type { BatchProcessingLevel } from './DdSdkReactNativeConfiguration'; +import type { UserInfo as UserInfoSingleton } from './sdk/UserInfoSingleton/types'; declare global { // eslint-disable-next-line no-var, vars-on-top @@ -118,13 +119,6 @@ export type DdSdkType = { setTrackingConsent(trackingConsent: string): Promise; }; -export type UserInfo = { - id: string; - name?: string; - email?: string; - extraInfo?: object; -}; - /** * The entry point to use Datadog's Trace feature. */ @@ -153,3 +147,44 @@ export type DdTraceType = { timestampMs?: number ): Promise; }; + +// Shared types across modules + +// Core + +export type UserInfo = { + id: string; + name?: string; + email?: string; + extraInfo?: object; +}; + +// DdLogs + +export type LogStatus = 'debug' | 'info' | 'warn' | 'error'; + +export type LogEvent = { + message: string; + context: object; + errorKind?: string; + errorMessage?: string; + stacktrace?: string; + fingerprint?: string; + readonly source?: ErrorSource; + // readonly date: number; // TODO: RUMM-2446 & RUMM-2447 + readonly status: LogStatus; + readonly userInfo: UserInfoSingleton; + readonly attributes?: object; +}; + +export type LogEventMapper = (logEvent: LogEvent) => LogEvent | null; + +// DdRum + +export enum ErrorSource { + NETWORK = 'NETWORK', + SOURCE = 'SOURCE', + CONSOLE = 'CONSOLE', + WEBVIEW = 'WEBVIEW', + CUSTOM = 'CUSTOM' +} From 3a687f57658c617a198a9ab2905e23202f0feb37 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 9 Sep 2025 12:08:35 +0200 Subject: [PATCH 019/168] iOS: Always use SDK default core instance --- .../core/ios/Sources/DatadogSDKWrapper.swift | 44 ++++++-------- .../ios/Sources/DdLogsImplementation.swift | 7 ++- .../ios/Sources/DdSdkImplementation.swift | 17 ++++-- .../Sources/DdSdkNativeInitialization.swift | 22 ++++--- packages/core/ios/Sources/DdTelemetry.swift | 49 ++++++++++++++++ .../ios/Tests/DatadogSdkWrapperTests.swift | 17 +++--- packages/core/ios/Tests/DdSdkTests.swift | 58 ++++++++----------- .../DdSessionReplayImplementation.swift | 28 ++------- .../ios/Tests/DdSessionReplayTests.swift | 6 +- .../Sources/RCTDatadogWebViewTracking.swift | 18 +++--- .../DatadogSDKReactNativeWebViewTests.swift | 14 +++-- 11 files changed, 158 insertions(+), 122 deletions(-) create mode 100644 packages/core/ios/Sources/DdTelemetry.swift diff --git a/packages/core/ios/Sources/DatadogSDKWrapper.swift b/packages/core/ios/Sources/DatadogSDKWrapper.swift index 9cbac3bd8..3c56688b0 100644 --- a/packages/core/ios/Sources/DatadogSDKWrapper.swift +++ b/packages/core/ios/Sources/DatadogSDKWrapper.swift @@ -13,11 +13,15 @@ import DatadogCrashReporting import DatadogInternal import Foundation +<<<<<<< HEAD #if os(iOS) import DatadogWebViewTracking #endif public typealias OnCoreInitializedListener = (DatadogCoreProtocol) -> Void +======= +public typealias OnSdkInitializedListener = () -> Void +>>>>>>> 0443e0ff (iOS: Always use SDK default core instance) /// Wrapper around the Datadog SDK. Use DatadogSDKWrapper.shared to access the instance. public class DatadogSDKWrapper { @@ -25,25 +29,14 @@ public class DatadogSDKWrapper { public static var shared = DatadogSDKWrapper() // Initialization callbacks - internal var onCoreInitializedListeners: [OnCoreInitializedListener] = [] - internal var loggerConfiguration = DatadogLogs.Logger.Configuration() - // Core instance - private var coreInstance: DatadogCoreProtocol? = nil + internal var onSdkInitializedListeners: [OnSdkInitializedListener] = [] - private init() { } - - public func addOnCoreInitializedListener(listener:@escaping OnCoreInitializedListener) { - onCoreInitializedListeners.append(listener) - } + internal private(set) var loggerConfiguration = DatadogLogs.Logger.Configuration() - /// This is intended for internal testing only. - public func setCoreInstance(core: DatadogCoreProtocol?) { - self.coreInstance = core - } + private init() { } - /// This is not supposed to be used in the SDK itself, rather by other SDKs like Session Replay. - public func getCoreInstance() -> DatadogCoreProtocol? { - return coreInstance + public func addOnSdkInitializedListener(listener:@escaping OnSdkInitializedListener) { + onSdkInitializedListeners.append(listener) } // SDK Wrapper @@ -52,15 +45,16 @@ public class DatadogSDKWrapper { loggerConfiguration: DatadogLogs.Logger.Configuration, trackingConsent: TrackingConsent ) -> Void { - let core = Datadog.initialize(with: coreConfiguration, trackingConsent: trackingConsent) - setCoreInstance(core: core) - for listener in onCoreInitializedListeners { - listener(core) + Datadog.initialize(with: coreConfiguration, trackingConsent: trackingConsent) + + for listener in onSdkInitializedListeners { + listener() } self.loggerConfiguration = loggerConfiguration } +<<<<<<< HEAD internal func isInitialized() -> Bool { return Datadog.isInitialized() } @@ -161,17 +155,15 @@ public class DatadogSDKWrapper { #if os(iOS) +======= +>>>>>>> 0443e0ff (iOS: Always use SDK default core instance) // Webview private var webviewMessageEmitter: InternalExtension.AbstractMessageEmitter? internal func enableWebviewTracking() { - if let core = coreInstance { - webviewMessageEmitter = WebViewTracking._internal.messageEmitter(in: core) - } else { - consolePrint("Core instance was not found when initializing Webview tracking.", .critical) - } + webviewMessageEmitter = WebViewTracking._internal.messageEmitter(in: CoreRegistry.default) } - + internal func sendWebviewMessage(body: NSString) throws { try self.webviewMessageEmitter?.send(body: body) } diff --git a/packages/core/ios/Sources/DdLogsImplementation.swift b/packages/core/ios/Sources/DdLogsImplementation.swift index 264a3d0b3..fe3fde092 100644 --- a/packages/core/ios/Sources/DdLogsImplementation.swift +++ b/packages/core/ios/Sources/DdLogsImplementation.swift @@ -5,7 +5,9 @@ */ import Foundation +import DatadogInternal import DatadogLogs +import DatadogCore @objc public class DdLogsImplementation: NSObject { @@ -20,7 +22,10 @@ public class DdLogsImplementation: NSObject { @objc public override convenience init() { - self.init({ DatadogSDKWrapper.shared.createLogger() }, { DatadogSDKWrapper.shared.isInitialized() }) + self.init( + { DatadogLogs.Logger.create(with: DatadogSDKWrapper.shared.loggerConfiguration) }, + { Datadog.isInitialized() } + ) } @objc diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index b4ddc59fd..973b8db9f 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -139,42 +139,47 @@ public class DdSdkImplementation: NSObject { public func sendTelemetryLog(message: NSString, attributes: NSDictionary, config: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { let castedAttributes = castAttributesToSwift(attributes) let castedConfig = castAttributesToSwift(config) - DatadogSDKWrapper.shared.sendTelemetryLog(message: message as String, attributes: castedAttributes, config: castedConfig) + DdTelemetry.sendTelemetryLog(message: message as String, attributes: castedAttributes, config: castedConfig) resolve(nil) } @objc public func telemetryDebug(message: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DatadogSDKWrapper.shared.telemetryDebug(id: "datadog_react_native:\(message)", message: message as String) + DdTelemetry.telemetryDebug(id: "datadog_react_native:\(message)", message: message as String) resolve(nil) } @objc public func telemetryError(message: NSString, stack: NSString, kind: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DatadogSDKWrapper.shared.telemetryError(id: "datadog_react_native:\(String(describing: kind)):\(message)", message: message as String, kind: kind as String, stack: stack as String) + DdTelemetry.telemetryError(id: "datadog_react_native:\(String(describing: kind)):\(message)", message: message as String, kind: kind as String, stack: stack as String) resolve(nil) } +<<<<<<< HEAD #if os(iOS) +======= + +>>>>>>> 0443e0ff (iOS: Always use SDK default core instance) @objc public func consumeWebviewEvent(message: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { do{ try DatadogSDKWrapper.shared.sendWebviewMessage(body: message) } catch { - DatadogSDKWrapper.shared.telemetryError(id: "datadog_react_native:\(error.localizedDescription)", message: "The message being sent was:\(message)" as String, kind: "WebViewEventBridgeError" as String, stack: String(describing: error) as String) + DdTelemetry.telemetryError(id: "datadog_react_native:\(error.localizedDescription)", message: "The message being sent was:\(message)" as String, kind: "WebViewEventBridgeError" as String, stack: String(describing: error) as String) } + resolve(nil) } #endif @objc public func clearAllData(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DatadogSDKWrapper.shared.clearAllData() + Datadog.clearAllData() resolve(nil) } func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration) -> Void { - DatadogSDKWrapper.shared.overrideTelemetryConfiguration( + DdTelemetry.overrideTelemetryConfiguration( initializationType: rnConfiguration.configurationForTelemetry?.initializationType as? String, reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion as? String, reactVersion: rnConfiguration.configurationForTelemetry?.reactVersion as? String, diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index dec3cbcb2..4ea229871 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -33,12 +33,13 @@ public class DdSdkNativeInitialization: NSObject { } internal func initialize(sdkConfiguration: DdSdkConfiguration) { - // TODO: see if this `if` is still needed - if DatadogSDKWrapper.shared.isInitialized() { - // Initializing the SDK twice results in Global.rum and - // Global.sharedTracer to be set to no-op instances + if Datadog.isInitialized(instanceName: CoreRegistry.defaultInstanceName) { + // Initializing the SDK twice results in Global.rum and Global.sharedTracer to be set to no-op instances consolePrint("Datadog SDK is already initialized, skipping initialization.", .debug) - DatadogSDKWrapper.shared.telemetryDebug(id: "datadog_react_native: RN SDK was already initialized in native", message: "RN SDK was already initialized in native") + DdTelemetry.telemetryDebug( + id: "datadog_react_native: RN SDK was already initialized in native", + message: "RN SDK was already initialized in native" + ) RUMMonitor.shared().currentSessionID { sessionId in guard let id = sessionId else { return } @@ -81,21 +82,24 @@ public class DdSdkNativeInitialization: NSObject { func enableFeatures(sdkConfiguration: DdSdkConfiguration) { let rumConfig = buildRUMConfiguration(configuration: sdkConfiguration) - DatadogSDKWrapper.shared.enableRUM(with: rumConfig) + RUM.enable(with: rumConfig) let logsConfig = buildLogsConfiguration(configuration: sdkConfiguration) - DatadogSDKWrapper.shared.enableLogs(with: logsConfig) + Logs.enable(with: logsConfig) let traceConfig = buildTraceConfiguration(configuration: sdkConfiguration) - DatadogSDKWrapper.shared.enableTrace(with: traceConfig) + Trace.enable(with: traceConfig) if sdkConfiguration.nativeCrashReportEnabled ?? false { - DatadogSDKWrapper.shared.enableCrashReporting() + CrashReporting.enable() } +<<<<<<< HEAD #if os(iOS) DatadogSDKWrapper.shared.enableWebviewTracking() #endif +======= +>>>>>>> 0443e0ff (iOS: Always use SDK default core instance) } func buildSDKConfiguration(configuration: DdSdkConfiguration, defaultAppVersion: String = getDefaultAppVersion()) -> Datadog.Configuration { diff --git a/packages/core/ios/Sources/DdTelemetry.swift b/packages/core/ios/Sources/DdTelemetry.swift new file mode 100644 index 000000000..cb1896db8 --- /dev/null +++ b/packages/core/ios/Sources/DdTelemetry.swift @@ -0,0 +1,49 @@ + +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2019-2025 Datadog, Inc. + */ +import DatadogCore +import DatadogInternal + +public class DdTelemetry { + public static func sendTelemetryLog(message: String, attributes: [String: any Encodable], config: [String: any Encodable]) { + let id = (config["onlyOnce"] as? Bool) == true ? message : UUID().uuidString + CoreRegistry.default.telemetry.debug(id: id, message: message, attributes: attributes) + } + + public static func telemetryDebug(id: String, message: String) { + return Datadog._internal.telemetry.debug(id: id, message: message) + } + + public static func telemetryError(id: String, message: String, kind: String?, stack: String?) { + return Datadog._internal.telemetry.error(id: id, message: message, kind: kind, stack: stack) + } + + public static func overrideTelemetryConfiguration( + initializationType: String? = nil, + reactNativeVersion: String? = nil, + reactVersion: String? = nil, + trackCrossPlatformLongTasks: Bool? = nil, + trackErrors: Bool? = nil, + trackInteractions: Bool? = nil, + trackLongTask: Bool? = nil, + trackNativeErrors: Bool? = nil, + trackNativeLongTasks: Bool? = nil, + trackNetworkRequests: Bool? = nil + ) { + CoreRegistry.default.telemetry.configuration( + initializationType: initializationType, + reactNativeVersion: reactNativeVersion, + reactVersion: reactVersion, + trackCrossPlatformLongTasks: trackCrossPlatformLongTasks, + trackErrors: trackErrors, + trackLongTask: trackLongTask, + trackNativeErrors: trackNativeErrors, + trackNativeLongTasks: trackNativeLongTasks, + trackNetworkRequests: trackNetworkRequests, + trackUserInteractions: trackInteractions + ) + } +} diff --git a/packages/core/ios/Tests/DatadogSdkWrapperTests.swift b/packages/core/ios/Tests/DatadogSdkWrapperTests.swift index 4811b4c1d..a445b2bbe 100644 --- a/packages/core/ios/Tests/DatadogSdkWrapperTests.swift +++ b/packages/core/ios/Tests/DatadogSdkWrapperTests.swift @@ -8,22 +8,23 @@ import XCTest @testable import DatadogSDKReactNative import DatadogTrace import DatadogInternal - +import DatadogRUM +import DatadogLogs internal class DatadogSdkWrapperTests: XCTestCase { override func setUp() { super.setUp() - DatadogSDKWrapper.shared.setCoreInstance(core: nil) - DatadogSDKWrapper.shared.onCoreInitializedListeners = [] + DatadogSDKWrapper.shared.onSdkInitializedListeners = [] } - func testItSetsCoreUsedForFeatures() { + func testOverrideCoreRegistryDefault() { let coreMock = MockDatadogCore() - DatadogSDKWrapper.shared.setCoreInstance(core: coreMock) + CoreRegistry.register(default: coreMock) + defer { CoreRegistry.unregisterDefault() } - DatadogSDKWrapper.shared.enableTrace(with: .init()) - DatadogSDKWrapper.shared.enableRUM(with: .init(applicationID: "app-id")) - DatadogSDKWrapper.shared.enableLogs(with: .init()) + Trace.enable(with: .init()) + RUM.enable(with: .init(applicationID: "app-id")) + Logs.enable(with: .init()) XCTAssertNotNil(coreMock.features["tracing"]) XCTAssertNotNil(coreMock.features["rum"]) diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index edbbba4dd..bcc3252a8 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -34,8 +34,7 @@ class DdSdkTests: XCTestCase { private func mockReject(args _: String?, arg _: String?, err _: Error?) {} override func tearDown() { - DatadogSDKWrapper.shared.setCoreInstance(core: nil) - DatadogSDKWrapper.shared.onCoreInitializedListeners = [] + DatadogSDKWrapper.shared.onSdkInitializedListeners = [] Datadog.internalFlushAndDeinitialize() } @@ -84,11 +83,10 @@ class DdSdkTests: XCTestCase { let bridge = DispatchQueueMock() let mockJSRefreshRateMonitor = MockJSRefreshRateMonitor() let mockListener = MockOnCoreInitializedListener() - DatadogSDKWrapper.shared.addOnCoreInitializedListener(listener: mockListener.listener) + DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: mockListener.listener) - let expectation = self.expectation(description: "Core is set when promise resolves") + let expectation = self.expectation(description: "Listener is called when promise resolves") func mockPromiseResolve(_: Any?) { - XCTAssertNotNil(mockListener.core) expectation.fulfill() } @@ -276,9 +274,9 @@ class DdSdkTests: XCTestCase { } func testSDKInitializationWithOnInitializedCallback() { - var coreFromCallback: DatadogCoreProtocol? = nil - DatadogSDKWrapper.shared.addOnCoreInitializedListener(listener: { core in - coreFromCallback = core + var isInitialized = false + DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: { + isInitialized = Datadog.isInitialized() }) DdSdkImplementation( @@ -293,14 +291,16 @@ class DdSdkTests: XCTestCase { reject: mockReject ) - XCTAssertNotNil(coreFromCallback) + XCTAssertTrue(isInitialized) } func testEnableAllFeatures() { let core = MockDatadogCore() + CoreRegistry.register(default: core) + defer { CoreRegistry.unregisterDefault() } + let configuration: DdSdkConfiguration = .mockAny() - DatadogSDKWrapper.shared.setCoreInstance(core: core) DdSdkNativeInitialization().enableFeatures( sdkConfiguration: configuration ) @@ -479,9 +479,11 @@ class DdSdkTests: XCTestCase { func testBuildConfigurationWithCrashReport() { let core = MockDatadogCore() + CoreRegistry.register(default: core) + defer { CoreRegistry.unregisterDefault() } + let configuration: DdSdkConfiguration = .mockAny(nativeCrashReportEnabled: true) - DatadogSDKWrapper.shared.setCoreInstance(core: core) DdSdkNativeInitialization().enableFeatures( sdkConfiguration: configuration ) @@ -1233,6 +1235,9 @@ class DdSdkTests: XCTestCase { func testConfigurationTelemetryOverride() throws { let core = MockDatadogCore() + CoreRegistry.register(default: core) + defer { CoreRegistry.unregisterDefault() } + let configuration: DdSdkConfiguration = .mockAny( nativeCrashReportEnabled: false, nativeLongTaskThresholdMs: 0.0, @@ -1244,7 +1249,6 @@ class DdSdkTests: XCTestCase { ] ) - DatadogSDKWrapper.shared.setCoreInstance(core: core) DdSdkImplementation().overrideReactNativeTelemetry(rnConfiguration: configuration) XCTAssertEqual(core.configuration?.initializationType, "LEGACY") @@ -1313,11 +1317,12 @@ class DdSdkTests: XCTestCase { XCTAssertTrue(bridge.isSameQueue(queue: mockJSRefreshRateMonitor.jsQueue!)) } - func testCallsOnCoreInitializedListeners() throws { + func testCallsOnSdkInitializedListeners() throws { let bridge = DispatchQueueMock() let mockJSRefreshRateMonitor = MockJSRefreshRateMonitor() let mockListener = MockOnCoreInitializedListener() - DatadogSDKWrapper.shared.addOnCoreInitializedListener(listener: mockListener.listener) + + DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: mockListener.listener) DdSdkImplementation( mainDispatchQueue: DispatchQueueMock(), @@ -1331,23 +1336,7 @@ class DdSdkTests: XCTestCase { reject: mockReject ) - XCTAssertNotNil(mockListener.core) - } - - func testConsumeWebviewEvent() throws { - let configuration: DdSdkConfiguration = .mockAny() - let core = MockDatadogCore() - - DatadogSDKWrapper.shared.setCoreInstance(core: core) - DdSdkNativeInitialization().enableFeatures( - sdkConfiguration: configuration - ) - - DdSdkImplementation().consumeWebviewEvent( - message: "{\"eventType\":\"rum\",\"event\":{\"blabla\":\"custom message\"}}", - resolve: mockResolve, reject: mockReject) - - XCTAssertNotNil(core.baggages["browser-rum-event"]) + XCTAssertTrue(mockListener.called) } func testInitialResourceThreshold() { @@ -1601,9 +1590,8 @@ extension DdSdkImplementation { } class MockOnCoreInitializedListener { - var core: DatadogCoreProtocol? - - func listener(core: DatadogCoreProtocol) { - self.core = core + var called = false + func listener() { + self.called = true } } diff --git a/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift b/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift index 45cfc2582..0fc9c6637 100644 --- a/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift +++ b/packages/react-native-session-replay/ios/Sources/DdSessionReplayImplementation.swift @@ -6,6 +6,7 @@ import Foundation @_spi(Internal) import DatadogSessionReplay +import DatadogCore import DatadogInternal import DatadogSDKReactNative import React @@ -66,8 +67,6 @@ public class DdSessionReplayImplementation: NSObject { customEndpoint: customEndpointURL ) -// let bundle = Bundle(for: DdSessionReplayImplementation.self) - var svgMap: [String: SVGData] = [:] if let bundle = Bundle.ddSessionReplayResources, @@ -92,38 +91,21 @@ public class DdSessionReplayImplementation: NSObject { fabricWrapper: fabricWrapper ) ]) - - if let core = DatadogSDKWrapper.shared.getCoreInstance() { - sessionReplay.enable( - with: sessionReplayConfiguration, - in: core - ) - } else { - consolePrint("Core instance was not found when initializing Session Replay.", .critical) - } + + sessionReplay.enable(with: sessionReplayConfiguration, in: CoreRegistry.default) resolve(nil) } @objc public func startRecording(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - if let core = DatadogSDKWrapper.shared.getCoreInstance() { - sessionReplay.startRecording(in: core) - } else { - consolePrint("Core instance was not found when calling startRecording in Session Replay.", .critical) - } - + sessionReplay.startRecording(in: CoreRegistry.default) resolve(nil) } @objc public func stopRecording(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) -> Void { - if let core = DatadogSDKWrapper.shared.getCoreInstance() { - sessionReplay.stopRecording(in: core) - } else { - consolePrint("Core instance was not found when calling stopRecording in Session Replay.", .critical) - } - + sessionReplay.stopRecording(in: CoreRegistry.default) resolve(nil) } diff --git a/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift b/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift index aa9102850..067a11df0 100644 --- a/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift +++ b/packages/react-native-session-replay/ios/Tests/DdSessionReplayTests.swift @@ -35,7 +35,11 @@ internal class DdSessionReplayTests: XCTestCase { override func setUp() { super.setUp() let mockDatadogCore = MockDatadogCore() - DatadogSDKWrapper.shared.setCoreInstance(core: mockDatadogCore) + CoreRegistry.register(default: mockDatadogCore) + } + + override func tearDown() { + CoreRegistry.unregisterDefault() } func testEnablesSessionReplayWithZeroReplaySampleRate() { diff --git a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift index 97fd835b5..45f11e452 100644 --- a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift +++ b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift @@ -8,26 +8,27 @@ import WebKit import DatadogWebViewTracking import DatadogSDKReactNative import DatadogCore +import DatadogInternal @objc public class RCTDatadogWebViewTracking: NSObject { var webView: RCTDatadogWebView? = nil var allowedHosts: Set = Set() - var coreListener: OnCoreInitializedListener? + var onSdkInitializedListener: OnSdkInitializedListener? public override init() { super.init() - self.coreListener = { [weak self] (core: DatadogCoreProtocol) in + self.onSdkInitializedListener = { [weak self] in guard let strongSelf = self, let webView = strongSelf.webView else { return } strongSelf.enableWebViewTracking( webView: webView, allowedHosts: strongSelf.allowedHosts, - core: core + core: CoreRegistry.default ) } } - + /** Enables tracking on the given WebView. @@ -42,15 +43,16 @@ import DatadogCore guard !webView.isTrackingEnabled else { return } - if let core = DatadogSDKWrapper.shared.getCoreInstance() { - enableWebViewTracking(webView: webView, allowedHosts: allowedHosts, core: core) - } else if let coreListener = self.coreListener { - DatadogSDKWrapper.shared.addOnCoreInitializedListener(listener: coreListener) + if CoreRegistry.isRegistered(instanceName: CoreRegistry.defaultInstanceName) { + enableWebViewTracking(webView: webView, allowedHosts: allowedHosts, core: CoreRegistry.default) + } else if let onSdkInitializedListener = self.onSdkInitializedListener { + DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: onSdkInitializedListener) } else { // TODO: Report initialization problem } } + private func enableWebViewTracking( webView: RCTDatadogWebView, allowedHosts: Set, diff --git a/packages/react-native-webview/ios/Tests/DatadogSDKReactNativeWebViewTests.swift b/packages/react-native-webview/ios/Tests/DatadogSDKReactNativeWebViewTests.swift index 7ea36bda8..20f1b84fa 100644 --- a/packages/react-native-webview/ios/Tests/DatadogSDKReactNativeWebViewTests.swift +++ b/packages/react-native-webview/ios/Tests/DatadogSDKReactNativeWebViewTests.swift @@ -17,7 +17,11 @@ internal class DatadogSDKReactNativeWebViewTests: XCTestCase { override func setUp() { super.setUp() let mockDatadogCore = MockDatadogCore() - DatadogSDKWrapper.shared.setCoreInstance(core: mockDatadogCore) + CoreRegistry.register(default: mockDatadogCore) + } + + override func tearDown() { + CoreRegistry.unregisterDefault() } func testDatadogWebViewManagerReturnsDatadogWebView() { @@ -41,9 +45,10 @@ internal class DatadogSDKReactNativeWebViewTests: XCTestCase { XCTAssertFalse(view.isTrackingEnabled) } - func testDatadogWebViewTrackingIsDisabledIfCoreIsNotReady() { + func testDatadogWebViewTrackingIsDisabledIfSdkIsNotInitialized() { // Given - DatadogSDKWrapper.shared.setCoreInstance(core: nil) + CoreRegistry.unregisterDefault() + let viewManager = RCTDatadogWebViewManager() let allowedHosts = NSArray(objects: "example1.com", "example2.com") @@ -82,7 +87,7 @@ internal class DatadogSDKReactNativeWebViewTests: XCTestCase { view.addSubview(WKWebView()) - DatadogSDKWrapper.shared.setCoreInstance(core: nil) + CoreRegistry.unregisterDefault() // Given let selector = NSSelectorFromString("setupDatadogWebView:view:") @@ -92,7 +97,6 @@ internal class DatadogSDKReactNativeWebViewTests: XCTestCase { XCTAssertFalse(view.isTrackingEnabled) // When - DatadogSDKWrapper.shared.setCoreInstance(core: MockDatadogCore()) DatadogSDKWrapper.shared.callInitialize() let expectation = self.expectation(description: "WebView tracking is enabled through the listener.") From 7a79687264f9029898696cb74eb207016153fabd Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 8 Sep 2025 15:27:17 +0200 Subject: [PATCH 020/168] Bump native SDK dependencies to 3.0.0 --- benchmarks/android/app/build.gradle | 2 +- benchmarks/ios/Podfile.lock | 74 +++++++------- bump-native-dd-sdk.sh | 2 +- example-new-architecture/ios/Podfile.lock | 70 +++++++------- example/ios/Podfile.lock | 96 +++++++++---------- packages/core/DatadogSDKReactNative.podspec | 12 +-- packages/core/android/build.gradle | 10 +- ...DatadogSDKReactNativeSessionReplay.podspec | 2 +- .../android/build.gradle | 4 +- .../DatadogSDKReactNativeWebView.podspec | 4 +- .../react-native-webview/android/build.gradle | 2 +- 11 files changed, 139 insertions(+), 139 deletions(-) diff --git a/benchmarks/android/app/build.gradle b/benchmarks/android/app/build.gradle index 86d26791f..7dc4b3707 100644 --- a/benchmarks/android/app/build.gradle +++ b/benchmarks/android/app/build.gradle @@ -129,5 +129,5 @@ dependencies { // Benchmark tools from dd-sdk-android are used for vitals recording // Remember to bump thid alongside the main dd-sdk-android dependencies - implementation("com.datadoghq:dd-sdk-android-benchmark-internal:2.25.0") + implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.0.0") } diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index 771bfab42..7e2fb2829 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogCrashReporting (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogCore (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogCrashReporting (3.0.0): + - DatadogInternal (= 3.0.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (2.30.2) - - DatadogLogs (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogRUM (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogSDKReactNative (2.14.1): - - DatadogCore (= 2.30.2) - - DatadogCrashReporting (= 2.30.2) - - DatadogLogs (= 2.30.2) - - DatadogRUM (= 2.30.2) - - DatadogTrace (= 2.30.2) - - DatadogWebViewTracking (= 2.30.2) + - DatadogInternal (3.0.0) + - DatadogLogs (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogRUM (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogSDKReactNative (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -39,7 +39,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 2.30.2) + - DatadogSessionReplay (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -60,10 +60,10 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.14.1): - - DatadogInternal (= 2.30.2) + - DatadogSDKReactNativeWebView (2.12.1): + - DatadogInternal (= 3.0.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 2.30.2) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -84,13 +84,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSessionReplay (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogTrace (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogSessionReplay (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogTrace (3.0.0): + - DatadogInternal (= 3.0.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogWebViewTracking (3.0.0): + - DatadogInternal (= 3.0.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.78.2) @@ -2070,17 +2070,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DatadogCore: 8e50ad6cb68343f701707f7eeca16e0acb52ed8c - DatadogCrashReporting: 34763d4276d4fce286c7e8729cfcd2ac04dca43b - DatadogInternal: bd8672d506a7e67936ed5f7ca612169e49029b44 - DatadogLogs: d683aa9e0c9339f5ae679ead70bbdbe41cdc32f6 - DatadogRUM: 8b794aa458e6323ea9b1cef3f820fd3d092cbe27 - DatadogSDKReactNative: 14c7f36115fdd37841dd620f3376848204596909 - DatadogSDKReactNativeSessionReplay: 27bcc3d5280a85fb2a244b84916c1d01a43668e8 - DatadogSDKReactNativeWebView: 1849aa7769bde0a2a2088705e596276631769656 - DatadogSessionReplay: 56a91d799fe34967c5ae79a222364e37d67020f5 - DatadogTrace: 3ba194791267efa09634234749111cac95abd3e5 - DatadogWebViewTracking: 8287d5ad06e992de5e46dd72a17e05c7513344be + DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 + DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e + DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 + DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd + DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f + DatadogSDKReactNative: 241bf982c16ceff03d94a58e6d005e55c47b99c6 + DatadogSDKReactNativeSessionReplay: bba36092686e3183e97c1a0c7f4ca8142582ae28 + DatadogSDKReactNativeWebView: 6da060df20e235abac533e582d9fc2b3a5070840 + DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 + DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 + DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: e32d34492c519a2194ec9d7f5e7a79d11b73f91c diff --git a/bump-native-dd-sdk.sh b/bump-native-dd-sdk.sh index 8545802db..67d99a0e7 100755 --- a/bump-native-dd-sdk.sh +++ b/bump-native-dd-sdk.sh @@ -23,7 +23,7 @@ podspec_files=( "packages/react-native-webview/DatadogSDKReactNativeWebView.podspec" ) -ios_pattern="('Datadog[^']+', '~> )[0-9.]+'" +ios_pattern="('Datadog[^']+', ')[0-9.]+'" android_pattern='(com\.datadoghq:dd-sdk-android-[^:"]+):[0-9.]+' if [[ "$sdk" == "ios" ]]; then diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index ca3b81b1d..cc9913ce3 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogCrashReporting (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogCore (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogCrashReporting (3.0.0): + - DatadogInternal (= 3.0.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (2.30.2) - - DatadogLogs (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogRUM (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogSDKReactNative (2.14.1): - - DatadogCore (= 2.30.2) - - DatadogCrashReporting (= 2.30.2) - - DatadogLogs (= 2.30.2) - - DatadogRUM (= 2.30.2) - - DatadogTrace (= 2.30.2) - - DatadogWebViewTracking (= 2.30.2) + - DatadogInternal (3.0.0) + - DatadogLogs (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogRUM (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogSDKReactNative (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -37,13 +37,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNative/Tests (2.14.1): - - DatadogCore (= 2.30.2) - - DatadogCrashReporting (= 2.30.2) - - DatadogLogs (= 2.30.2) - - DatadogRUM (= 2.30.2) - - DatadogTrace (= 2.30.2) - - DatadogWebViewTracking (= 2.30.2) + - DatadogSDKReactNative/Tests (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -64,11 +64,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogTrace (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogTrace (3.0.0): + - DatadogInternal (= 3.0.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogWebViewTracking (3.0.0): + - DatadogInternal (= 3.0.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1850,14 +1850,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 8e50ad6cb68343f701707f7eeca16e0acb52ed8c - DatadogCrashReporting: 34763d4276d4fce286c7e8729cfcd2ac04dca43b - DatadogInternal: bd8672d506a7e67936ed5f7ca612169e49029b44 - DatadogLogs: d683aa9e0c9339f5ae679ead70bbdbe41cdc32f6 - DatadogRUM: 8b794aa458e6323ea9b1cef3f820fd3d092cbe27 - DatadogSDKReactNative: 3092efeaf685e37afe770740ef058ee3f9f5bf5d - DatadogTrace: 3ba194791267efa09634234749111cac95abd3e5 - DatadogWebViewTracking: 8287d5ad06e992de5e46dd72a17e05c7513344be + DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 + DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e + DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 + DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd + DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f + DatadogSDKReactNative: 0a80aa75958d595a99be54d2838db53eda7a08af + DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 + DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 0deeb0a40..acf9029a0 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,34 +1,34 @@ PODS: - boost (1.84.0) - - DatadogCore (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogCrashReporting (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogCore (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogCrashReporting (3.0.0): + - DatadogInternal (= 3.0.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (2.30.2) - - DatadogLogs (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogRUM (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogSDKReactNative (2.14.1): - - DatadogCore (= 2.30.2) - - DatadogCrashReporting (= 2.30.2) - - DatadogLogs (= 2.30.2) - - DatadogRUM (= 2.30.2) - - DatadogTrace (= 2.30.2) - - DatadogWebViewTracking (= 2.30.2) + - DatadogInternal (3.0.0) + - DatadogLogs (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogRUM (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogSDKReactNative (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - React-Core - - DatadogSDKReactNative/Tests (2.14.1): - - DatadogCore (= 2.30.2) - - DatadogCrashReporting (= 2.30.2) - - DatadogLogs (= 2.30.2) - - DatadogRUM (= 2.30.2) - - DatadogTrace (= 2.30.2) - - DatadogWebViewTracking (= 2.30.2) + - DatadogSDKReactNative/Tests (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - React-Core - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 2.30.2) + - DatadogSessionReplay (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -51,7 +51,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay/Tests (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 2.30.2) + - DatadogSessionReplay (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -73,25 +73,25 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.14.1): - - DatadogInternal (= 2.30.2) + - DatadogSDKReactNativeWebView (2.12.1): + - DatadogInternal (= 3.0.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 2.30.2) + - DatadogWebViewTracking (= 3.0.0) - React-Core - - DatadogSDKReactNativeWebView/Tests (2.14.1): - - DatadogInternal (= 2.30.2) + - DatadogSDKReactNativeWebView/Tests (2.12.1): + - DatadogInternal (= 3.0.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 2.30.2) + - DatadogWebViewTracking (= 3.0.0) - React-Core - react-native-webview - React-RCTText - - DatadogSessionReplay (2.30.2): - - DatadogInternal (= 2.30.2) - - DatadogTrace (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogSessionReplay (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogTrace (3.0.0): + - DatadogInternal (= 3.0.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (2.30.2): - - DatadogInternal (= 2.30.2) + - DatadogWebViewTracking (3.0.0): + - DatadogInternal (= 3.0.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1988,17 +1988,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 8e50ad6cb68343f701707f7eeca16e0acb52ed8c - DatadogCrashReporting: 34763d4276d4fce286c7e8729cfcd2ac04dca43b - DatadogInternal: bd8672d506a7e67936ed5f7ca612169e49029b44 - DatadogLogs: d683aa9e0c9339f5ae679ead70bbdbe41cdc32f6 - DatadogRUM: 8b794aa458e6323ea9b1cef3f820fd3d092cbe27 - DatadogSDKReactNative: 676327b41d63d42172cc3543dbed83a9e5f0e32c - DatadogSDKReactNativeSessionReplay: 65bb7f28d508739d76c939d1e8242100c0d42204 - DatadogSDKReactNativeWebView: dfddb218204ae6eec3c092fd20ed3c9d268b8683 - DatadogSessionReplay: 56a91d799fe34967c5ae79a222364e37d67020f5 - DatadogTrace: 3ba194791267efa09634234749111cac95abd3e5 - DatadogWebViewTracking: 8287d5ad06e992de5e46dd72a17e05c7513344be + DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 + DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e + DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 + DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd + DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f + DatadogSDKReactNative: e430b3f4d7adb0fac07b61e2fb65ceacdaf82b3e + DatadogSDKReactNativeSessionReplay: 4b2a3d166a79581f18522795b40141c34cf3685d + DatadogSDKReactNativeWebView: 35dc2b9736e1aaa82b366bf6b8a8a959a9b088c5 + DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 + DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 + DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/packages/core/DatadogSDKReactNative.podspec b/packages/core/DatadogSDKReactNative.podspec index ff3b91232..a2f41ddd1 100644 --- a/packages/core/DatadogSDKReactNative.podspec +++ b/packages/core/DatadogSDKReactNative.podspec @@ -19,14 +19,14 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the versions in sync with DatadogSDKReactNativeSessionReplay.podspec - s.dependency 'DatadogCore', '2.30.2' - s.dependency 'DatadogLogs', '2.30.2' - s.dependency 'DatadogTrace', '2.30.2' - s.dependency 'DatadogRUM', '2.30.2' - s.dependency 'DatadogCrashReporting', '2.30.2' + s.dependency 'DatadogCore', '3.0.0' + s.dependency 'DatadogLogs', '3.0.0' + s.dependency 'DatadogTrace', '3.0.0' + s.dependency 'DatadogRUM', '3.0.0' + s.dependency 'DatadogCrashReporting', '3.0.0' # DatadogWebViewTracking is not available for tvOS - s.ios.dependency 'DatadogWebViewTracking', '2.30.2' + s.ios.dependency 'DatadogWebViewTracking', '3.0.0' s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'ios/Tests/**/*.{swift,json}' diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index 0396ae323..a8d09d4d1 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -201,16 +201,16 @@ dependencies { // This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0. // To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0 if (reactNativeMinorVersion < 76) { - implementation("com.datadoghq:dd-sdk-android-rum:2.26.2") { + implementation("com.datadoghq:dd-sdk-android-rum:3.0.0") { exclude group: "androidx.metrics", module: "metrics-performance" } implementation "androidx.metrics:metrics-performance:1.0.0-beta01" } else { - implementation "com.datadoghq:dd-sdk-android-rum:2.26.2" + implementation "com.datadoghq:dd-sdk-android-rum:3.0.0" } - implementation "com.datadoghq:dd-sdk-android-logs:2.26.2" - implementation "com.datadoghq:dd-sdk-android-trace:2.26.2" - implementation "com.datadoghq:dd-sdk-android-webview:2.26.2" + implementation "com.datadoghq:dd-sdk-android-logs:3.0.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.0.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" implementation "com.google.code.gson:gson:2.10.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec index 915fc4129..3a2d5ff48 100644 --- a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec +++ b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogSessionReplay', '2.30.2' + s.dependency 'DatadogSessionReplay', '3.0.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-session-replay/android/build.gradle b/packages/react-native-session-replay/android/build.gradle index 5851ee598..6d6fe20b1 100644 --- a/packages/react-native-session-replay/android/build.gradle +++ b/packages/react-native-session-replay/android/build.gradle @@ -216,8 +216,8 @@ dependencies { api "com.facebook.react:react-android:$reactNativeVersion" } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.datadoghq:dd-sdk-android-session-replay:2.26.2" - implementation "com.datadoghq:dd-sdk-android-internal:2.26.2" + implementation "com.datadoghq:dd-sdk-android-session-replay:3.0.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.0.0" implementation project(path: ':datadog_mobile-react-native') testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec index 3d4668bb0..26e160bbc 100644 --- a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec +++ b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec @@ -23,8 +23,8 @@ Pod::Spec.new do |s| end # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogWebViewTracking', '2.30.2' - s.dependency 'DatadogInternal', '2.30.2' + s.dependency 'DatadogWebViewTracking', '3.0.0' + s.dependency 'DatadogInternal', '3.0.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-webview/android/build.gradle b/packages/react-native-webview/android/build.gradle index a0c407316..8eeaea88e 100644 --- a/packages/react-native-webview/android/build.gradle +++ b/packages/react-native-webview/android/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.facebook.react:react-android:$reactNativeVersion" } - implementation "com.datadoghq:dd-sdk-android-webview:2.26.2" + implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native') From 5aa0b4de446f2929ec7cb16a65929250f88dfd15 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 12 Sep 2025 14:43:25 +0200 Subject: [PATCH 021/168] Remove setUser --- packages/codepush/__mocks__/react-native.ts | 3 - packages/core/__mocks__/react-native.ts | 3 - .../datadog/reactnative/DatadogSDKWrapper.kt | 10 -- .../com/datadog/reactnative/DatadogWrapper.kt | 17 -- .../reactnative/DdSdkImplementation.kt | 17 +- .../kotlin/com/datadog/reactnative/DdSdk.kt | 14 +- .../kotlin/com/datadog/reactnative/DdSdk.kt | 11 -- .../com/datadog/reactnative/DdSdkTest.kt | 158 ------------------ packages/core/ios/Sources/DdSdk.mm | 11 -- .../ios/Sources/DdSdkImplementation.swift | 15 +- packages/core/ios/Tests/DdSdkTests.swift | 132 --------------- packages/core/ios/Tests/MockRUMMonitor.swift | 16 ++ packages/core/jest/mock.js | 3 - packages/core/src/DdSdkReactNative.tsx | 25 +-- .../src/__tests__/DdSdkReactNative.test.tsx | 16 -- packages/core/src/logs/eventMapper.ts | 6 +- .../core/src/sdk/EventMappers/EventMapper.ts | 2 +- .../UserInfoSingleton/UserInfoSingleton.ts | 4 +- .../__tests__/UserInfoSingleton.test.ts | 6 +- .../core/src/sdk/UserInfoSingleton/types.ts | 5 +- packages/core/src/specs/NativeDdSdk.ts | 7 - packages/core/src/types.tsx | 10 +- .../__mocks__/react-native.ts | 3 - 23 files changed, 42 insertions(+), 452 deletions(-) diff --git a/packages/codepush/__mocks__/react-native.ts b/packages/codepush/__mocks__/react-native.ts index b35df4e31..046ced2f6 100644 --- a/packages/codepush/__mocks__/react-native.ts +++ b/packages/codepush/__mocks__/react-native.ts @@ -18,9 +18,6 @@ actualRN.NativeModules.DdSdk = { initialize: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, - setUser: jest.fn().mockImplementation( - () => new Promise(resolve => resolve()) - ) as jest.MockedFunction, setAttributes: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, diff --git a/packages/core/__mocks__/react-native.ts b/packages/core/__mocks__/react-native.ts index 9507ec33f..260fe68a7 100644 --- a/packages/core/__mocks__/react-native.ts +++ b/packages/core/__mocks__/react-native.ts @@ -18,9 +18,6 @@ actualRN.NativeModules.DdSdk = { initialize: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, - setUser: jest.fn().mockImplementation( - () => new Promise(resolve => resolve()) - ) as jest.MockedFunction, setUserInfo: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index da0841ac4..198061d14 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -71,16 +71,6 @@ internal class DatadogSDKWrapper : DatadogWrapper { DatadogSDKWrapperStorage.notifyOnInitializedListeners(core as InternalSdkCore) } - @Deprecated("Use setUserInfo instead; the user ID is now required.") - override fun setUser( - id: String?, - name: String?, - email: String?, - extraInfo: Map - ) { - Datadog.setUserInfo(id, name, email, extraInfo) - } - override fun setUserInfo( id: String, name: String?, diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index 19b25e587..3ae3e6266 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -62,23 +62,6 @@ interface DatadogWrapper { consent: TrackingConsent ) - /** - * Sets the user information. - * - * @param id (nullable) a unique user identifier (relevant to your business domain) - * @param name (nullable) the user name or alias - * @param email (nullable) the user email - * @param extraInfo additional information. An extra information can be - * nested up to 8 levels deep. Keys using more than 8 levels will be sanitized by SDK. - */ - @Deprecated("Use setUserInfo instead; the user ID is now required.") - fun setUser( - id: String?, - name: String?, - email: String?, - extraInfo: Map - ) - /** * Sets the user information. * diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index b04a2ddf3..7a5c6848c 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -78,21 +78,6 @@ class DdSdkImplementation( promise.resolve(null) } - /** - * Set the user information. - * @param user The user object (use builtin attributes: 'id', 'email', 'name', and/or any custom - * attribute). - */ - @Deprecated("Use setUserInfo instead; the user ID is now required.") - fun setUser(user: ReadableMap, promise: Promise) { - val extraInfo = user.toHashMap().toMutableMap() - val id = extraInfo.remove("id")?.toString() - val name = extraInfo.remove("name")?.toString() - val email = extraInfo.remove("email")?.toString() - datadog.setUser(id, name, email, extraInfo) - promise.resolve(null) - } - /** * Set the user information. * @param userInfo The user object (use builtin attributes: 'id', 'email', 'name', and any custom @@ -110,7 +95,7 @@ class DdSdkImplementation( if (id != null) { datadog.setUserInfo(id, name, email, extraInfo) } else { - datadog.setUser(null, name, email, extraInfo) + // TO DO - Log warning? } promise.resolve(null) diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt index 5bc470947..cfafffffe 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -51,19 +51,7 @@ class DdSdk( /** * Set the user information. - * @param user The user object (use builtin attributes: 'id', 'email', 'name', and/or any custom - * attribute). - */ - @Deprecated("Use setUserInfo instead; the user ID is now required.") - @ReactMethod - override fun setUser(user: ReadableMap, promise: Promise) { - implementation.setUser(user, promise) - } - - /** - * Set the user information. - * @param user The user object (use builtin attributes: 'id', 'email', 'name', and any custom - * attribute inside 'extraInfo'). + * @param user The user object (use builtin attributes: 'id', 'email', 'name', and any custom * attribute inside 'extraInfo'). */ @ReactMethod override fun setUserInfo(user: ReadableMap, promise: Promise) { diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 17acd6d20..97acb2ebf 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -75,17 +75,6 @@ class DdSdk( implementation.setAttributes(attributes, promise) } - /** - * Set the user information. - * @param user The user object (use builtin attributes: 'id', 'email', 'name', and/or any custom - * attribute). - */ - @Deprecated("Use setUserInfo instead; the user ID is now required.") - @ReactMethod - fun setUser(user: ReadableMap, promise: Promise) { - implementation.setUser(user, promise) - } - /** * Set the user information. * @param user The user object (use builtin attributes: 'id', 'email', 'name', and any custom * attribute inside 'extraInfo'). diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index a39485dae..8797bcc64 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -2810,164 +2810,6 @@ internal class DdSdkTest { // region misc - @Test - fun `𝕄 set native user info 𝕎 setUser()`( - @MapForgery( - key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), - value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) - ) extraInfo: Map - ) { - // When - testedBridgeSdk.setUser(extraInfo.toReadableMap(), mockPromise) - - // Then - argumentCaptor> { - verify(mockDatadog) - .setUser( - isNull(), - isNull(), - isNull(), - capture() - ) - - assertThat(firstValue) - .containsAllEntriesOf(extraInfo) - .hasSize(extraInfo.size) - } - } - - @Test - fun `𝕄 set native user info 𝕎 setUser() {with id}`( - @StringForgery id: String, - @MapForgery( - key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), - value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) - ) extraInfo: Map - ) { - // Given - val user = extraInfo.toMutableMap().also { - it.put("id", id) - } - - // When - testedBridgeSdk.setUser(user.toReadableMap(), mockPromise) - - // Then - argumentCaptor> { - verify(mockDatadog) - .setUser( - eq(id), - isNull(), - isNull(), - capture() - ) - - assertThat(firstValue) - .containsAllEntriesOf(extraInfo) - .hasSize(extraInfo.size) - } - } - - @Test - fun `𝕄 set native user info 𝕎 setUser() {with name}`( - @StringForgery name: String, - @MapForgery( - key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), - value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) - ) extraInfo: Map - ) { - // Given - val user = extraInfo.toMutableMap().also { - it.put("name", name) - } - - // When - testedBridgeSdk.setUser(user.toReadableMap(), mockPromise) - - // Then - argumentCaptor> { - verify(mockDatadog) - .setUser( - isNull(), - eq(name), - isNull(), - capture() - ) - - assertThat(firstValue) - .containsAllEntriesOf(extraInfo) - .hasSize(extraInfo.size) - } - } - - @Test - fun `𝕄 set native user info 𝕎 setUser() {with email}`( - @StringForgery(regex = "\\w+@\\w+\\.[a-z]{3}") email: String, - @MapForgery( - key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), - value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) - ) extraInfo: Map - ) { - // Given - val user = extraInfo.toMutableMap().also { - it.put("email", email) - } - - // When - testedBridgeSdk.setUser(user.toReadableMap(), mockPromise) - - // Then - argumentCaptor> { - verify(mockDatadog) - .setUser( - isNull(), - isNull(), - eq(email), - capture() - ) - - assertThat(firstValue) - .containsAllEntriesOf(extraInfo) - .hasSize(extraInfo.size) - } - } - - @Test - fun `𝕄 set native user info 𝕎 setUser() {with id, name and email}`( - @StringForgery id: String, - @StringForgery name: String, - @StringForgery(regex = "\\w+@\\w+\\.[a-z]{3}") email: String, - @MapForgery( - key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), - value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) - ) extraInfo: Map - ) { - // Given - val user = extraInfo.toMutableMap().also { - it.put("id", id) - it.put("name", name) - it.put("email", email) - } - - // When - testedBridgeSdk.setUser(user.toReadableMap(), mockPromise) - - // Then - argumentCaptor> { - verify(mockDatadog) - .setUser( - eq(id), - eq(name), - eq(email), - capture() - ) - - assertThat(firstValue) - .containsAllEntriesOf(extraInfo) - .hasSize(extraInfo.size) - } - } - @Test fun `𝕄 set native user info 𝕎 setUserInfo() {with id}`( @StringForgery id: String diff --git a/packages/core/ios/Sources/DdSdk.mm b/packages/core/ios/Sources/DdSdk.mm index 590452c86..918a8db03 100644 --- a/packages/core/ios/Sources/DdSdk.mm +++ b/packages/core/ios/Sources/DdSdk.mm @@ -37,13 +37,6 @@ + (void)initFromNative { [self setAttributes:attributes resolve:resolve reject:reject]; } -RCT_REMAP_METHOD(setUser, withUser:(NSDictionary*)user - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - [self setUser:user resolve:resolve reject:reject]; -} - RCT_REMAP_METHOD(setUserInfo, withUserInfo:(NSDictionary*)userInfo withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) @@ -146,10 +139,6 @@ - (void)setTrackingConsent:(NSString *)trackingConsent resolve:(RCTPromiseResolv [self.ddSdkImplementation setTrackingConsentWithTrackingConsent:trackingConsent resolve:resolve reject:reject]; } -- (void)setUser:(NSDictionary *)user resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - [self.ddSdkImplementation setUserWithUser:user resolve:resolve reject:reject]; -} - - (void)setUserInfo:(NSDictionary *)userInfo resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self.ddSdkImplementation setUserInfoWithUserInfo:userInfo resolve:resolve reject:reject]; } diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 973b8db9f..b2e610635 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -87,18 +87,6 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - @objc - public func setUser(user: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - var castedUser = castAttributesToSwift(user) - let id = castedUser.removeValue(forKey: "id") as? String - let name = castedUser.removeValue(forKey: "name") as? String - let email = castedUser.removeValue(forKey: "email") as? String - let extraInfo: [String: Encodable] = castedUser // everything what's left is an `extraInfo` - - Datadog.setUserInfo(id: id, name: name, email: email, extraInfo: extraInfo) - resolve(nil) - } - @objc public func setUserInfo(userInfo: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { let castedUserInfo = castAttributesToSwift(userInfo) @@ -115,8 +103,9 @@ public class DdSdkImplementation: NSObject { if let validId = id { Datadog.setUserInfo(id: validId, name: name, email: email, extraInfo: extraInfo) } else { - Datadog.setUserInfo(name: name, email: email, extraInfo: extraInfo) + // TO DO - log warning message? } + resolve(nil) } diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index bcc3252a8..6be5e0994 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -535,85 +535,6 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(ddConfig.trackFrustrations, false) } - func testSetUser() throws { - let bridge = DdSdkImplementation( - mainDispatchQueue: DispatchQueueMock(), - jsDispatchQueue: DispatchQueueMock(), - jsRefreshRateMonitor: JSRefreshRateMonitor(), - RUMMonitorProvider: { MockRUMMonitor() }, - RUMMonitorInternalProvider: { nil } - ) - bridge.initialize( - configuration: .mockAny(), - resolve: mockResolve, - reject: mockReject - ) - - bridge.setUser( - user: NSDictionary( - dictionary: [ - "id": "id_123", - "name": "John Doe", - "email": "john@doe.com", - "extra-info-1": 123, - "extra-info-2": "abc", - "extra-info-3": true, - ] - ), - resolve: mockResolve, - reject: mockReject - ) - - let ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - let userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, "id_123") - XCTAssertEqual(userInfo.name, "John Doe") - XCTAssertEqual(userInfo.email, "john@doe.com") - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) - } - - func testSetUserOptionalId() throws { - let bridge = DdSdkImplementation( - mainDispatchQueue: DispatchQueueMock(), - jsDispatchQueue: DispatchQueueMock(), - jsRefreshRateMonitor: JSRefreshRateMonitor(), - RUMMonitorProvider: { MockRUMMonitor() }, - RUMMonitorInternalProvider: { nil } - ) - bridge.initialize( - configuration: .mockAny(), - resolve: mockResolve, - reject: mockReject - ) - - bridge.setUser( - user: NSDictionary( - dictionary: [ - "name": "John Doe", - "email": "john@doe.com", - "extra-info-1": 123, - "extra-info-2": "abc", - "extra-info-3": true, - ] - ), - resolve: mockResolve, - reject: mockReject - ) - - let ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - let userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, nil) - XCTAssertEqual(userInfo.name, "John Doe") - XCTAssertEqual(userInfo.email, "john@doe.com") - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) - } - func testSetUserInfo() throws { let bridge = DdSdkImplementation( mainDispatchQueue: DispatchQueueMock(), @@ -668,59 +589,6 @@ class DdSdkTests: XCTestCase { } } - func testSetUserInfoOptionalId() throws { - let bridge = DdSdkImplementation( - mainDispatchQueue: DispatchQueueMock(), - jsDispatchQueue: DispatchQueueMock(), - jsRefreshRateMonitor: JSRefreshRateMonitor(), - RUMMonitorProvider: { MockRUMMonitor() }, - RUMMonitorInternalProvider: { nil } - ) - bridge.initialize( - configuration: .mockAny(), - resolve: mockResolve, - reject: mockReject - ) - - bridge.setUserInfo( - userInfo: NSDictionary( - dictionary: [ - "name": "John Doe", - "email": "john@doe.com", - "extraInfo": [ - "extra-info-1": 123, - "extra-info-2": "abc", - "extra-info-3": true, - "extra-info-4": [ - "nested-extra-info-1": 456 - ], - ], - ] - ), - resolve: mockResolve, - reject: mockReject - ) - - let ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - let userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, nil) - XCTAssertEqual(userInfo.name, "John Doe") - XCTAssertEqual(userInfo.email, "john@doe.com") - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) - - if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] - as? DatadogSDKReactNative.AnyEncodable, - let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] - { - XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) - } else { - XCTFail("extra-info-4 is not of expected type or value") - } - } - func testAddUserExtraInfo() throws { let bridge = DdSdkImplementation( mainDispatchQueue: DispatchQueueMock(), diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index fe17e7748..f0fa03364 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,6 +10,22 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { + func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { + // not implemented + } + + func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { + // not implemented + } + + func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { + // not implemented + } + + func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { + // not implemented + } + func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } diff --git a/packages/core/jest/mock.js b/packages/core/jest/mock.js index 9c08f5335..a8161295a 100644 --- a/packages/core/jest/mock.js +++ b/packages/core/jest/mock.js @@ -27,9 +27,6 @@ module.exports = { .fn() .mockImplementation(() => new Promise(resolve => resolve())), isInitialized: jest.fn().mockImplementation(() => true), - setUser: jest - .fn() - .mockImplementation(() => new Promise(resolve => resolve())), setUserInfo: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index aa7e2ec36..668ae09f3 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -38,7 +38,6 @@ import { DdSdk } from './sdk/DdSdk'; import { FileBasedConfiguration } from './sdk/FileBasedConfiguration/FileBasedConfiguration'; import { GlobalState } from './sdk/GlobalState/GlobalState'; import { UserInfoSingleton } from './sdk/UserInfoSingleton/UserInfoSingleton'; -import type { UserInfo } from './sdk/UserInfoSingleton/types'; import { DdSdkConfiguration } from './types'; import { adaptLongTaskThreshold } from './utils/longTasksUtils'; import { version as sdkVersion } from './version'; @@ -192,22 +191,6 @@ export class DdSdkReactNative { AttributesSingleton.getInstance().setAttributes(attributes); }; - /** - * Set the user information. - * @deprecated UserInfo id property is now mandatory (please user setUserInfo instead) - * @param user: The user object (use builtin attributes: 'id', 'email', 'name', and/or any custom attribute). - * @returns a Promise. - */ - // eslint-disable-next-line @typescript-eslint/ban-types - static setUser = async (user: UserInfo): Promise => { - InternalLog.log( - `Setting user ${JSON.stringify(user)}`, - SdkVerbosity.DEBUG - ); - await DdSdk.setUser(user); - UserInfoSingleton.getInstance().setUserInfo(user); - }; - /** * Sets the user information. * @param id: A mandatory unique user identifier (relevant to your business domain). @@ -245,6 +228,14 @@ export class DdSdkReactNative { ); const userInfo = UserInfoSingleton.getInstance().getUserInfo(); + if (!userInfo) { + InternalLog.log( + 'Skipped adding User Extra Info: User Info is currently undefined. A user ID must be set before adding extra info. Please call setUserInfo() first.', + SdkVerbosity.WARN + ); + + return; + } const updatedUserInfo = { ...userInfo, extraInfo: { diff --git a/packages/core/src/__tests__/DdSdkReactNative.test.tsx b/packages/core/src/__tests__/DdSdkReactNative.test.tsx index 18bf060ce..f9405aa51 100644 --- a/packages/core/src/__tests__/DdSdkReactNative.test.tsx +++ b/packages/core/src/__tests__/DdSdkReactNative.test.tsx @@ -63,7 +63,6 @@ beforeEach(async () => { DdSdkReactNative['wasAutoInstrumented'] = false; NativeModules.DdSdk.initialize.mockClear(); NativeModules.DdSdk.setAttributes.mockClear(); - NativeModules.DdSdk.setUser.mockClear(); NativeModules.DdSdk.setTrackingConsent.mockClear(); NativeModules.DdSdk.onRUMSessionStarted.mockClear(); @@ -1064,21 +1063,6 @@ describe('DdSdkReactNative', () => { }); }); - describe('setUser', () => { - it('calls SDK method when setUser, and sets the user in UserProvider', async () => { - // GIVEN - const user = { id: 'id', foo: 'bar' }; - - // WHEN - await DdSdkReactNative.setUser(user); - - // THEN - expect(DdSdk.setUser).toHaveBeenCalledTimes(1); - expect(DdSdk.setUser).toHaveBeenCalledWith(user); - expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual(user); - }); - }); - describe('setUserInfo', () => { it('calls SDK method when setUserInfo, and sets the user in UserProvider', async () => { // GIVEN diff --git a/packages/core/src/logs/eventMapper.ts b/packages/core/src/logs/eventMapper.ts index eb7b5f22c..939e882a5 100644 --- a/packages/core/src/logs/eventMapper.ts +++ b/packages/core/src/logs/eventMapper.ts @@ -31,13 +31,15 @@ export const formatRawLogToNativeEvent = ( export const formatRawLogToLogEvent = ( rawLog: RawLog | RawLogWithError, additionalInformation: { - userInfo: UserInfo; + userInfo?: UserInfo; attributes: Attributes; } ): LogEvent => { + const userInfo = additionalInformation?.userInfo; + return { ...rawLog, - userInfo: additionalInformation.userInfo, + ...(userInfo !== undefined ? { userInfo } : {}), attributes: additionalInformation.attributes }; }; diff --git a/packages/core/src/sdk/EventMappers/EventMapper.ts b/packages/core/src/sdk/EventMappers/EventMapper.ts index beafcf420..9ca252d72 100644 --- a/packages/core/src/sdk/EventMappers/EventMapper.ts +++ b/packages/core/src/sdk/EventMappers/EventMapper.ts @@ -15,7 +15,7 @@ import type { UserInfo } from '../UserInfoSingleton/types'; import { deepClone } from './utils/deepClone'; export type AdditionalEventDataForMapper = { - userInfo: UserInfo; + userInfo?: UserInfo; attributes: Attributes; }; diff --git a/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts b/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts index c3862aabc..26392d794 100644 --- a/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts +++ b/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts @@ -7,13 +7,13 @@ import type { UserInfo } from './types'; class UserInfoProvider { - private userInfo: UserInfo = {}; + private userInfo: UserInfo | undefined = undefined; setUserInfo = (userInfo: UserInfo) => { this.userInfo = userInfo; }; - getUserInfo = (): UserInfo => { + getUserInfo = (): UserInfo | undefined => { return this.userInfo; }; } diff --git a/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts b/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts index 78a722c01..1f7ae84e7 100644 --- a/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts +++ b/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts @@ -9,6 +9,7 @@ import { UserInfoSingleton } from '../UserInfoSingleton'; describe('UserInfoSingleton', () => { it('sets, returns and resets the user info', () => { UserInfoSingleton.getInstance().setUserInfo({ + id: 'test', email: 'user@mail.com', extraInfo: { loggedIn: true @@ -16,6 +17,7 @@ describe('UserInfoSingleton', () => { }); expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual({ + id: 'test', email: 'user@mail.com', extraInfo: { loggedIn: true @@ -24,6 +26,8 @@ describe('UserInfoSingleton', () => { UserInfoSingleton.reset(); - expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual({}); + expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual( + undefined + ); }); }); diff --git a/packages/core/src/sdk/UserInfoSingleton/types.ts b/packages/core/src/sdk/UserInfoSingleton/types.ts index 97a03ae7f..dd14eb150 100644 --- a/packages/core/src/sdk/UserInfoSingleton/types.ts +++ b/packages/core/src/sdk/UserInfoSingleton/types.ts @@ -5,11 +5,8 @@ */ export type UserInfo = { - readonly id?: string /** @deprecated To be made mandatory when removing DdSdkReactnative.setUser */; + readonly id: string; readonly name?: string; readonly email?: string; readonly extraInfo?: Record; - readonly [ - key: string - ]: unknown /** @deprecated To be removed alongside DdSdkReactnative.setUser */; }; diff --git a/packages/core/src/specs/NativeDdSdk.ts b/packages/core/src/specs/NativeDdSdk.ts index 6f1ce82a5..bbf2572ee 100644 --- a/packages/core/src/specs/NativeDdSdk.ts +++ b/packages/core/src/specs/NativeDdSdk.ts @@ -31,13 +31,6 @@ export interface Spec extends TurboModule { */ setAttributes(attributes: Object): Promise; - /** - * Set the user information. - * @deprecated: Use setUserInfo instead - * @param user: The user object (use builtin attributes: 'id', 'email', 'name', and/or any custom attribute). - */ - setUser(user: Object): Promise; - /** * Set the user information. * @param user: The user object (use builtin attributes: 'id', 'email', 'name', and any custom attribute under extraInfo). diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index e1c5096fb..fe1a5895c 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -5,7 +5,6 @@ */ import type { BatchProcessingLevel } from './DdSdkReactNativeConfiguration'; -import type { UserInfo as UserInfoSingleton } from './sdk/UserInfoSingleton/types'; declare global { // eslint-disable-next-line no-var, vars-on-top @@ -90,13 +89,6 @@ export type DdSdkType = { */ setAttributes(attributes: object): Promise; - /** - * Sets the user information. - * @deprecated UserInfo id property is now mandatory (please user setUserInfo instead) - * @param user: The user object (use builtin attributes: 'id', 'email', 'name', and/or any custom attribute). - */ - setUser(user: object): Promise; - /** * Sets the user information. * @param id: A unique user identifier (relevant to your business domain) @@ -173,7 +165,7 @@ export type LogEvent = { readonly source?: ErrorSource; // readonly date: number; // TODO: RUMM-2446 & RUMM-2447 readonly status: LogStatus; - readonly userInfo: UserInfoSingleton; + readonly userInfo?: UserInfo; readonly attributes?: object; }; diff --git a/packages/react-native-apollo-client/__mocks__/react-native.ts b/packages/react-native-apollo-client/__mocks__/react-native.ts index b35df4e31..046ced2f6 100644 --- a/packages/react-native-apollo-client/__mocks__/react-native.ts +++ b/packages/react-native-apollo-client/__mocks__/react-native.ts @@ -18,9 +18,6 @@ actualRN.NativeModules.DdSdk = { initialize: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, - setUser: jest.fn().mockImplementation( - () => new Promise(resolve => resolve()) - ) as jest.MockedFunction, setAttributes: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, From 9d130e5b4e8b339eac52491b60ca71cad738608a Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 12 Sep 2025 17:09:36 +0200 Subject: [PATCH 022/168] Update Tracer imports for Android to remove opentracing dependencies --- .../reactnative/DdTraceImplementation.kt | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt index 901ec3f6a..3ed77eb75 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdTraceImplementation.kt @@ -6,30 +6,28 @@ package com.datadog.reactnative -import com.datadog.android.trace.AndroidTracer -import com.datadog.android.trace.Trace -import com.datadog.android.trace.TraceConfiguration +import com.datadog.android.Datadog +import com.datadog.android.trace.DatadogTracing import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReadableMap -import io.opentracing.Scope -import io.opentracing.Span -import io.opentracing.Tracer -import io.opentracing.util.GlobalTracer +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.tracer.DatadogTracer +import com.datadog.android.trace.GlobalDatadogTracer import java.util.concurrent.TimeUnit /** * The entry point to use Datadog's Trace feature. */ class DdTraceImplementation( - private val tracerProvider: () -> Tracer = { - val tracer = AndroidTracer.Builder().build() - GlobalTracer.registerIfAbsent(tracer) - - GlobalTracer.get() + private val tracerProvider: () -> DatadogTracer = { + val tracer = DatadogTracing.newTracerBuilder(Datadog.getInstance()).build() + GlobalDatadogTracer.registerIfAbsent(tracer) + GlobalDatadogTracer.get() } ) { - private val spanMap: MutableMap = mutableMapOf() - private val scopeMap: MutableMap = mutableMapOf() + private val spanMap: MutableMap = mutableMapOf() + private val scopeMap: MutableMap = mutableMapOf() // lazy here is on purpose. The thing is that this class will be instantiated even // before Sdk.initialize is called, but Tracer can be created only after SDK is initialized. @@ -47,15 +45,18 @@ class DdTraceImplementation( .start() // This is required for traces to be able to be bundled with logs. - val scope = tracer.scopeManager().activate(span) - + val scope = tracer.activateSpan(span) val spanContext = span.context() span.setTags(context.toHashMap()) span.setTags(GlobalState.globalAttributes) - val spanId = spanContext.toSpanId() + val spanId = spanContext.spanId.toString() + spanMap[spanId] = span - scopeMap[spanId] = scope + if (scope != null) { + scopeMap[spanId] = scope + } + promise.resolve(spanId) } @@ -82,7 +83,7 @@ class DdTraceImplementation( promise.resolve(null) } - private fun Span.setTags(tags: Map) { + private fun DatadogSpan.setTags(tags: Map) { for ((key, value) in tags) { when (value) { is Boolean -> setTag(key, value) From c83598a1848514bbe2c6faa5ac91dc53983c4493 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 18 Sep 2025 10:13:53 +0200 Subject: [PATCH 023/168] Fix android tests --- .../com/datadog/reactnative/DdTraceTest.kt | 84 ++++++++++--------- .../com/datadog/tools/unit/MockRumMonitor.kt | 10 +-- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdTraceTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdTraceTest.kt index 16d459a57..8c22f88e1 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdTraceTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdTraceTest.kt @@ -6,6 +6,12 @@ package com.datadog.reactnative +import com.datadog.android.trace.api.scope.DatadogScope +import com.datadog.android.trace.api.span.DatadogSpan +import com.datadog.android.trace.api.span.DatadogSpanBuilder +import com.datadog.android.trace.api.span.DatadogSpanContext +import com.datadog.android.trace.api.trace.DatadogTraceId +import com.datadog.android.trace.api.tracer.DatadogTracer import com.datadog.tools.unit.toReadableMap import com.facebook.react.bridge.Promise import fr.xgouchet.elmyr.annotation.AdvancedForgery @@ -15,11 +21,6 @@ import fr.xgouchet.elmyr.annotation.MapForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeExtension -import io.opentracing.Scope -import io.opentracing.ScopeManager -import io.opentracing.Span -import io.opentracing.SpanContext -import io.opentracing.Tracer import org.assertj.core.api.Assertions.assertThat import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.Assumptions.assumeTrue @@ -51,22 +52,19 @@ internal class DdTraceTest { lateinit var testedTrace: DdTraceImplementation @Mock - lateinit var mockTracer: Tracer + lateinit var mockTracer: DatadogTracer @Mock - lateinit var mockSpanBuilder: Tracer.SpanBuilder + lateinit var mockSpanBuilder: DatadogSpanBuilder @Mock - lateinit var mockSpanContext: SpanContext + lateinit var mockSpanContext: DatadogSpanContext @Mock - lateinit var mockScopeManager: ScopeManager + lateinit var mockSpan: DatadogSpan @Mock - lateinit var mockSpan: Span - - @Mock - lateinit var mockScope: Scope + lateinit var mockScope: DatadogScope @StringForgery lateinit var fakeOperation: String @@ -74,11 +72,11 @@ internal class DdTraceTest { @DoubleForgery(1000000000000.0, 2000000000000.0) var fakeTimestamp: Double = 0.0 - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeSpanId: String + @LongForgery(100L, 2000L) + var fakeSpanId: Long = 0 - @StringForgery(type = StringForgeryType.HEXADECIMAL) - lateinit var fakeTraceId: String + @Mock + lateinit var fakeTraceId: DatadogTraceId @MapForgery( key = AdvancedForgery(string = [StringForgery()]), @@ -102,7 +100,6 @@ internal class DdTraceTest { @BeforeEach fun `set up`() { whenever(mockTracer.buildSpan(fakeOperation)) doReturn mockSpanBuilder - whenever(mockTracer.scopeManager()) doReturn mockScopeManager whenever( mockSpanBuilder.withStartTimestamp( fakeTimestamp.toLong() * 1000 @@ -110,9 +107,9 @@ internal class DdTraceTest { ) doReturn mockSpanBuilder whenever(mockSpanBuilder.start()) doReturn mockSpan whenever(mockSpan.context()) doReturn mockSpanContext - whenever(mockSpanContext.toSpanId()) doReturn fakeSpanId - whenever(mockSpanContext.toTraceId()) doReturn fakeTraceId - whenever(mockScopeManager.activate(mockSpan)) doReturn mockScope + whenever(mockSpanContext.spanId) doReturn fakeSpanId + whenever(mockSpanContext.traceId) doReturn fakeTraceId + whenever(mockTracer.activateSpan(mockSpan)) doReturn mockScope testedTrace = DdTraceImplementation(tracerProvider = { mockTracer }) } @@ -133,7 +130,7 @@ internal class DdTraceTest { ) // Then - assertThat(lastResolvedValue).isEqualTo(fakeSpanId) + assertThat(lastResolvedValue.toString()).isEqualTo(fakeSpanId.toString()) } @Test @@ -154,18 +151,20 @@ internal class DdTraceTest { fakeTimestamp, mockPromise ) - val id = lastResolvedValue - testedTrace.finishSpan(id as String, fakeContext.toReadableMap(), endTimestamp, mockPromise) + val id = lastResolvedValue.toString() + testedTrace.finishSpan(id, fakeContext.toReadableMap(), endTimestamp, mockPromise) // Then - assertThat(id).isEqualTo(fakeSpanId) + assertThat(id).isEqualTo(fakeSpanId.toString()) verify(mockSpan).finish(endTimestamp.toLong() * 1000) } @Test fun `M do nothing W startSpan() + finishSpan() with unknown id`( @LongForgery(100L, 2000L) duration: Long, - @StringForgery(type = StringForgeryType.HEXADECIMAL) otherSpanId: String + @StringForgery(type = StringForgeryType.HEXADECIMAL) + @LongForgery(100L, 2000L) + otherSpanId: Long ) { // Given assumeTrue(otherSpanId != fakeSpanId) @@ -178,11 +177,16 @@ internal class DdTraceTest { fakeTimestamp, mockPromise ) - val id = lastResolvedValue - testedTrace.finishSpan(otherSpanId, fakeContext.toReadableMap(), endTimestamp, mockPromise) + val id = lastResolvedValue.toString() + testedTrace.finishSpan( + otherSpanId.toString(), + fakeContext.toReadableMap(), + endTimestamp, + mockPromise + ) // Then - assertThat(id).isEqualTo(fakeSpanId) + assertThat(id).isEqualTo(fakeSpanId.toString()) verify(mockSpan, never()).finish(any()) } @@ -200,7 +204,7 @@ internal class DdTraceTest { fakeTimestamp, mockPromise ) - val id = lastResolvedValue + val id = lastResolvedValue.toString() testedTrace.finishSpan( id as String, emptyMap().toReadableMap(), @@ -209,7 +213,7 @@ internal class DdTraceTest { ) // Then - assertThat(id).isEqualTo(fakeSpanId) + assertThat(id).isEqualTo(fakeSpanId.toString()) verify(mockSpan).context() verify(mockSpan).finish(endTimestamp.toLong() * 1000) fakeContext.forEach { @@ -232,11 +236,11 @@ internal class DdTraceTest { fakeTimestamp, mockPromise ) - val id = lastResolvedValue - testedTrace.finishSpan(id as String, fakeContext.toReadableMap(), endTimestamp, mockPromise) + val id = lastResolvedValue.toString() + testedTrace.finishSpan(id, fakeContext.toReadableMap(), endTimestamp, mockPromise) // Then - assertThat(id).isEqualTo(fakeSpanId) + assertThat(id).isEqualTo(fakeSpanId.toString()) verify(mockSpan).context() verify(mockSpan).finish(endTimestamp.toLong() * 1000) fakeContext.forEach { @@ -262,16 +266,16 @@ internal class DdTraceTest { fakeTimestamp, mockPromise ) - val id = lastResolvedValue + val id = lastResolvedValue.toString() testedTrace.finishSpan( - id as String, + id, emptyMap().toReadableMap(), endTimestamp, mockPromise ) // Then - assertThat(id).isEqualTo(fakeSpanId) + assertThat(id).isEqualTo(fakeSpanId.toString()) verify(mockSpan).context() verify(mockSpan).finish(endTimestamp.toLong() * 1000) fakeContext.forEach { @@ -298,14 +302,14 @@ internal class DdTraceTest { fakeTimestamp, mockPromise ) - val id = lastResolvedValue + val id = lastResolvedValue.toString() fakeGlobalState.forEach { (k, v) -> GlobalState.addAttribute(k, v) } - testedTrace.finishSpan(id as String, fakeContext.toReadableMap(), endTimestamp, mockPromise) + testedTrace.finishSpan(id, fakeContext.toReadableMap(), endTimestamp, mockPromise) // Then - assertThat(id).isEqualTo(fakeSpanId) + assertThat(id).isEqualTo(fakeSpanId.toString()) verify(mockSpan).context() verify(mockSpan).finish(endTimestamp.toLong() * 1000) expectedAttributes.forEach { diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt index a2e79d630..7c5585edd 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt @@ -29,6 +29,8 @@ class MockRumMonitor : RumMonitor { override fun addAttribute(key: String, value: Any?) {} + override fun addViewAttributes(attributes: Map) {} + override fun addError( message: String, source: RumErrorSource, @@ -61,6 +63,7 @@ class MockRumMonitor : RumMonitor { override fun getCurrentSessionId(callback: (String?) -> Unit) {} override fun removeAttribute(key: String) {} + override fun removeViewAttributes(attributes: Collection) {} override fun startAction( type: RumActionType, @@ -75,13 +78,6 @@ class MockRumMonitor : RumMonitor { attributes: Map ) {} - override fun startResource( - key: String, - method: String, - url: String, - attributes: Map - ) {} - override fun startView( key: Any, name: String, From dee0ebcdbc02777336a719b1c176a92854a974c1 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 18 Sep 2025 10:50:27 +0200 Subject: [PATCH 024/168] Fix iOS tests --- packages/core/ios/Tests/DdSdkTests.swift | 2 +- packages/core/ios/Tests/RUMMocks.swift | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 6be5e0994..555ce4549 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -738,7 +738,7 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(actualFirstPartyHosts, expectedFirstPartyHosts) XCTAssertEqual(actualTracingSamplingRate, 66) - XCTAssertEqual(actualTraceContextInjection, .all) + XCTAssertEqual(actualTraceContextInjection, .sampled) } func testBuildTelemetrySampleRate() { diff --git a/packages/core/ios/Tests/RUMMocks.swift b/packages/core/ios/Tests/RUMMocks.swift index 6a6fd94e7..01d0f9d0d 100644 --- a/packages/core/ios/Tests/RUMMocks.swift +++ b/packages/core/ios/Tests/RUMMocks.swift @@ -213,14 +213,14 @@ extension RUMActionID: RandomMockable { } } -extension RUMDevice.RUMDeviceType: RandomMockable { - static func mockRandom() -> RUMDevice.RUMDeviceType { +extension Device.DeviceType: RandomMockable { + static func mockRandom() -> Device.DeviceType { return [.mobile, .desktop, .tablet, .tv, .gamingConsole, .bot, .other].randomElement()! } } -extension RUMDevice: RandomMockable { - static func mockRandom() -> RUMDevice { +extension Device: RandomMockable { + static func mockRandom() -> Device { return .init( architecture: .mockRandom(), brand: .mockRandom(), @@ -231,8 +231,8 @@ extension RUMDevice: RandomMockable { } } -extension RUMOperatingSystem: RandomMockable { - static func mockRandom() -> RUMOperatingSystem { +extension OperatingSystem: RandomMockable { + static func mockRandom() -> OperatingSystem { return .init( build: .mockRandom(length: 5), name: .mockRandom(length: 5), From f0b3c5efa5aa5f3addb7ae352f5d48bfd0d5503c Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 19 Sep 2025 11:45:00 +0200 Subject: [PATCH 025/168] Bump Native SDKs to 3.1.0 --- benchmarks/android/app/build.gradle | 2 +- benchmarks/ios/Podfile.lock | 70 +++++++-------- example-new-architecture/ios/Podfile.lock | 66 +++++++------- example/ios/Podfile.lock | 88 +++++++++---------- packages/core/DatadogSDKReactNative.podspec | 12 +-- packages/core/android/build.gradle | 10 +-- .../com/datadog/tools/unit/MockRumMonitor.kt | 23 +++++ packages/core/ios/Tests/DdLogsTests.swift | 2 + ...DatadogSDKReactNativeSessionReplay.podspec | 2 +- .../android/build.gradle | 4 +- .../DatadogSDKReactNativeWebView.podspec | 4 +- .../react-native-webview/android/build.gradle | 2 +- 12 files changed, 155 insertions(+), 130 deletions(-) diff --git a/benchmarks/android/app/build.gradle b/benchmarks/android/app/build.gradle index 7dc4b3707..04c240bd0 100644 --- a/benchmarks/android/app/build.gradle +++ b/benchmarks/android/app/build.gradle @@ -129,5 +129,5 @@ dependencies { // Benchmark tools from dd-sdk-android are used for vitals recording // Remember to bump thid alongside the main dd-sdk-android dependencies - implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.0.0") + implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.1.0") } diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index 7e2fb2829..fcf687c21 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogCrashReporting (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogCore (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogCrashReporting (3.1.0): + - DatadogInternal (= 3.1.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.0.0) - - DatadogLogs (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogRUM (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogInternal (3.1.0) + - DatadogLogs (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogRUM (3.1.0): + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -39,7 +39,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.0.0) + - DatadogSessionReplay (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -61,9 +61,9 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNativeWebView (2.12.1): - - DatadogInternal (= 3.0.0) + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.0.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -84,13 +84,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSessionReplay (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogTrace (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogSessionReplay (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogTrace (3.1.0): + - DatadogInternal (= 3.1.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogWebViewTracking (3.1.0): + - DatadogInternal (= 3.1.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.78.2) @@ -2070,17 +2070,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 - DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e - DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 - DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd - DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f - DatadogSDKReactNative: 241bf982c16ceff03d94a58e6d005e55c47b99c6 - DatadogSDKReactNativeSessionReplay: bba36092686e3183e97c1a0c7f4ca8142582ae28 - DatadogSDKReactNativeWebView: 6da060df20e235abac533e582d9fc2b3a5070840 - DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 - DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 - DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 + DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d + DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d + DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc + DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 + DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 + DatadogSDKReactNative: 8e0f39de38621d4d7ed961a74d8a216fd3a38321 + DatadogSDKReactNativeSessionReplay: f9288c8e981dcc65d1f727b01421ee9a7601e75f + DatadogSDKReactNativeWebView: 993527f6c5d38e0fcc4804a6a60c334dd199dc5b + DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 + DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 + DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: e32d34492c519a2194ec9d7f5e7a79d11b73f91c diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index cc9913ce3..0378f5d72 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogCrashReporting (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogCore (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogCrashReporting (3.1.0): + - DatadogInternal (= 3.1.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.0.0) - - DatadogLogs (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogRUM (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogInternal (3.1.0) + - DatadogLogs (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogRUM (3.1.0): + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -38,12 +38,12 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNative/Tests (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -64,11 +64,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogTrace (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogTrace (3.1.0): + - DatadogInternal (= 3.1.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogWebViewTracking (3.1.0): + - DatadogInternal (= 3.1.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1850,14 +1850,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 - DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e - DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 - DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd - DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f - DatadogSDKReactNative: 0a80aa75958d595a99be54d2838db53eda7a08af - DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 - DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 + DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d + DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d + DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc + DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 + DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 + DatadogSDKReactNative: 069ea9876220b2d09b0f4b180ce571b1b6ecbb35 + DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 + DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index acf9029a0..8a658e34f 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,34 +1,34 @@ PODS: - boost (1.84.0) - - DatadogCore (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogCrashReporting (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogCore (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogCrashReporting (3.1.0): + - DatadogInternal (= 3.1.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.0.0) - - DatadogLogs (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogRUM (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogInternal (3.1.0) + - DatadogLogs (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogRUM (3.1.0): + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - DatadogSDKReactNative/Tests (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.0.0) + - DatadogSessionReplay (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -51,7 +51,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay/Tests (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.0.0) + - DatadogSessionReplay (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -74,24 +74,24 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNativeWebView (2.12.1): - - DatadogInternal (= 3.0.0) + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.0.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - DatadogSDKReactNativeWebView/Tests (2.12.1): - - DatadogInternal (= 3.0.0) + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.0.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - react-native-webview - React-RCTText - - DatadogSessionReplay (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogTrace (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogSessionReplay (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogTrace (3.1.0): + - DatadogInternal (= 3.1.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogWebViewTracking (3.1.0): + - DatadogInternal (= 3.1.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1988,17 +1988,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 - DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e - DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 - DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd - DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f - DatadogSDKReactNative: e430b3f4d7adb0fac07b61e2fb65ceacdaf82b3e - DatadogSDKReactNativeSessionReplay: 4b2a3d166a79581f18522795b40141c34cf3685d - DatadogSDKReactNativeWebView: 35dc2b9736e1aaa82b366bf6b8a8a959a9b088c5 - DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 - DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 - DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 + DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d + DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d + DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc + DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 + DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 + DatadogSDKReactNative: af351a4e1ce08124c290c52de94b0062a166cc67 + DatadogSDKReactNativeSessionReplay: dcbd55d9d0f2b86026996a8b7ec9654922d5dfe1 + DatadogSDKReactNativeWebView: 096ac87eb753b6a217b93441983264b9837c3b7e + DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 + DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 + DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/packages/core/DatadogSDKReactNative.podspec b/packages/core/DatadogSDKReactNative.podspec index a2f41ddd1..c0d235304 100644 --- a/packages/core/DatadogSDKReactNative.podspec +++ b/packages/core/DatadogSDKReactNative.podspec @@ -19,14 +19,14 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the versions in sync with DatadogSDKReactNativeSessionReplay.podspec - s.dependency 'DatadogCore', '3.0.0' - s.dependency 'DatadogLogs', '3.0.0' - s.dependency 'DatadogTrace', '3.0.0' - s.dependency 'DatadogRUM', '3.0.0' - s.dependency 'DatadogCrashReporting', '3.0.0' + s.dependency 'DatadogCore', '3.1.0' + s.dependency 'DatadogLogs', '3.1.0' + s.dependency 'DatadogTrace', '3.1.0' + s.dependency 'DatadogRUM', '3.1.0' + s.dependency 'DatadogCrashReporting', '3.1.0' # DatadogWebViewTracking is not available for tvOS - s.ios.dependency 'DatadogWebViewTracking', '3.0.0' + s.ios.dependency 'DatadogWebViewTracking', '3.1.0' s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'ios/Tests/**/*.{swift,json}' diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index a8d09d4d1..59bb75cc3 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -201,16 +201,16 @@ dependencies { // This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0. // To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0 if (reactNativeMinorVersion < 76) { - implementation("com.datadoghq:dd-sdk-android-rum:3.0.0") { + implementation("com.datadoghq:dd-sdk-android-rum:3.1.0") { exclude group: "androidx.metrics", module: "metrics-performance" } implementation "androidx.metrics:metrics-performance:1.0.0-beta01" } else { - implementation "com.datadoghq:dd-sdk-android-rum:3.0.0" + implementation "com.datadoghq:dd-sdk-android-rum:3.1.0" } - implementation "com.datadoghq:dd-sdk-android-logs:3.0.0" - implementation "com.datadoghq:dd-sdk-android-trace:3.0.0" - implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" + implementation "com.datadoghq:dd-sdk-android-logs:3.1.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.1.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" implementation "com.google.code.gson:gson:2.10.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt index 7c5585edd..702cc2533 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt @@ -13,6 +13,7 @@ import com.datadog.android.rum.RumMonitor import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod import com.datadog.android.rum._RumInternalProxy +import com.datadog.android.rum.featureoperations.FailureReason class MockRumMonitor : RumMonitor { override var debug = false @@ -123,4 +124,26 @@ class MockRumMonitor : RumMonitor { key: Any, attributes: Map ) {} + + @ExperimentalRumApi + override fun startFeatureOperation( + name: String, + operationKey: String?, + attributes: Map + ) {} + + @ExperimentalRumApi + override fun succeedFeatureOperation( + name: String, + operationKey: String?, + attributes: Map + ) {} + + @ExperimentalRumApi + override fun failFeatureOperation( + name: String, + operationKey: String?, + failureReason: FailureReason, + attributes: Map + ) {} } diff --git a/packages/core/ios/Tests/DdLogsTests.swift b/packages/core/ios/Tests/DdLogsTests.swift index 2d9fdebac..60640e807 100644 --- a/packages/core/ios/Tests/DdLogsTests.swift +++ b/packages/core/ios/Tests/DdLogsTests.swift @@ -463,6 +463,8 @@ private class MockNativeLogger: LoggerProtocol { } extension MockNativeLogger: InternalLoggerProtocol { + func critical(message: String, error: (any Error)?, attributes: [String : any Encodable]?, completionHandler: @escaping DatadogInternal.CompletionHandler) {} + func log(level: DatadogLogs.LogLevel, message: String, errorKind: String?, errorMessage: String?, stackTrace: String?, attributes: [String : Encodable]?) { receivedMethodCalls.append(MethodCall( kind: MockNativeLogger.MethodCall.Kind(from: level), diff --git a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec index 3a2d5ff48..6a5d0b78f 100644 --- a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec +++ b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogSessionReplay', '3.0.0' + s.dependency 'DatadogSessionReplay', '3.1.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-session-replay/android/build.gradle b/packages/react-native-session-replay/android/build.gradle index 6d6fe20b1..a0d77f2ff 100644 --- a/packages/react-native-session-replay/android/build.gradle +++ b/packages/react-native-session-replay/android/build.gradle @@ -216,8 +216,8 @@ dependencies { api "com.facebook.react:react-android:$reactNativeVersion" } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.datadoghq:dd-sdk-android-session-replay:3.0.0" - implementation "com.datadoghq:dd-sdk-android-internal:3.0.0" + implementation "com.datadoghq:dd-sdk-android-session-replay:3.1.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.1.0" implementation project(path: ':datadog_mobile-react-native') testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec index 26e160bbc..080a853d8 100644 --- a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec +++ b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec @@ -23,8 +23,8 @@ Pod::Spec.new do |s| end # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogWebViewTracking', '3.0.0' - s.dependency 'DatadogInternal', '3.0.0' + s.dependency 'DatadogWebViewTracking', '3.1.0' + s.dependency 'DatadogInternal', '3.1.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-webview/android/build.gradle b/packages/react-native-webview/android/build.gradle index 8eeaea88e..e1fb081f0 100644 --- a/packages/react-native-webview/android/build.gradle +++ b/packages/react-native-webview/android/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.facebook.react:react-android:$reactNativeVersion" } - implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native') From efd38c7cba8e1867ea4a7f6d06e6a82ee3f1450b Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 19 Sep 2025 14:33:51 +0200 Subject: [PATCH 026/168] Fix internaltTestingTools tests --- .../DdInternalTestingImplementation.kt | 45 ++++++++------- .../DdInternalTestingImplementationTest.kt | 55 ++++++++++++------- 2 files changed, 60 insertions(+), 40 deletions(-) diff --git a/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt b/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt index b33bef6de..0d548aa72 100644 --- a/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt +++ b/packages/internal-testing-tools/android/src/main/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementation.kt @@ -6,11 +6,13 @@ package com.datadog.reactnative.internaltesting +import androidx.annotation.WorkerThread import com.datadog.android.api.InternalLogger import com.datadog.android.Datadog import com.datadog.android.api.context.DatadogContext import com.datadog.android.api.context.NetworkInfo import com.datadog.android.api.context.TimeInfo +import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope import com.datadog.android.api.storage.EventBatchWriter @@ -112,53 +114,54 @@ internal class FeatureScopeInterceptor( private val featureScope: FeatureScope, private val core: InternalSdkCore, ) : FeatureScope by featureScope { - private val eventsBatchInterceptor = EventBatchInterceptor() + private val eventWriteScopeInterceptor = EventWriteScopeInterceptor() fun eventsWritten(): List { - return eventsBatchInterceptor.events + return eventWriteScopeInterceptor.events } fun clearData() { - eventsBatchInterceptor.clearData() + eventWriteScopeInterceptor.clearData() } // region FeatureScope override fun withWriteContext( - forceNewBatch: Boolean, - callback: (DatadogContext, EventBatchWriter) -> Unit + withFeatureContexts: Set, + callback: (datadogContext: DatadogContext, write: EventWriteScope) -> Unit ) { - featureScope.withWriteContext(forceNewBatch, callback) + featureScope.withWriteContext(withFeatureContexts, callback) core.getDatadogContext()?.let { - callback(it, eventsBatchInterceptor) + callback(it, eventWriteScopeInterceptor) } } // endregion } - -internal class EventBatchInterceptor: EventBatchWriter { +internal class EventWriteScopeInterceptor : EventWriteScope { internal val events = mutableListOf() - override fun currentMetadata(): ByteArray? { - return null - } - fun clearData() { events.clear() } - override fun write( - event: RawBatchEvent, - batchMetadata: ByteArray?, - eventType: EventType - ): Boolean { - val eventContent = String(event.data) + private val writer = object : EventBatchWriter { + override fun currentMetadata(): ByteArray? = null - events += eventContent + override fun write( + event: RawBatchEvent, + batchMetadata: ByteArray?, + eventType: EventType + ): Boolean { + events += String(event.data) + return true + } + } - return true + override fun invoke(p1: (EventBatchWriter) -> Unit) { + p1(writer) } } + diff --git a/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt b/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt index 4a6938f9b..d25db9274 100644 --- a/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt +++ b/packages/internal-testing-tools/android/src/test/kotlin/com/datadog/reactnative/internaltesting/DdInternalTestingImplementationTest.kt @@ -10,9 +10,9 @@ import android.content.Context import com.datadog.android.Datadog import com.datadog.android.api.SdkCore import com.datadog.android.api.context.DatadogContext +import com.datadog.android.api.feature.EventWriteScope import com.datadog.android.api.feature.Feature import com.datadog.android.api.feature.FeatureScope -import com.datadog.android.api.storage.EventBatchWriter import com.datadog.android.api.storage.EventType import com.datadog.android.api.storage.RawBatchEvent import com.datadog.android.api.storage.datastore.DataStoreHandler @@ -85,23 +85,27 @@ internal class DdInternalTestingImplementationTest { wrappedCore.registerFeature(mockFeature) requireNotNull(wrappedCore.getFeature(mockFeature.name)) - .withWriteContext { _, eventBatchWriter -> - eventBatchWriter.write( - RawBatchEvent(data = "mock event for test".toByteArray()), - batchMetadata = null, - eventType = EventType.DEFAULT + .withWriteContext { _, writeScope -> + writeScope { + val rawBatchEvent = + RawBatchEvent(data = "mock event for test".toByteArray()) + it.write( + rawBatchEvent, + batchMetadata = null, + eventType = EventType.DEFAULT + ) + } + + // Then + assertThat( + wrappedCore.featureScopes[mockFeature.name] + ?.eventsWritten() + ?.first() ) + .isEqualTo( + "mock event for test" + ) } - - // Then - assertThat( - wrappedCore.featureScopes[mockFeature.name] - ?.eventsWritten() - ?.first() - ) - .isEqualTo( - "mock event for test" - ) } } } @@ -116,10 +120,23 @@ internal class MockFeatureScope(private val feature: Feature) : FeatureScope { return feature as T } + override fun withContext( + withFeatureContexts: Set, + callback: (datadogContext: DatadogContext) -> Unit + ) { + } + override fun withWriteContext( - forceNewBatch: Boolean, - callback: (DatadogContext, EventBatchWriter) -> Unit - ) {} + withFeatureContexts: Set, + callback: (datadogContext: DatadogContext, write: EventWriteScope) -> Unit + ) { + } + + override fun getWriteContextSync( + withFeatureContexts: Set + ): Pair? { + return TODO("Provide the return value") + } } internal class MockFeature(override val name: String) : Feature { From d40ec79b2005b9343ade6ffd641510d41a7095d0 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 5 Sep 2025 17:47:29 +0200 Subject: [PATCH 027/168] Use native sdk's core instance instead of the one inside RN SDK wrapper --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 ++ .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +----- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 +------ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index 4388ad5f6..ee55d08fe 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,7 +65,9 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) + Logs.enable(logsConfiguration, Datadog.getInstance()) + Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 97acb2ebf..3c86e28b4 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -21,11 +21,7 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation( - reactContext, - datadog = datadogWrapper, - ddTelemetry - ) + private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 8797bcc64..c9f91738f 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -163,12 +163,7 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation( - mockReactContext, - mockDatadog, - mockDdTelemetry, - TestUiThreadExecutor() - ) + testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From d4f93e1018ea813467a80dd39b34af0b35062752 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Mon, 22 Sep 2025 16:20:27 +0200 Subject: [PATCH 028/168] Fixed internal testing tools and unit tests --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 -- .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +++++- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 ++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index ee55d08fe..4388ad5f6 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,9 +65,7 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) - Logs.enable(logsConfiguration, Datadog.getInstance()) - Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 3c86e28b4..97acb2ebf 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -21,7 +21,11 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) + private val implementation = DdSdkImplementation( + reactContext, + datadog = datadogWrapper, + ddTelemetry + ) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index c9f91738f..8797bcc64 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -163,7 +163,12 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) + testedBridgeSdk = DdSdkImplementation( + mockReactContext, + mockDatadog, + mockDdTelemetry, + TestUiThreadExecutor() + ) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From 4d2b1ee7cc033e2e10d8f1eff63bc6db98b53a2d Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 19 Sep 2025 17:37:31 +0200 Subject: [PATCH 029/168] Expose clearUserInfo API --- packages/core/__mocks__/react-native.ts | 3 + .../datadog/reactnative/DatadogSDKWrapper.kt | 4 ++ .../com/datadog/reactnative/DatadogWrapper.kt | 5 ++ .../reactnative/DdSdkImplementation.kt | 10 ++- .../kotlin/com/datadog/reactnative/DdSdk.kt | 8 +++ .../kotlin/com/datadog/reactnative/DdSdk.kt | 8 +++ .../com/datadog/reactnative/DdSdkTest.kt | 11 +++ packages/core/ios/Sources/DdSdk.mm | 12 +++- .../ios/Sources/DdSdkImplementation.swift | 8 ++- packages/core/ios/Tests/DdSdkTests.swift | 67 +++++++++++++++++++ packages/core/jest/mock.js | 3 + packages/core/src/DdSdkReactNative.tsx | 10 +++ .../src/__tests__/DdSdkReactNative.test.tsx | 26 +++++++ .../UserInfoSingleton/UserInfoSingleton.ts | 4 ++ .../__tests__/UserInfoSingleton.test.ts | 55 ++++++++++++--- packages/core/src/specs/NativeDdSdk.ts | 5 ++ packages/core/src/types.tsx | 5 ++ 17 files changed, 230 insertions(+), 14 deletions(-) diff --git a/packages/core/__mocks__/react-native.ts b/packages/core/__mocks__/react-native.ts index 260fe68a7..0e85e65ae 100644 --- a/packages/core/__mocks__/react-native.ts +++ b/packages/core/__mocks__/react-native.ts @@ -24,6 +24,9 @@ actualRN.NativeModules.DdSdk = { addUserExtraInfo: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, + clearUserInfo: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, setAttributes: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index 198061d14..f781687eb 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -86,6 +86,10 @@ internal class DatadogSDKWrapper : DatadogWrapper { Datadog.addUserProperties(extraInfo) } + override fun clearUserInfo() { + Datadog.clearUserInfo() + } + override fun addRumGlobalAttributes(attributes: Map) { val rumMonitor = this.getRumMonitor() for (attribute in attributes) { diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index 3ae3e6266..49d606b35 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -86,6 +86,11 @@ interface DatadogWrapper { extraInfo: Map ) + /** + * Clears the user information. + */ + fun clearUserInfo() + /** * Adds global attributes. * diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 7a5c6848c..7adcf7438 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -102,7 +102,7 @@ class DdSdkImplementation( } /** - * Sets the user information. + * Sets the user extra information. * @param userExtraInfo: The additional information. (To set the id, name or email please user setUserInfo). */ fun addUserExtraInfo( @@ -114,6 +114,14 @@ class DdSdkImplementation( promise.resolve(null) } + /** + * Clears the user information. + */ + fun clearUserInfo(promise: Promise) { + datadog.clearUserInfo() + promise.resolve(null) + } + /** * Set the tracking consent regarding the data collection. * @param trackingConsent Consent, which can take one of the following values: 'pending', diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt index cfafffffe..4e4668a3e 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -67,6 +67,14 @@ class DdSdk( implementation.addUserExtraInfo(extraInfo, promise) } + /** + * Clears the user information. + */ + @ReactMethod + override fun clearUserInfo(promise: Promise) { + implementation.clearUserInfo(promise) + } + /** * Set the tracking consent regarding the data collection. * @param trackingConsent Consent, which can take one of the following values: 'pending', diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 97acb2ebf..0ebdd37fb 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -93,6 +93,14 @@ class DdSdk( implementation.addUserExtraInfo(extraInfo, promise) } + /** + * Clears the user information. + */ + @ReactMethod + fun clearUserInfo(promise: Promise) { + implementation.clearUserInfo(promise) + } + /** * Set the tracking consent regarding the data collection. * @param trackingConsent Consent, which can take one of the following values: 'pending', diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 8797bcc64..f917bb847 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -2954,6 +2954,17 @@ internal class DdSdkTest { } } + @Test + fun `𝕄 clear user info 𝕎 clearUserInfo()`() { + // When + testedBridgeSdk.clearUserInfo(mockPromise) + + // Then + argumentCaptor> { + verify(mockDatadog).clearUserInfo() + } + } + @Test fun `𝕄 set RUM attributes 𝕎 setAttributes`( @MapForgery( diff --git a/packages/core/ios/Sources/DdSdk.mm b/packages/core/ios/Sources/DdSdk.mm index 918a8db03..674cd0fbc 100644 --- a/packages/core/ios/Sources/DdSdk.mm +++ b/packages/core/ios/Sources/DdSdk.mm @@ -51,6 +51,12 @@ + (void)initFromNative { [self addUserExtraInfo:extraInfo resolve:resolve reject:reject]; } +RCT_EXPORT_METHOD(clearUserInfo:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self clearUserInfo:resolve reject:reject]; +} + RCT_REMAP_METHOD(setTrackingConsent, withTrackingConsent:(NSString*)trackingConsent withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) @@ -81,7 +87,7 @@ + (void)initFromNative { [self consumeWebviewEvent:message resolve:resolve reject:reject]; } -RCT_REMAP_METHOD(clearAllData, withResolver:(RCTPromiseResolveBlock)resolve +RCT_EXPORT_METHOD(clearAllData:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) { [self clearAllData:resolve reject:reject]; @@ -143,6 +149,10 @@ - (void)setUserInfo:(NSDictionary *)userInfo resolve:(RCTPromiseResolveBlock)res [self.ddSdkImplementation setUserInfoWithUserInfo:userInfo resolve:resolve reject:reject]; } +- (void)clearUserInfo:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation clearUserInfoWithResolve:resolve reject:reject]; +} + -(void)addUserExtraInfo:(NSDictionary *)extraInfo resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self.ddSdkImplementation addUserExtraInfoWithExtraInfo:extraInfo resolve:resolve reject:reject]; } diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index b2e610635..03c630b70 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -108,7 +108,7 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - + @objc public func addUserExtraInfo(extraInfo: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { let castedExtraInfo = castAttributesToSwift(extraInfo) @@ -117,6 +117,12 @@ public class DdSdkImplementation: NSObject { resolve(nil) } + @objc + public func clearUserInfo(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + Datadog.clearUserInfo() + resolve(nil) + } + @objc public func setTrackingConsent(trackingConsent: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { Datadog.set(trackingConsent: (trackingConsent as NSString?).asTrackingConsent()) diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 555ce4549..efbf57b96 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -651,6 +651,73 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } + + func testClearUserInfo() throws { + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ) + bridge.initialize( + configuration: .mockAny(), + resolve: mockResolve, + reject: mockReject + ) + + bridge.setUserInfo( + userInfo: NSDictionary( + dictionary: [ + "id": "id_123", + "name": "John Doe", + "email": "john@doe.com", + "extraInfo": [ + "extra-info-1": 123, + "extra-info-2": "abc", + "extra-info-3": true, + "extra-info-4": [ + "nested-extra-info-1": 456 + ], + ], + ] + ), + resolve: mockResolve, + reject: mockReject + ) + + var ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + var userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, "id_123") + XCTAssertEqual(userInfo.name, "John Doe") + XCTAssertEqual(userInfo.email, "john@doe.com") + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) + + if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] + as? DatadogSDKReactNative.AnyEncodable, + let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] + { + XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) + } else { + XCTFail("extra-info-4 is not of expected type or value") + } + + bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) + + ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, nil) + XCTAssertEqual(userInfo.name, nil) + XCTAssertEqual(userInfo.email, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) + } func testSettingAttributes() { let rumMonitorMock = MockRUMMonitor() diff --git a/packages/core/jest/mock.js b/packages/core/jest/mock.js index a8161295a..8e154c4cd 100644 --- a/packages/core/jest/mock.js +++ b/packages/core/jest/mock.js @@ -33,6 +33,9 @@ module.exports = { addUserExtraInfo: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), + clearUserInfo: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), setAttributes: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index 668ae09f3..1838542df 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -214,6 +214,16 @@ export class DdSdkReactNative { UserInfoSingleton.getInstance().setUserInfo(userInfo); }; + /** + * Clears the user information. + * @returns a Promise. + */ + static clearUserInfo = async (): Promise => { + InternalLog.log('Clearing user info', SdkVerbosity.DEBUG); + await DdSdk.clearUserInfo(); + UserInfoSingleton.getInstance().clearUserInfo(); + }; + /** * Set the user information. * @param extraUserInfo: The additional information. (To set the id, name or email please user setUserInfo). diff --git a/packages/core/src/__tests__/DdSdkReactNative.test.tsx b/packages/core/src/__tests__/DdSdkReactNative.test.tsx index f9405aa51..57ff5d0ed 100644 --- a/packages/core/src/__tests__/DdSdkReactNative.test.tsx +++ b/packages/core/src/__tests__/DdSdkReactNative.test.tsx @@ -1112,6 +1112,32 @@ describe('DdSdkReactNative', () => { }); }); + describe('clearUserInfo', () => { + it('calls SDK method when clearUserInfo, and clears the user in UserProvider', async () => { + // GIVEN + const userInfo = { + id: 'id', + name: 'name', + email: 'email', + extraInfo: { + foo: 'bar' + } + }; + + await DdSdkReactNative.setUserInfo(userInfo); + + // WHEN + await DdSdkReactNative.clearUserInfo(); + + // THEN + expect(DdSdk.clearUserInfo).toHaveBeenCalledTimes(1); + expect(DdSdk.setUserInfo).toHaveBeenCalled(); + expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual( + undefined + ); + }); + }); + describe('setTrackingConsent', () => { it('calls SDK method when setTrackingConsent', async () => { // GIVEN diff --git a/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts b/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts index 26392d794..3ce23614b 100644 --- a/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts +++ b/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts @@ -16,6 +16,10 @@ class UserInfoProvider { getUserInfo = (): UserInfo | undefined => { return this.userInfo; }; + + clearUserInfo = () => { + this.userInfo = undefined; + }; } export class UserInfoSingleton { diff --git a/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts b/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts index 1f7ae84e7..f8e7276d6 100644 --- a/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts +++ b/packages/core/src/sdk/UserInfoSingleton/__tests__/UserInfoSingleton.test.ts @@ -7,27 +7,60 @@ import { UserInfoSingleton } from '../UserInfoSingleton'; describe('UserInfoSingleton', () => { - it('sets, returns and resets the user info', () => { + beforeEach(() => { + UserInfoSingleton.reset(); + }); + + it('returns undefined by default', () => { + expect(UserInfoSingleton.getInstance().getUserInfo()).toBeUndefined(); + }); + + it('stores and returns user info after setUserInfo', () => { + const info = { + id: 'test', + email: 'user@mail.com', + extraInfo: { loggedIn: true } + }; + + UserInfoSingleton.getInstance().setUserInfo(info); + + expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual(info); + }); + + it('clears user info with clearUserInfo', () => { UserInfoSingleton.getInstance().setUserInfo({ id: 'test', email: 'user@mail.com', - extraInfo: { - loggedIn: true - } + extraInfo: { loggedIn: true } }); - expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual({ + UserInfoSingleton.getInstance().clearUserInfo(); + + expect(UserInfoSingleton.getInstance().getUserInfo()).toBeUndefined(); + }); + + it('reset() replaces the provider and clears stored user info', () => { + const instanceBefore = UserInfoSingleton.getInstance(); + + UserInfoSingleton.getInstance().setUserInfo({ id: 'test', email: 'user@mail.com', - extraInfo: { - loggedIn: true - } + extraInfo: { loggedIn: true } }); UserInfoSingleton.reset(); - expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual( - undefined - ); + const instanceAfter = UserInfoSingleton.getInstance(); + + expect(instanceAfter).not.toBe(instanceBefore); + + expect(instanceAfter.getUserInfo()).toBeUndefined(); + }); + + it('getInstance returns the same provider between calls (singleton behavior)', () => { + const a = UserInfoSingleton.getInstance(); + const b = UserInfoSingleton.getInstance(); + + expect(a).toBe(b); }); }); diff --git a/packages/core/src/specs/NativeDdSdk.ts b/packages/core/src/specs/NativeDdSdk.ts index bbf2572ee..a2ce1120e 100644 --- a/packages/core/src/specs/NativeDdSdk.ts +++ b/packages/core/src/specs/NativeDdSdk.ts @@ -37,6 +37,11 @@ export interface Spec extends TurboModule { */ setUserInfo(user: Object): Promise; + /** + * Clears the user information. + */ + clearUserInfo(): Promise; + /** * Add custom attributes to the current user information * @param extraInfo: The extraInfo object containing additionall custom attributes diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index fe1a5895c..bad19d429 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -98,6 +98,11 @@ export type DdSdkType = { */ setUserInfo(userInfo: UserInfo): Promise; + /** + * Clears the user information. + */ + clearUserInfo(): Promise; + /** * Add additional user information. * @param extraUserInfo: The additional information. (To set the id, name or email please user setUserInfo). From 55ef82bb35d16b8a880da0f9823cc2eefbbfc0e6 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 29 Sep 2025 12:20:44 +0200 Subject: [PATCH 030/168] Update attribute API --- benchmarks/src/testSetup/monitor.ts | 5 +- example/src/ddUtils.tsx | 4 +- packages/codepush/__mocks__/react-native.ts | 13 +- packages/core/__mocks__/react-native.ts | 13 +- .../datadog/reactnative/DatadogSDKWrapper.kt | 17 ++- .../com/datadog/reactnative/DatadogWrapper.kt | 22 ++++ .../reactnative/DdSdkImplementation.kt | 51 ++++++- .../kotlin/com/datadog/reactnative/DdSdk.kt | 39 +++++- .../kotlin/com/datadog/reactnative/DdSdk.kt | 37 +++++- .../com/datadog/reactnative/DdSdkTest.kt | 118 ++++++++++++++++- packages/core/ios/Sources/AnyEncodable.swift | 21 ++- packages/core/ios/Sources/DdSdk.mm | 42 +++++- .../ios/Sources/DdSdkImplementation.swift | 31 ++++- packages/core/ios/Sources/GlobalState.swift | 2 +- packages/core/ios/Tests/DdSdkTests.swift | 124 +++++++++++++++++- packages/core/ios/Tests/MockRUMMonitor.swift | 12 +- packages/core/jest/mock.js | 11 +- packages/core/src/DdSdkReactNative.tsx | 53 +++++++- .../src/__tests__/DdSdkReactNative.test.tsx | 68 +++++++++- .../AttributesSingleton.ts | 26 +++- .../__tests__/AttributesSingleton.test.ts | 60 +++++++-- packages/core/src/specs/NativeDdSdk.ts | 23 +++- packages/core/src/types.tsx | 21 ++- .../__mocks__/react-native.ts | 4 +- 24 files changed, 742 insertions(+), 75 deletions(-) diff --git a/benchmarks/src/testSetup/monitor.ts b/benchmarks/src/testSetup/monitor.ts index c7cc23473..93ea0fccd 100644 --- a/benchmarks/src/testSetup/monitor.ts +++ b/benchmarks/src/testSetup/monitor.ts @@ -4,8 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ -import { DefaultTimeProvider, RumActionType } from "@datadog/mobile-react-native"; -import { ErrorSource } from "@datadog/mobile-react-native/lib/typescript/rum/types"; +import { DefaultTimeProvider, ErrorSource, RumActionType } from "@datadog/mobile-react-native"; import type { DdRumType, ResourceKind } from "@datadog/mobile-react-native/lib/typescript/rum/types"; import type { GestureResponderEvent } from "react-native/types"; @@ -72,4 +71,4 @@ export const Monitor: Pick { DdLogs.info('The RN Sdk was properly initialized') DdSdkReactNative.setUserInfo({id: "1337", name: "Xavier", email: "xg@example.com", extraInfo: { type: "premium" } }) - DdSdkReactNative.setAttributes({campaign: "ad-network"}) + DdSdkReactNative.addAttributes({campaign: "ad-network"}) }); } diff --git a/packages/codepush/__mocks__/react-native.ts b/packages/codepush/__mocks__/react-native.ts index 046ced2f6..0c8189840 100644 --- a/packages/codepush/__mocks__/react-native.ts +++ b/packages/codepush/__mocks__/react-native.ts @@ -18,9 +18,18 @@ actualRN.NativeModules.DdSdk = { initialize: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, - setAttributes: jest.fn().mockImplementation( + addAttribute: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) - ) as jest.MockedFunction, + ) as jest.MockedFunction, + removeAttribute: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + addAttributes: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + removeAttributes: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, setTrackingConsent: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, diff --git a/packages/core/__mocks__/react-native.ts b/packages/core/__mocks__/react-native.ts index 0e85e65ae..24e3f80c7 100644 --- a/packages/core/__mocks__/react-native.ts +++ b/packages/core/__mocks__/react-native.ts @@ -27,9 +27,18 @@ actualRN.NativeModules.DdSdk = { clearUserInfo: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, - setAttributes: jest.fn().mockImplementation( + addAttribute: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) - ) as jest.MockedFunction, + ) as jest.MockedFunction, + removeAttribute: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + addAttributes: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + removeAttributes: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, setTrackingConsent: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index f781687eb..06151d834 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -89,11 +89,24 @@ internal class DatadogSDKWrapper : DatadogWrapper { override fun clearUserInfo() { Datadog.clearUserInfo() } + + override fun addRumGlobalAttribute(key: String, value: Any?) { + this.getRumMonitor().addAttribute(key, value) + } + + override fun removeRumGlobalAttribute(key: String) { + this.getRumMonitor().removeAttribute(key) + } override fun addRumGlobalAttributes(attributes: Map) { - val rumMonitor = this.getRumMonitor() for (attribute in attributes) { - rumMonitor.addAttribute(attribute.key, attribute.value) + this.addRumGlobalAttribute(attribute.key, attribute.value) + } + } + + override fun removeRumGlobalAttributes(keys: Array) { + for (key in keys) { + this.removeRumGlobalAttribute(key) } } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index 49d606b35..d6395b18b 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -91,6 +91,21 @@ interface DatadogWrapper { */ fun clearUserInfo() + + /** Adds a global attribute. + * + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + fun addRumGlobalAttribute(key: String, value: Any?) + + /** + * Removes a global attribute. + * + * @param key: Key that identifies the attribute. + */ + fun removeRumGlobalAttribute(key: String) + /** * Adds global attributes. * @@ -98,6 +113,13 @@ interface DatadogWrapper { */ fun addRumGlobalAttributes(attributes: Map) + /** + * Removes global attributes. + * + * @param keys Keys linked to the attributes to be removed + */ + fun removeRumGlobalAttributes(keys: Array) + /** * Sets tracking consent. */ diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 7adcf7438..ed545d9e1 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -14,6 +14,7 @@ import com.datadog.android.rum.configuration.VitalsUpdateFrequency import com.facebook.react.bridge.LifecycleEventListener import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext +import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import java.util.Locale import java.util.concurrent.TimeUnit @@ -66,11 +67,35 @@ class DdSdkImplementation( } /** - * Sets the global context (set of attributes) attached with all future Logs, Spans and RUM + * Sets a specific attribute in the global context attached with all future Logs, Spans and RUM. + * + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + fun addAttribute(key: String, value: ReadableMap, promise: Promise) { + val attributeValue = value.toMap()["value"] + datadog.addRumGlobalAttribute(key, attributeValue) + GlobalState.addAttribute(key, attributeValue) + promise.resolve(null) + } + + /** + * Removes an attribute from the global context attached with all future Logs, Spans and RUM events. + * @param key: They key associated with the attribute to be removed. + */ + fun removeAttribute(key: String, promise: Promise) { + datadog.removeRumGlobalAttribute(key) + GlobalState.removeAttribute(key) + promise.resolve(null) + } + + + /** + * Adds a set of attributes to the global context that is attached with all future Logs, Spans and RUM * events. - * @param attributes The global context attributes. + * @param attributes: The global context attributes. */ - fun setAttributes(attributes: ReadableMap, promise: Promise) { + fun addAttributes(attributes: ReadableMap, promise: Promise) { datadog.addRumGlobalAttributes(attributes.toHashMap()) for ((k,v) in attributes.toHashMap()) { GlobalState.addAttribute(k, v) @@ -78,6 +103,26 @@ class DdSdkImplementation( promise.resolve(null) } + /** + * Removes a set of attributes from the global context that is attached with all future Logs, Spans and RUM + * events. + * @param keys: They keys associated with the attributes to be removed. + */ + fun removeAttributes(keys: ReadableArray, promise: Promise) { + val keysArray = mutableListOf() + for (i in 0 until keys.size()) { + val key: String = keys.getString(i) + keysArray.add(key) + } + val keysStringArray = keysArray.toTypedArray() + + datadog.removeRumGlobalAttributes(keysStringArray) + for (key in keysStringArray) { + GlobalState.removeAttribute(key) + } + promise.resolve(null) + } + /** * Set the user information. * @param userInfo The user object (use builtin attributes: 'id', 'email', 'name', and any custom diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt index 4e4668a3e..a9d430081 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -12,13 +12,14 @@ import com.facebook.react.bridge.LifecycleEventListener import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import com.facebook.react.modules.core.DeviceEventManagerModule /** The entry point to initialize Datadog's features. */ class DdSdk( reactContext: ReactApplicationContext, - datadogWrapper: DatadogWrapper = DatadogSDKWrapper() + datadogWrapper: DatadogWrapper = DatadogSDKWrapper(), ddTelemetry: DdTelemetry = DdTelemetry() ) : NativeDdSdkSpec(reactContext) { @@ -40,13 +41,43 @@ class DdSdk( } /** - * Sets the global context (set of attributes) attached with all future Logs, Spans and RUM + * Sets a specific attribute in the global context attached with all future Logs, Spans and RUM + * + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + @ReactMethod + override fun addAttribute(key: String, value: ReadableMap, promise: Promise) { + implementation.addAttribute(key, value, promise) + } + + /** + * Removes an attribute from the context attached with all future Logs, Spans and RUM events. + * @param key: They key associated with the attribute to be removed. + */ + @ReactMethod + override fun removeAttribute(key: String, promise: Promise) { + implementation.removeAttribute(key, promise) + } + + /** + * Adds a set of attributes to the global context that is attached with all future Logs, Spans and RUM * events. * @param attributes The global context attributes. */ @ReactMethod - override fun setAttributes(attributes: ReadableMap, promise: Promise) { - implementation.setAttributes(attributes, promise) + override fun addAttributes(attributes: ReadableMap, promise: Promise) { + implementation.addAttributes(attributes, promise) + } + + /** + * Removes a set of attributes from the global context that is attached with all future Logs, Spans and RUM + * events. + * @param keys: They keys associated with the attributes to be removed. + */ + @ReactMethod + override fun removeAttributes(keys: ReadableArray, promise: Promise) { + implementation.removeAttributes(keys, promise) } /** diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 0ebdd37fb..958ba521b 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -12,6 +12,7 @@ import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap /** The entry point to initialize Datadog's features. */ @@ -66,13 +67,43 @@ class DdSdk( } /** - * Sets the global context (set of attributes) attached with all future Logs, Spans and RUM + * Sets a specific attribute in the global context attached with all future Logs, Spans and RUM + * + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + @ReactMethod + fun addAttribute(key: String, value: ReadableMap, promise: Promise) { + implementation.addAttribute(key, value, promise) + } + + /** + * Removes an attribute from the context attached with all future Logs, Spans and RUM events. + * @param key: They key associated with the attribute to be removed. + */ + @ReactMethod + fun removeAttribute(key: String, promise: Promise) { + implementation.removeAttribute(key, promise) + } + + /** + * Adds a set of attributes to the global context that is attached with all future Logs, Spans and RUM * events. * @param attributes The global context attributes. */ @ReactMethod - fun setAttributes(attributes: ReadableMap, promise: Promise) { - implementation.setAttributes(attributes, promise) + fun addAttributes(attributes: ReadableMap, promise: Promise) { + implementation.addAttributes(attributes, promise) + } + + /** + * Removes a set of attributes from the global context that is attached with all future Logs, Spans and RUM + * events. + * @param keys: They keys associated with the attributes to be removed. + */ + @ReactMethod + fun removeAttributes(keys: ReadableArray, promise: Promise) { + implementation.removeAttributes(keys, promise) } /** diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index f917bb847..ae6ded89c 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -40,6 +40,7 @@ import com.datadog.tools.unit.setStaticValue import com.datadog.tools.unit.toReadableArray import com.datadog.tools.unit.toReadableJavaOnlyMap import com.datadog.tools.unit.toReadableMap +import com.facebook.react.bridge.JavaOnlyMap import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableMap @@ -78,7 +79,6 @@ import org.mockito.kotlin.doReturn import org.mockito.kotlin.doThrow import org.mockito.kotlin.eq import org.mockito.kotlin.inOrder -import org.mockito.kotlin.isNotNull import org.mockito.kotlin.isNull import org.mockito.kotlin.mock import org.mockito.kotlin.never @@ -2966,28 +2966,96 @@ internal class DdSdkTest { } @Test - fun `𝕄 set RUM attributes 𝕎 setAttributes`( + fun `M set Rum attribute W addAttribute`( + @StringForgery(type = StringForgeryType.NUMERICAL) key: String, + @StringForgery(type = StringForgeryType.ASCII) value: String + ) { + // When + val attributeMap = JavaOnlyMap().apply { + putString("value", value) + } + testedBridgeSdk.addAttribute(key, attributeMap, mockPromise) + + // Then + verify(mockDatadog).addRumGlobalAttribute(key, value) + } + + @Test + fun `M set GlobalState attribute W addAttribute`( + @StringForgery(type = StringForgeryType.NUMERICAL) key: String, + @StringForgery(type = StringForgeryType.ASCII) value: String + ) { + // When + val attributeMap = JavaOnlyMap().apply { + putString("value", value) + } + testedBridgeSdk.addAttribute(key, attributeMap, mockPromise) + + // Then + assertThat(GlobalState.globalAttributes).containsEntry(key, value) + } + + @Test + fun `M remove Rum attribute W removeAttribute`( + @StringForgery(type = StringForgeryType.NUMERICAL) key: String, + @StringForgery(type = StringForgeryType.ASCII) value: String + ) { + // Given + val attributeMap = JavaOnlyMap().apply { + putString("value", value) + } + testedBridgeSdk.addAttribute(key, attributeMap, mockPromise) + assertThat(GlobalState.globalAttributes).containsEntry(key, value) + + // When + testedBridgeSdk.removeAttribute(key, mockPromise) + + // Then + verify(mockDatadog).removeRumGlobalAttribute(key) + } + + @Test + fun `M remove GlobalState attribute W removeAttribute`( + @StringForgery(type = StringForgeryType.NUMERICAL) key: String, + @StringForgery(type = StringForgeryType.ASCII) value: String + ) { + // Given + val attributeMap = JavaOnlyMap().apply { + putString("value", value) + } + testedBridgeSdk.addAttribute(key, attributeMap, mockPromise) + assertThat(GlobalState.globalAttributes).containsEntry(key, value) + + // When + testedBridgeSdk.removeAttribute(key, mockPromise) + + // Then + assertThat(GlobalState.globalAttributes).doesNotContainEntry(key, value) + } + + @Test + fun `𝕄 set RUM attributes 𝕎 addAttributes`( @MapForgery( key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) ) customAttributes: Map ) { // When - testedBridgeSdk.setAttributes(customAttributes.toReadableMap(), mockPromise) + testedBridgeSdk.addAttributes(customAttributes.toReadableMap(), mockPromise) // Then verify(mockDatadog).addRumGlobalAttributes(customAttributes) } @Test - fun `𝕄 set GlobalState attributes 𝕎 setAttributes`( + fun `𝕄 set GlobalState attributes 𝕎 addAttributes`( @MapForgery( key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) ) customAttributes: Map ) { // When - testedBridgeSdk.setAttributes(customAttributes.toReadableMap(), mockPromise) + testedBridgeSdk.addAttributes(customAttributes.toReadableMap(), mockPromise) // Then customAttributes.forEach { (k, v) -> @@ -2995,6 +3063,46 @@ internal class DdSdkTest { } } + @Test + fun `𝕄 remove RUM attributes 𝕎 removeAttributes`( + @MapForgery( + key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), + value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) + ) customAttributes: Map + ) { + // Given + testedBridgeSdk.addAttributes(customAttributes.toReadableMap(), mockPromise) + verify(mockDatadog).addRumGlobalAttributes(customAttributes) + + // When + val keys = customAttributes.keys.toReadableArray() + testedBridgeSdk.removeAttributes(keys, mockPromise) + + // Then + verify(mockDatadog).removeRumGlobalAttributes(customAttributes.keys.toTypedArray()) + } + + @Test + fun `𝕄 remve GlobalState attributes 𝕎 removeAttributes`( + @MapForgery( + key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), + value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) + ) customAttributes: Map + ) { + // Given + testedBridgeSdk.addAttributes(customAttributes.toReadableMap(), mockPromise) + verify(mockDatadog).addRumGlobalAttributes(customAttributes) + + // When + val keys = customAttributes.keys.toReadableArray() + testedBridgeSdk.removeAttributes(keys, mockPromise) + + // Then + customAttributes.forEach { (k, v) -> + assertThat(GlobalState.globalAttributes).doesNotContainEntry(k, v) + } + } + @Test fun `𝕄 build Granted consent 𝕎 buildTrackingConsent {granted}`(forge: Forge) { // When diff --git a/packages/core/ios/Sources/AnyEncodable.swift b/packages/core/ios/Sources/AnyEncodable.swift index 39821af87..7fac7bb3b 100644 --- a/packages/core/ios/Sources/AnyEncodable.swift +++ b/packages/core/ios/Sources/AnyEncodable.swift @@ -14,18 +14,25 @@ internal func castAttributesToSwift(_ attributes: [String: Any]) -> [String: Enc var casted: [String: Encodable] = [:] attributes.forEach { key, value in - if let castedValue = castByPreservingTypeInformation(attributeValue: value) { - // If possible, cast attribute by preserving its type information - casted[key] = castedValue - } else { - // Otherwise, cast by preserving its encoded value (and loosing type information) - casted[key] = castByPreservingEncodedValue(attributeValue: value) - } + casted[key] = castValueToSwift(value) } return casted } +internal func castValueToSwift(_ value: Any) -> Encodable { + var casted: Encodable + if let castedValue = castByPreservingTypeInformation(attributeValue: value) { + // If possible, cast attribute by preserving its type information + casted = castedValue + } else { + // Otherwise, cast by preserving its encoded value (and loosing type information) + casted = castByPreservingEncodedValue(attributeValue: value) + } + + return casted +} + /// Casts `Any` value to `Encodable` by preserving its type information. private func castByPreservingTypeInformation(attributeValue: Any) -> Encodable? { switch attributeValue { diff --git a/packages/core/ios/Sources/DdSdk.mm b/packages/core/ios/Sources/DdSdk.mm index 674cd0fbc..06736a69e 100644 --- a/packages/core/ios/Sources/DdSdk.mm +++ b/packages/core/ios/Sources/DdSdk.mm @@ -30,11 +30,33 @@ + (void)initFromNative { [self initialize:configuration resolve:resolve reject:reject]; } -RCT_REMAP_METHOD(setAttributes, withAttributes:(NSDictionary*)attributes +RCT_EXPORT_METHOD(addAttribute:(NSString*) key + withValue:(NSDictionary*) value + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self addAttribute:key value:value resolve:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(removeAttribute:(NSString*) key + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self removeAttribute:key resolve:resolve reject:reject]; +} + +RCT_REMAP_METHOD(addAttributes, withAttributes:(NSDictionary*)attributes withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) { - [self setAttributes:attributes resolve:resolve reject:reject]; + [self addAttributes:attributes resolve:resolve reject:reject]; +} + +RCT_REMAP_METHOD(removeAttributes, withKeys:(NSArray *)keys + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self removeAttributes:keys resolve:resolve reject:reject]; } RCT_REMAP_METHOD(setUserInfo, withUserInfo:(NSDictionary*)userInfo @@ -137,8 +159,20 @@ - (void)initialize:(NSDictionary *)configuration resolve:(RCTPromiseResolveBlock [self.ddSdkImplementation initializeWithConfiguration:configuration resolve:resolve reject:reject]; } -- (void)setAttributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { - [self.ddSdkImplementation setAttributesWithAttributes:attributes resolve:resolve reject:reject]; +- (void)addAttribute:(NSString *)key value:(NSDictionary *)value resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation addAttributeWithKey:key value:value resolve:resolve reject:reject]; +} + +- (void)removeAttribute:(NSString *)key resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation removeAttributeWithKey:key resolve:resolve reject:reject]; +} + +- (void)addAttributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation addAttributesWithAttributes:attributes resolve:resolve reject:reject]; +} + +- (void)removeAttributes:(NSArray *)keys resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation removeAttributesWithKeys:keys resolve:resolve reject:reject]; } - (void)setTrackingConsent:(NSString *)trackingConsent resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 03c630b70..8aae5e10d 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -75,14 +75,43 @@ public class DdSdkImplementation: NSObject { resolve(nil) } + + @objc + public func addAttribute(key: AttributeKey, value: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + if let attributeValue = value.object(forKey: "value") { + let castedValue = castValueToSwift(attributeValue) + RUMMonitorProvider().addAttribute(forKey: key, value: castedValue) + GlobalState.addAttribute(forKey: key, value: castedValue) + } + + resolve(nil) + } + + @objc + public func removeAttribute(key: AttributeKey, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + RUMMonitorProvider().removeAttribute(forKey: key) + GlobalState.removeAttribute(key: key) + + resolve(nil) + } @objc - public func setAttributes(attributes: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func addAttributes(attributes: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { let castedAttributes = castAttributesToSwift(attributes) for (key, value) in castedAttributes { RUMMonitorProvider().addAttribute(forKey: key, value: value) GlobalState.addAttribute(forKey: key, value: value) } + + resolve(nil) + } + + @objc + public func removeAttributes(keys: [AttributeKey], resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + RUMMonitorProvider().removeAttributes(forKeys: keys) + for (key) in keys { + GlobalState.removeAttribute(key: key) + } resolve(nil) } diff --git a/packages/core/ios/Sources/GlobalState.swift b/packages/core/ios/Sources/GlobalState.swift index b932803a1..a758bf0ef 100644 --- a/packages/core/ios/Sources/GlobalState.swift +++ b/packages/core/ios/Sources/GlobalState.swift @@ -15,7 +15,7 @@ internal struct GlobalState { } internal static func removeAttribute(key: String) { - GlobalState.globalAttributes.removeValue(forKey: key) + GlobalState.globalAttributes[key] = nil } } diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index efbf57b96..adbb57da9 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -651,7 +651,7 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } - + func testClearUserInfo() throws { let bridge = DdSdkImplementation( mainDispatchQueue: DispatchQueueMock(), @@ -704,12 +704,12 @@ class DdSdkTests: XCTestCase { } else { XCTFail("extra-info-4 is not of expected type or value") } - + bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) - + ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() userInfo = try XCTUnwrap(ddContext.userInfo) - + XCTAssertEqual(userInfo.id, nil) XCTAssertEqual(userInfo.name, nil) XCTAssertEqual(userInfo.email, nil) @@ -719,7 +719,59 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) } - func testSettingAttributes() { + func testRemovingAttribute() { + let rumMonitorMock = MockRUMMonitor() + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { nil } + ) + + bridge.initialize( + configuration: .mockAny(), + resolve: mockResolve, + reject: mockReject + ) + + bridge.addAttributes( + attributes: NSDictionary( + dictionary: [ + "attribute-1": 123, + "attribute-2": "abc", + ] + ), + resolve: mockResolve, + reject: mockReject + ) + + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, "abc") + + XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, "abc") + + bridge.removeAttribute(key: "attribute-1", resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, "abc") + + XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, "abc") + + bridge.removeAttribute(key: "attribute-2", resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, nil) + + XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, nil) + + GlobalState.globalAttributes.removeAll() + } + + func testAddingAttributes() { let rumMonitorMock = MockRUMMonitor() let bridge = DdSdkImplementation( mainDispatchQueue: DispatchQueueMock(), @@ -734,7 +786,7 @@ class DdSdkTests: XCTestCase { reject: mockReject ) - bridge.setAttributes( + bridge.addAttributes( attributes: NSDictionary( dictionary: [ "attribute-1": 123, @@ -757,6 +809,66 @@ class DdSdkTests: XCTestCase { GlobalState.globalAttributes.removeAll() } + func testRemovingAttributes() { + let rumMonitorMock = MockRUMMonitor() + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { nil } + ) + bridge.initialize( + configuration: .mockAny(), + resolve: mockResolve, + reject: mockReject + ) + + bridge.addAttributes( + attributes: NSDictionary( + dictionary: [ + "attribute-1": 123, + "attribute-2": "abc", + "attribute-3": true, + ] + ), + resolve: mockResolve, + reject: mockReject + ) + + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, "abc") + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-3"] as? Bool, true) + + XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, "abc") + XCTAssertEqual(GlobalState.globalAttributes["attribute-3"] as? Bool, true) + + bridge.removeAttributes( + keys: ["attribute-1", "attribute-2"], resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, nil) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-3"] as? Bool, true) + + XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, nil) + XCTAssertEqual(GlobalState.globalAttributes["attribute-3"] as? Bool, true) + + bridge.removeAttributes(keys: ["attribute-3"], resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, nil) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-3"] as? Bool, nil) + + XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, nil) + XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, nil) + XCTAssertEqual(GlobalState.globalAttributes["attribute-3"] as? Bool, nil) + + GlobalState.globalAttributes.removeAll() + + } + func testBuildLongTaskThreshold() { let configuration: DdSdkConfiguration = .mockAny(nativeLongTaskThresholdMs: 2500) diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index f0fa03364..3a882ed47 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -38,14 +38,20 @@ internal class MockRUMMonitor: RUMMonitorProtocol { addedAttributes[key] = value } - func removeAttribute(forKey key: DatadogInternal.AttributeKey) {} + func removeAttribute(forKey key: DatadogInternal.AttributeKey) { + addedAttributes.removeValue(forKey: key) + } func addAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { - // Not implemented + for (key, value) in attributes { + addAttribute(forKey: key, value: value) + } } func removeAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { - // Not implemented + for key in keys { + removeAttribute(forKey: key) + } } var debug: Bool diff --git a/packages/core/jest/mock.js b/packages/core/jest/mock.js index 8e154c4cd..c49d13f48 100644 --- a/packages/core/jest/mock.js +++ b/packages/core/jest/mock.js @@ -36,7 +36,16 @@ module.exports = { clearUserInfo: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), - setAttributes: jest + addAttribute: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + removeAttribute: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + addAttributes: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + removeAttributes: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), setTrackingConsent: jest diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index 1838542df..8360a695b 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -175,20 +175,61 @@ export class DdSdkReactNative { ); }; + /** + * Adds a specific attribute to the global context attached with all future Logs, Spans and RUM. + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + static addAttribute = async ( + key: string, + value: unknown + ): Promise => { + InternalLog.log( + `Adding attribute ${JSON.stringify(value)} for key ${key}`, + SdkVerbosity.DEBUG + ); + await DdSdk.addAttribute(key, { value }); + AttributesSingleton.getInstance().addAttribute(key, value); + }; + + /** + * Removes an attribute from the context attached with all future Logs, Spans and RUM events. + * @param key: They key associated with the attribute to be removed. + */ + static removeAttribute = async (key: string): Promise => { + InternalLog.log( + `Removing attribute for key ${key}`, + SdkVerbosity.DEBUG + ); + await DdSdk.removeAttribute(key); + AttributesSingleton.getInstance().removeAttribute(key); + }; + /** * Adds a set of attributes to the global context attached with all future Logs, Spans and RUM events. - * To remove an attribute, set it to `undefined` in a call to `setAttributes`. * @param attributes: The global context attributes. * @returns a Promise. */ - // eslint-disable-next-line @typescript-eslint/ban-types - static setAttributes = async (attributes: Attributes): Promise => { + static addAttributes = async (attributes: Attributes): Promise => { + InternalLog.log( + `Adding attributes ${JSON.stringify(attributes)}`, + SdkVerbosity.DEBUG + ); + await DdSdk.addAttributes(attributes); + AttributesSingleton.getInstance().addAttributes(attributes); + }; + + /** + * Removes a set of attributes from the context attached with all future Logs, Spans and RUM events. + * @param keys: They keys associated with the attributes to be removed. + */ + static removeAttributes = async (keys: string[]): Promise => { InternalLog.log( - `Setting attributes ${JSON.stringify(attributes)}`, + `Removing attributes for keys ${JSON.stringify(keys)}`, SdkVerbosity.DEBUG ); - await DdSdk.setAttributes(attributes); - AttributesSingleton.getInstance().setAttributes(attributes); + await DdSdk.removeAttributes(keys); + AttributesSingleton.getInstance().removeAttributes(keys); }; /** diff --git a/packages/core/src/__tests__/DdSdkReactNative.test.tsx b/packages/core/src/__tests__/DdSdkReactNative.test.tsx index 57ff5d0ed..5e6f8c447 100644 --- a/packages/core/src/__tests__/DdSdkReactNative.test.tsx +++ b/packages/core/src/__tests__/DdSdkReactNative.test.tsx @@ -62,7 +62,7 @@ beforeEach(async () => { GlobalState.instance.isInitialized = false; DdSdkReactNative['wasAutoInstrumented'] = false; NativeModules.DdSdk.initialize.mockClear(); - NativeModules.DdSdk.setAttributes.mockClear(); + NativeModules.DdSdk.addAttributes.mockClear(); NativeModules.DdSdk.setTrackingConsent.mockClear(); NativeModules.DdSdk.onRUMSessionStarted.mockClear(); @@ -1045,24 +1045,80 @@ describe('DdSdkReactNative', () => { }); }); - describe('setAttributes', () => { - it('calls SDK method when setAttributes', async () => { + describe('addAttribute', () => { + it('calls SDK method when addAttribute', async () => { + // GIVEN + const key = 'foo'; + const value = 'bar'; + + // WHEN + + await DdSdkReactNative.addAttribute(key, value); + + // THEN + expect(DdSdk.addAttribute).toHaveBeenCalledTimes(1); + expect(DdSdk.addAttribute).toHaveBeenCalledWith(key, { value }); + expect(AttributesSingleton.getInstance().getAttribute(key)).toEqual( + value + ); + }); + }); + + describe('removeAttribute', () => { + it('calls SDK method when removeAttribute', async () => { + // GIVEN + const key = 'foo'; + const value = 'bar'; + await DdSdkReactNative.addAttribute(key, value); + + // WHEN + await DdSdkReactNative.removeAttribute(key); + + // THEN + expect(DdSdk.removeAttribute).toHaveBeenCalledTimes(1); + expect(DdSdk.removeAttribute).toHaveBeenCalledWith(key); + expect(AttributesSingleton.getInstance().getAttribute(key)).toEqual( + undefined + ); + }); + }); + + describe('addAttributes', () => { + it('calls SDK method when addAttributes', async () => { // GIVEN const attributes = { foo: 'bar' }; // WHEN - await DdSdkReactNative.setAttributes(attributes); + await DdSdkReactNative.addAttributes(attributes); // THEN - expect(DdSdk.setAttributes).toHaveBeenCalledTimes(1); - expect(DdSdk.setAttributes).toHaveBeenCalledWith(attributes); + expect(DdSdk.addAttributes).toHaveBeenCalledTimes(1); + expect(DdSdk.addAttributes).toHaveBeenCalledWith(attributes); expect(AttributesSingleton.getInstance().getAttributes()).toEqual({ foo: 'bar' }); }); }); + describe('removeAttributes', () => { + it('calls SDK method when removeAttributes', async () => { + // GIVEN + const attributes = { foo: 'bar', baz: 'quux' }; + await DdSdkReactNative.addAttributes(attributes); + + // WHEN + await DdSdkReactNative.removeAttributes(['foo', 'baz']); + + // THEN + expect(DdSdk.removeAttributes).toHaveBeenCalledTimes(1); + expect(DdSdk.removeAttributes).toHaveBeenCalledWith(['foo', 'baz']); + expect(AttributesSingleton.getInstance().getAttributes()).toEqual( + {} + ); + }); + }); + describe('setUserInfo', () => { it('calls SDK method when setUserInfo, and sets the user in UserProvider', async () => { // GIVEN diff --git a/packages/core/src/sdk/AttributesSingleton/AttributesSingleton.ts b/packages/core/src/sdk/AttributesSingleton/AttributesSingleton.ts index a51bb6c99..ac92c2d32 100644 --- a/packages/core/src/sdk/AttributesSingleton/AttributesSingleton.ts +++ b/packages/core/src/sdk/AttributesSingleton/AttributesSingleton.ts @@ -9,13 +9,37 @@ import type { Attributes } from './types'; class AttributesProvider { private attributes: Attributes = {}; - setAttributes = (attributes: Attributes) => { + addAttribute = (key: string, value: unknown) => { + const newAttributes = { ...this.attributes }; + newAttributes[key] = value; + this.attributes = newAttributes; + }; + + removeAttribute = (key: string) => { + const updatedAttributes = { ...this.attributes }; + delete updatedAttributes[key]; + this.attributes = updatedAttributes; + }; + + addAttributes = (attributes: Attributes) => { this.attributes = { ...this.attributes, ...attributes }; }; + removeAttributes = (keys: string[]) => { + const updated = { ...this.attributes }; + for (const k of keys) { + delete updated[k]; + } + this.attributes = updated; + }; + + getAttribute = (key: string): unknown | undefined => { + return this.attributes[key]; + }; + getAttributes = (): Attributes => { return this.attributes; }; diff --git a/packages/core/src/sdk/AttributesSingleton/__tests__/AttributesSingleton.test.ts b/packages/core/src/sdk/AttributesSingleton/__tests__/AttributesSingleton.test.ts index 23fbe5ad7..90d1133b4 100644 --- a/packages/core/src/sdk/AttributesSingleton/__tests__/AttributesSingleton.test.ts +++ b/packages/core/src/sdk/AttributesSingleton/__tests__/AttributesSingleton.test.ts @@ -7,9 +7,12 @@ import { AttributesSingleton } from '../AttributesSingleton'; describe('AttributesSingleton', () => { - it('adds, returns and resets the user info', () => { - // Adding first attributes - AttributesSingleton.getInstance().setAttributes({ + beforeEach(() => { + AttributesSingleton.reset(); + }); + + it('adds, returns and resets the attributes', () => { + AttributesSingleton.getInstance().addAttributes({ appType: 'student', extraInfo: { loggedIn: true @@ -23,11 +26,8 @@ describe('AttributesSingleton', () => { } }); - // Removing and adding new attributes - AttributesSingleton.getInstance().setAttributes({ - appType: undefined, - newAttribute: false - }); + AttributesSingleton.getInstance().removeAttribute('appType'); + AttributesSingleton.getInstance().addAttribute('newAttribute', false); expect(AttributesSingleton.getInstance().getAttributes()).toEqual({ newAttribute: false, @@ -41,4 +41,48 @@ describe('AttributesSingleton', () => { expect(AttributesSingleton.getInstance().getAttributes()).toEqual({}); }); + + it('addAttribute sets a single key and getAttribute returns it', () => { + AttributesSingleton.getInstance().addAttribute('userId', '123'); + expect(AttributesSingleton.getInstance().getAttribute('userId')).toBe( + '123' + ); + expect(AttributesSingleton.getInstance().getAttributes()).toEqual({ + userId: '123' + }); + }); + + it('removeAttribute removes a single key and leaves others intact', () => { + AttributesSingleton.getInstance().addAttributes({ + a: 1, + b: 2 + }); + + AttributesSingleton.getInstance().removeAttribute('a'); + + expect( + AttributesSingleton.getInstance().getAttribute('a') + ).toBeUndefined(); + expect(AttributesSingleton.getInstance().getAttributes()).toEqual({ + b: 2 + }); + }); + + it('removeAttributes removes multiple keys (missing keys are ignored)', () => { + AttributesSingleton.getInstance().addAttributes({ + keyToKeep: 'yes', + keyToRemove1: true, + keyToRemove2: false + }); + + AttributesSingleton.getInstance().removeAttributes([ + 'keyToRemove1', + 'keyToRemove2', + 'keyToIgnore' + ]); + + expect(AttributesSingleton.getInstance().getAttributes()).toEqual({ + keyToKeep: 'yes' + }); + }); }); diff --git a/packages/core/src/specs/NativeDdSdk.ts b/packages/core/src/specs/NativeDdSdk.ts index a2ce1120e..70401fe3c 100644 --- a/packages/core/src/specs/NativeDdSdk.ts +++ b/packages/core/src/specs/NativeDdSdk.ts @@ -26,10 +26,29 @@ export interface Spec extends TurboModule { initialize(configuration: Object): Promise; /** - * Sets the global context (set of attributes) attached with all future Logs, Spans and RUM events. + * Adds a specific attribute to the global context attached with all future Logs, Spans and RUM. + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + addAttribute(key: string, value: Object): Promise; + + /** + * Removes an attribute from the context attached with all future Logs, Spans and RUM events. + * @param key: They key associated with the attribute to be removed. + */ + removeAttribute(key: string): Promise; + + /** + * Adds the global context (set of attributes) attached with all future Logs, Spans and RUM events. * @param attributes: The global context attributes. */ - setAttributes(attributes: Object): Promise; + addAttributes(attributes: Object): Promise; + + /** + * Removes a set of attributes from the context attached with all future Logs, Spans and RUM events. + * @param keys: They keys associated with the attributes to be removed. + */ + removeAttributes(keys: string[]): Promise; /** * Set the user information. diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index bad19d429..c8d9821cc 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -83,11 +83,30 @@ export type DdSdkType = { */ initialize(configuration: DdSdkConfiguration): Promise; + /** + * Sets a specific attribute in the global context attached with all future Logs, Spans and RUM + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + addAttribute(key: string, value: object): Promise; + + /** + * Removes an attribute from the context attached with all future Logs, Spans and RUM events. + * @param key: They key associated with the attribute to be removed. + */ + removeAttribute(key: string): Promise; + /** * Sets the global context (set of attributes) attached with all future Logs, Spans and RUM events. * @param attributes: The global context attributes. */ - setAttributes(attributes: object): Promise; + addAttributes(attributes: object): Promise; + + /** + * Removes a set of attributes from the context attached with all future Logs, Spans and RUM events. + * @param keys: They keys associated with the attributes to be removed. + */ + removeAttributes(keys: string[]): Promise; /** * Sets the user information. diff --git a/packages/react-native-apollo-client/__mocks__/react-native.ts b/packages/react-native-apollo-client/__mocks__/react-native.ts index 046ced2f6..bbac607d3 100644 --- a/packages/react-native-apollo-client/__mocks__/react-native.ts +++ b/packages/react-native-apollo-client/__mocks__/react-native.ts @@ -18,9 +18,9 @@ actualRN.NativeModules.DdSdk = { initialize: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, - setAttributes: jest.fn().mockImplementation( + addAttributes: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) - ) as jest.MockedFunction, + ) as jest.MockedFunction, setTrackingConsent: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, From 2fdddf99e162e27fbc063833626403ae4f1bea46 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 16 Oct 2025 11:01:21 +0200 Subject: [PATCH 031/168] JS refresh rate normalization --- .../reactnative/DdSdkImplementation.kt | 51 ++++++- .../com/datadog/reactnative/DdSdkTest.kt | 127 ++++++++++++++++++ .../ios/Sources/DdSdkImplementation.swift | 21 ++- packages/core/ios/Tests/DdSdkTests.swift | 108 +++++++++++++++ 4 files changed, 305 insertions(+), 2 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index ed545d9e1..574394a36 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -7,7 +7,10 @@ package com.datadog.reactnative import android.content.Context +import android.hardware.display.DisplayManager +import android.os.Build import android.util.Log +import android.view.Display import com.datadog.android.privacy.TrackingConsent import com.datadog.android.rum.RumPerformanceMetric import com.datadog.android.rum.configuration.VitalsUpdateFrequency @@ -19,6 +22,7 @@ import com.facebook.react.bridge.ReadableMap import java.util.Locale import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean +import kotlin.math.max /** The entry point to initialize Datadog's features. */ @Suppress("TooManyFunctions") @@ -293,9 +297,10 @@ class DdSdkImplementation( return { if (jsRefreshRateMonitoringEnabled && it > 0.0) { + val normalizedFrameTimeSeconds = normalizeFrameTime(it, appContext) datadog.getRumMonitor() ._getInternal() - ?.updatePerformanceMetric(RumPerformanceMetric.JS_FRAME_TIME, it) + ?.updatePerformanceMetric(RumPerformanceMetric.JS_FRAME_TIME, normalizedFrameTimeSeconds) } if (jsLongTasksMonitoringEnabled && it > @@ -308,6 +313,49 @@ class DdSdkImplementation( } } + /** + * Normalizes frameTime values so when are turned into FPS metrics they are normalized on a range of zero to 60fps. + * @param frameTimeSeconds: the frame time to normalize. In seconds. + * @param context: The current app context + * @param fpsBudget: The maximum fps under which the frame Time will be normalized [0-fpsBudget]. Defaults to 60Hz. + * @param deviceDisplayFps: The maximum fps supported by the device. If not provided it will be set from the value obtained from the app context. + */ + @Suppress("CyclomaticComplexMethod") + fun normalizeFrameTime( + frameTimeSeconds: Double, + context: Context, + fpsBudget: Double? = null, + deviceDisplayFps: Double? = null, + ) : Double { + val frameTimeMs = frameTimeSeconds * 1000.0 + val frameBudgetHz = fpsBudget ?: DEFAULT_REFRESH_HZ + val maxDeviceDisplayHz = deviceDisplayFps ?: getMaxDisplayRefreshRate(context) + ?: 60.0 + + val maxDeviceFrameTimeMs = 1000.0 / maxDeviceDisplayHz + val budgetFrameTimeMs = 1000.0 / frameBudgetHz + + if (listOf( + maxDeviceDisplayHz, frameTimeMs, frameBudgetHz, budgetFrameTimeMs, maxDeviceFrameTimeMs + ).any { !it.isFinite() || it <= 0.0 } + ) return 1.0 / DEFAULT_REFRESH_HZ + + + var normalizedFrameTimeMs = frameTimeMs / (maxDeviceFrameTimeMs / budgetFrameTimeMs) + + normalizedFrameTimeMs = max(normalizedFrameTimeMs, maxDeviceFrameTimeMs) + + return normalizedFrameTimeMs / 1000.0 // in seconds + } + + @Suppress("CyclomaticComplexMethod") + private fun getMaxDisplayRefreshRate(context: Context?): Double { + val dm = context?.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: return 60.0 + val display: Display = dm.getDisplay(Display.DEFAULT_DISPLAY) ?: return DEFAULT_REFRESH_HZ + + return display.supportedModes.maxOf { it.refreshRate.toDouble() } + } + // endregion internal companion object { internal const val DEFAULT_APP_VERSION = "?" @@ -317,6 +365,7 @@ class DdSdkImplementation( internal const val DD_DROP_ACTION = "_dd.action.drop_action" internal const val MONITOR_JS_ERROR_MESSAGE = "Error monitoring JS refresh rate" internal const val PACKAGE_INFO_NOT_FOUND_ERROR_MESSAGE = "Error getting package info" + internal const val DEFAULT_REFRESH_HZ = 60.0 internal const val NAME = "DdSdk" } } diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index ae6ded89c..327d8ffc0 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -60,6 +60,7 @@ import java.util.Locale import java.util.stream.Stream import kotlin.time.Duration.Companion.seconds import org.assertj.core.api.Assertions.assertThat +import org.assertj.core.data.Offset import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -3238,6 +3239,132 @@ internal class DdSdkTest { } } + @Test + fun `𝕄 normalize frameTime according to the device's refresh rate`() { + // 10 fps, 60Hz device, 60 fps budget -> 10 fps + var frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.1, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 60.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.1) + + // 30 fps, 60Hz device, 60 fps budget -> 30 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.03, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 60.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.03) + + // 60 fps, 60Hz device, 60 fps budget -> 60 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.016, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 60.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.016, Offset.offset(0.005)) + + // 60 fps, 120Hz device, 60 fps budget -> 30 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.016, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 120.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.032) + + // 120 fps, 120Hz device, 60 fps budget -> 60 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.0083, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 120.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.016, Offset.offset(0.005)) + + // 90 fps, 120Hz device, 60 fps budget -> 45 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.0111, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 120.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.0222, Offset.offset(0.001)) + + // 100 fps, 120Hz device, 60 fps budget -> 50 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.01, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 120.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.02, Offset.offset(0.001)) + + // 120 fps, 120Hz device, 120 fps budget -> 120 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.0083, + context = mockContext, + fpsBudget = 120.0, + deviceDisplayFps = 120.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.0083, Offset.offset(0.001)) + + // 80 fps, 160Hz device, 60 fps budget -> 30 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.0125, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 160.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.033, Offset.offset(0.001)) + + // 160 fps, 160Hz device, 60 fps budget -> 60 fps + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.00625, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 160.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.016, Offset.offset(0.001)) + + // Edge cases + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.0, + context = mockContext, + fpsBudget = 0.0, + deviceDisplayFps = 0.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.016, Offset.offset(0.001)) + + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.016, + context = mockContext, + fpsBudget = 0.0, + deviceDisplayFps = 0.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.016, Offset.offset(0.001)) + + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.016, + context = mockContext, + fpsBudget = 60.0, + deviceDisplayFps = 0.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.016, Offset.offset(0.001)) + + frameTimeSeconds = testedBridgeSdk.normalizeFrameTime( + frameTimeSeconds = 0.016, + context = mockContext, + fpsBudget = 0.0, + deviceDisplayFps = 60.0 + ) + assertThat(frameTimeSeconds).isEqualTo(0.016, Offset.offset(0.001)) + } + // endregion // region Internal diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 8aae5e10d..437b5ee39 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -244,7 +244,8 @@ public class DdSdkImplementation: NSObject { // Leave JS thread ASAP to give as much time to JS engine work. sharedQueue.async { if (shouldRecordFrameTime) { - rumMonitorInternal.updatePerformanceMetric(at: now, metric: .jsFrameTimeSeconds, value: frameTime, attributes: [:]) + let normalizedFrameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(frameTime) + rumMonitorInternal.updatePerformanceMetric(at: now, metric: .jsFrameTimeSeconds, value: normalizedFrameTimeSeconds, attributes: [:]) } if (shouldRecordLongTask) { rumMonitorInternal.addLongTask(at: now, duration: frameTime, attributes: ["long_task.target": "javascript"]) @@ -254,5 +255,23 @@ public class DdSdkImplementation: NSObject { return frameTimeCallback } + + // Normalizes frameTime values so when they are turned into FPS metrics they are normalized on a range between 0 and fpsBudget. If fpsBudget is not provided it will default to 60hz. + public static func normalizeFrameTimeForDeviceRefreshRate(_ frameTime: Double, fpsBudget: Double? = nil, deviceDisplayFps: Double? = nil) -> Double { + let DEFAULT_REFRESH_HZ = 60.0 + let frameTimeMs: Double = frameTime * 1000.0 + let frameBudgetHz: Double = fpsBudget ?? DEFAULT_REFRESH_HZ + let maxDeviceDisplayHz = deviceDisplayFps ?? Double(UIScreen.main.maximumFramesPerSecond) + let maxDeviceFrameTimeMs = 1000.0 / maxDeviceDisplayHz + let budgetFrameTimeMs = 1000.0 / frameBudgetHz + + guard maxDeviceDisplayHz > 0, frameTimeMs.isFinite, frameTimeMs > 0, frameBudgetHz > 0, budgetFrameTimeMs.isFinite, budgetFrameTimeMs > 0, maxDeviceFrameTimeMs.isFinite, maxDeviceFrameTimeMs > 0 else { + return 1.0 / DEFAULT_REFRESH_HZ + } + + var normalizedFrameTimeMs = frameTimeMs / (maxDeviceFrameTimeMs / budgetFrameTimeMs) + normalizedFrameTimeMs = max(normalizedFrameTimeMs, maxDeviceFrameTimeMs) + return normalizedFrameTimeMs / 1000.0 // in seconds + } } diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index adbb57da9..4a5d13f2e 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -1181,6 +1181,114 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(rumMonitorMock.receivedLongTasks.first?.value, 0.25) XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.25) } + + func testFrameTimeNormalizationFromCallback() { + let mockRefreshRateMonitor = MockJSRefreshRateMonitor() + let rumMonitorMock = MockRUMMonitor() + + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: mockRefreshRateMonitor, + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { rumMonitorMock._internalMock } + ).initialize( + configuration: .mockAny( + longTaskThresholdMs: 200, + vitalsUpdateFrequency: "average" + ), + resolve: mockResolve, + reject: mockReject + ) + + XCTAssertTrue(mockRefreshRateMonitor.isStarted) + + // 10 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.1) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.1) + + // 30 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.03) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.03) + + // 45 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.02) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.02) + + // 60 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.016) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) + + // 90 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.011) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) + + // 120 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.008) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) + } + + func testFrameTimeNormalizationUtilityFunction() { + + // 10 fps, 60fps capable device, 60 fps budget -> Normalized to 10fps + var frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.1, fpsBudget: 60.0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.1, accuracy: 0.01) + + // 30 fps, 60fps capable device, 60 fps budget -> Normalized to 30fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.03, fpsBudget: 60.0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.03, accuracy: 0.01) + + // 60 fps, 60fps capable device, 60 fps budget-> Normalized to 60fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.01) + + // 60 fps, 120fps capable device, 60 fps budget -> Normalized to 30fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.03, accuracy: 0.01) + + // 120 fps, 120fps capable device, 60 fps budget -> Normalized to 60fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0083, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + // 90 fps, 120fps capable device, 60 fps budget -> Normalized to 45fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0111, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.0222, accuracy: 0.001) + + // 100 fps, 120fps capable device, 60 fps budget -> Normalized to 50fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.01, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.02, accuracy: 0.001) + + // 120 fps, 120fps capable device, 120 fps budget -> Normalized to 120fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0083, fpsBudget: 120.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.0083, accuracy: 0.001) + + // 80 fps, 160fps capable device, 60 fps budget -> Normalized to 30fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0125, fpsBudget: 60.0, deviceDisplayFps: 160.0) + XCTAssertEqual(frameTimeSeconds, 0.033, accuracy: 0.001) + + // 160 fps, 160fps capable device, 60 fps budget -> Normalized to 60fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.00625, fpsBudget: 60.0, deviceDisplayFps: 160.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + // Edge cases + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0, fpsBudget: 0, deviceDisplayFps: 0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 0, deviceDisplayFps: 0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + } func testSDKInitializationWithCustomEndpoints() throws { let mockRefreshRateMonitor = MockJSRefreshRateMonitor() From 88baa6680e187d2bbb100677fdd70cf1349224da Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 10 Oct 2025 14:52:21 +0200 Subject: [PATCH 032/168] Expose view Attributes API --- packages/core/__mocks__/react-native.ts | 12 ++ .../reactnative/DdRumImplementation.kt | 44 ++++++ .../reactnative/DdSdkImplementation.kt | 2 - .../kotlin/com/datadog/reactnative/DdRum.kt | 38 ++++++ .../kotlin/com/datadog/reactnative/DdRum.kt | 38 ++++++ .../com/datadog/reactnative/DdRumTest.kt | 58 ++++++++ .../com/datadog/tools/unit/MockRumMonitor.kt | 17 +-- packages/core/ios/Sources/DdRum.mm | 47 ++++++- .../ios/Sources/DdRumImplementation.swift | 28 ++++ .../ios/Sources/DdSdkImplementation.swift | 129 ++++++++++++------ packages/core/ios/Tests/DdRumTests.swift | 59 ++++++++ packages/core/ios/Tests/MockRUMMonitor.swift | 38 +++--- packages/core/jest/mock.js | 12 ++ packages/core/src/rum/DdRum.ts | 45 ++++++ packages/core/src/rum/__tests__/DdRum.test.ts | 92 +++++++++++++ packages/core/src/rum/types.ts | 26 ++++ packages/core/src/specs/NativeDdRum.ts | 25 ++++ 17 files changed, 638 insertions(+), 72 deletions(-) diff --git a/packages/core/__mocks__/react-native.ts b/packages/core/__mocks__/react-native.ts index 24e3f80c7..73308f711 100644 --- a/packages/core/__mocks__/react-native.ts +++ b/packages/core/__mocks__/react-native.ts @@ -132,6 +132,18 @@ actualRN.NativeModules.DdRum = { addTiming: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, + addViewAttribute: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + removeViewAttribute: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + addViewAttributes: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + removeViewAttributes: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, addViewLoadingTime: jest.fn().mockImplementation( () => new Promise(resolve => resolve()) ) as jest.MockedFunction, diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt index 013872c08..4e3cd416f 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt @@ -13,6 +13,7 @@ import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod import com.facebook.react.bridge.Promise +import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap import java.util.Locale @@ -248,6 +249,49 @@ class DdRumImplementation(private val datadog: DatadogWrapper = DatadogSDKWrappe promise.resolve(null) } + /** + * Adds a custom attribute to the active RUM View. It will be propagated to all future RUM events associated with the active View. + * @param key: key for this view attribute. + * @param value: value for this attribute. + */ + fun addViewAttribute(key: String, value: ReadableMap, promise: Promise) { + val attributeValue = value.toMap()["value"] + val attributes = mutableMapOf() + attributes[key] = attributeValue + datadog.getRumMonitor().addViewAttributes(attributes) + promise.resolve(null) + } + + /** + * Removes an attribute from the active RUM View. + * @param key: key for the attribute to be removed from the view. + */ + fun removeViewAttribute(key: String, promise: Promise) { + val keysToDelete: Collection = listOf(key) + datadog.getRumMonitor().removeViewAttributes(keysToDelete) + promise.resolve(null) + } + + /** + * Adds multiple attributes to the active RUM View. They will be propagated to all future RUM events associated with the active View. + * @param attributes: key/value object containing all attributes to be added to the view. + */ + fun addViewAttributes(attributes: ReadableMap, promise: Promise) { + datadog.getRumMonitor().addViewAttributes(attributes.toMap()) + promise.resolve(null) + } + + /** + * Removes multiple attributes from the active RUM View. + * @param keys: keys for the attributes to be removed from the view. + */ + fun removeViewAttributes(keys: ReadableArray, promise: Promise) { + val keysToDelete = (0 until keys.size()) + .mapNotNull { keys.getString(it) } + datadog.getRumMonitor().removeViewAttributes(keysToDelete) + promise.resolve(null) + } + /** * Adds the loading time of the view to the active view. * It is calculated as the difference between the current time and the start time of the view. diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 574394a36..6741e971e 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -143,8 +143,6 @@ class DdSdkImplementation( if (id != null) { datadog.setUserInfo(id, name, email, extraInfo) - } else { - // TO DO - Log warning? } promise.resolve(null) diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt index ce8104685..6cb2b385b 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt @@ -9,6 +9,7 @@ package com.datadog.reactnative import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap /** @@ -201,6 +202,43 @@ class DdRum( implementation.addTiming(name, promise) } + /** + * Adds a custom attribute to the active RUM View. It will be propagated to all future RUM events associated with the active View. + * @param key: key for this view attribute. + * @param value: value for this attribute. + */ + @ReactMethod + override fun addViewAttribute(key: String, value: ReadableMap, promise: Promise) { + implementation.addViewAttribute(key, value, promise) + } + + /** + * Removes an attribute from the active RUM View. + * @param key: key for the attribute to be removed from the view. + */ + @ReactMethod + override fun removeViewAttribute(key: String, promise: Promise) { + implementation.removeViewAttribute(key, promise) + } + + /** + * Adds multiple attributes to the active RUM View. They will be propagated to all future RUM events associated with the active View. + * @param attributes: key/value object containing all attributes to be added to the view. + */ + @ReactMethod + override fun addViewAttributes(attributes: ReadableMap, promise: Promise) { + implementation.addViewAttributes(attributes, promise) + } + + /** + * Removes multiple attributes from the active RUM View. + * @param keys: keys for the attributes to be removed from the view. + */ + @ReactMethod + override fun removeViewAttributes(keys: ReadableArray, promise: Promise) { + implementation.removeViewAttributes(keys, promise) + } + /** * Adds the loading time of the view to the active view. * It is calculated as the difference between the current time and the start time of the view. diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt index 79742e854..a6c4965ea 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt @@ -10,6 +10,7 @@ import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod +import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap /** @@ -192,6 +193,43 @@ class DdRum( implementation.addTiming(name, promise) } + /** + * Adds a custom attribute to the active RUM View. It will be propagated to all future RUM events associated with the active View. + * @param key: key for this view attribute. + * @param value: value for this attribute. + */ + @ReactMethod + fun addViewAttribute(key: String, value: ReadableMap, promise: Promise) { + implementation.addViewAttribute(key, value, promise) + } + + /** + * Removes an attribute from the active RUM View. + * @param key: key for the attribute to be removed from the view. + */ + @ReactMethod + fun removeViewAttribute(key: String, promise: Promise) { + implementation.removeViewAttribute(key, promise) + } + + /** + * Adds multiple attributes to the active RUM View. They will be propagated to all future RUM events associated with the active View. + * @param attributes: key/value object containing all attributes to be added to the view. + */ + @ReactMethod + fun addViewAttributes(attributes: ReadableMap, promise: Promise) { + implementation.addViewAttributes(attributes, promise) + } + + /** + * Removes multiple attributes from the active RUM View. + * @param keys: keys for the attributes to be removed from the view. + */ + @ReactMethod + fun removeViewAttributes(keys: ReadableArray, promise: Promise) { + implementation.removeViewAttributes(keys, promise) + } + /** * Adds the loading time of the view to the active view. * It is calculated as the difference between the current time and the start time of the view. diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt index be1c57b3a..9619794df 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdRumTest.kt @@ -13,13 +13,16 @@ import com.datadog.android.rum.RumMonitor import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod import com.datadog.tools.unit.forge.BaseConfigurator +import com.datadog.tools.unit.toReadableArray import com.datadog.tools.unit.toReadableMap import com.facebook.react.bridge.Promise import fr.xgouchet.elmyr.Forge +import fr.xgouchet.elmyr.annotation.AdvancedForgery import fr.xgouchet.elmyr.annotation.BoolForgery import fr.xgouchet.elmyr.annotation.DoubleForgery import fr.xgouchet.elmyr.annotation.Forgery import fr.xgouchet.elmyr.annotation.IntForgery +import fr.xgouchet.elmyr.annotation.MapForgery import fr.xgouchet.elmyr.annotation.StringForgery import fr.xgouchet.elmyr.annotation.StringForgeryType import fr.xgouchet.elmyr.junit5.ForgeConfiguration @@ -456,6 +459,61 @@ internal class DdRumTest { verify(mockRumMonitor).addTiming(timing) } + @Test + fun `M call addViewAttribute W addViewAttribute()`( + @StringForgery key: String, + @StringForgery value: String + ) { + var attributeMap = mutableMapOf() + attributeMap.put("value", value) + + var attributes = mutableMapOf() + attributes.put(key, value) + + // When + testedDdRum.addViewAttribute(key, attributeMap.toReadableMap(), mockPromise) + + // Then + verify(mockRumMonitor).addViewAttributes(attributes) + } + + @Test + fun `M call removeViewAttribute W removeViewAttribute()`(@StringForgery key: String) { + // When + testedDdRum.removeViewAttribute(key, mockPromise) + + // Then + verify(mockRumMonitor).removeViewAttributes(listOf(key)) + } + + @Test + fun `M call addViewAttributes W addViewAttributes()`( + @MapForgery( + key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), + value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) + ) customAttributes: Map + ) { + // When + testedDdRum.addViewAttributes(customAttributes.toReadableMap(), mockPromise) + + // Then + verify(mockRumMonitor).addViewAttributes(customAttributes) + } + + @Test + fun `𝕄 call removeViewAttributes 𝕎 removeViewAttributes`( + @MapForgery( + key = AdvancedForgery(string = [StringForgery(StringForgeryType.NUMERICAL)]), + value = AdvancedForgery(string = [StringForgery(StringForgeryType.ASCII)]) + ) customAttributes: Map + ) { + // When + testedDdRum.removeViewAttributes(customAttributes.keys.toReadableArray(), mockPromise) + + // Then + verify(mockRumMonitor).removeViewAttributes(customAttributes.keys.toList()) + } + @Test fun `M call addViewLoadingTime w addViewLoadingTime()`(@BoolForgery overwrite: Boolean) { // When diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt index 702cc2533..13f73d94a 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/MockRumMonitor.kt @@ -30,7 +30,13 @@ class MockRumMonitor : RumMonitor { override fun addAttribute(key: String, value: Any?) {} - override fun addViewAttributes(attributes: Map) {} + override fun removeAttribute(key: String) {} + + override fun clearAttributes() {} + + override fun getAttributes(): Map { + return mapOf() + } override fun addError( message: String, @@ -55,15 +61,10 @@ class MockRumMonitor : RumMonitor { @ExperimentalRumApi override fun addViewLoadingTime(overwrite: Boolean) {} - override fun clearAttributes() {} - - override fun getAttributes(): Map { - return mapOf() - } - override fun getCurrentSessionId(callback: (String?) -> Unit) {} - override fun removeAttribute(key: String) {} + override fun addViewAttributes(attributes: Map) {} + override fun removeViewAttributes(attributes: Collection) {} override fun startAction( diff --git a/packages/core/ios/Sources/DdRum.mm b/packages/core/ios/Sources/DdRum.mm index 5d831942a..f5c324ce8 100644 --- a/packages/core/ios/Sources/DdRum.mm +++ b/packages/core/ios/Sources/DdRum.mm @@ -107,6 +107,35 @@ @implementation DdRum [self addTiming:name resolve:resolve reject:reject]; } +RCT_EXPORT_METHOD(addViewAttribute:(NSString*) key + withValue:(NSDictionary*) value + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self addViewAttribute:key value:value resolve:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(removeViewAttribute:(NSString*) key + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self removeViewAttribute:key resolve:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(addViewAttributes:(NSDictionary*) attributes + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self addViewAttributes:attributes resolve:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(removeViewAttributes:(NSArray *)keys + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self removeViewAttributes:keys resolve:resolve reject:reject]; +} + RCT_REMAP_METHOD(addViewLoadingTime, withOverwrite:(BOOL)overwrite withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) @@ -138,7 +167,7 @@ @implementation DdRum // Thanks to this guard, we won't compile this code when we build for the old architecture. #ifdef RCT_NEW_ARCH_ENABLED - (std::shared_ptr)getTurboModule: - (const facebook::react::ObjCTurboModule::InitParams &)params +(const facebook::react::ObjCTurboModule::InitParams &)params { return std::make_shared(params); } @@ -180,6 +209,22 @@ - (void)addTiming:(NSString *)name resolve:(RCTPromiseResolveBlock)resolve rejec [self.ddRumImplementation addTimingWithName:name resolve:resolve reject:reject]; } +- (void)addViewAttribute:(NSString *)key value:(NSDictionary *)value resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation addViewAttributeWithKey:key value:value resolve:resolve reject:reject]; +} + +- (void)removeViewAttribute:(NSString *)key resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation removeViewAttributeWithKey:key resolve:resolve reject:reject]; +} + +- (void)addViewAttributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation addViewAttributesWithAttributes:attributes resolve:resolve reject:reject]; +} + +- (void)removeViewAttributes:(NSArray *)keys resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation removeViewAttributesWithKeys:keys resolve:resolve reject:reject]; +} + - (void)addViewLoadingTime:(BOOL)overwrite resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {\ [self.ddRumImplementation addViewLoadingTimeWithOverwrite:overwrite resolve:resolve reject:reject]; } diff --git a/packages/core/ios/Sources/DdRumImplementation.swift b/packages/core/ios/Sources/DdRumImplementation.swift index 9f8da4c7f..6fac21f82 100644 --- a/packages/core/ios/Sources/DdRumImplementation.swift +++ b/packages/core/ios/Sources/DdRumImplementation.swift @@ -181,6 +181,34 @@ public class DdRumImplementation: NSObject { resolve(nil) } + @objc + public func addViewAttribute(key: AttributeKey, value: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + if let attributeValue = value.object(forKey: "value") { + let castedAttribute = castValueToSwift(attributeValue) + nativeRUM.addViewAttribute(forKey: key, value: castedAttribute) + } + resolve(nil) + } + + @objc + public func removeViewAttribute(key: AttributeKey, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + nativeRUM.removeViewAttribute(forKey: key) + resolve(nil) + } + + @objc + public func addViewAttributes(attributes: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + let castedAttributes = castAttributesToSwift(attributes) + nativeRUM.addViewAttributes(castedAttributes) + resolve(nil) + } + + @objc + public func removeViewAttributes(keys: [AttributeKey], resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + nativeRUM.removeViewAttributes(forKeys: keys) + resolve(nil) + } + @objc public func addViewLoadingTime(overwrite: Bool, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { nativeRUM.addViewLoadingTime(overwrite: overwrite) diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 437b5ee39..9c3fe980f 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -4,13 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import Foundation import DatadogCore -import DatadogRUM -import DatadogLogs -import DatadogTrace import DatadogCrashReporting import DatadogInternal +import DatadogLogs +import DatadogRUM +import DatadogTrace +import DatadogWebViewTracking +import Foundation import React #if os(iOS) @@ -18,7 +19,8 @@ import DatadogWebViewTracking #endif func getDefaultAppVersion() -> String { - let bundleShortVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String + let bundleShortVersion = + Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String let bundleVersion = Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String return bundleShortVersion ?? bundleVersion ?? "0.0.0" } @@ -35,7 +37,7 @@ public class DdSdkImplementation: NSObject { var webviewMessageEmitter: InternalExtension.AbstractMessageEmitter? #endif - private let jsLongTaskThresholdInSeconds: TimeInterval = 0.1; + private let jsLongTaskThresholdInSeconds: TimeInterval = 0.1 @objc public convenience init(bridge: RCTBridge) { @@ -47,7 +49,7 @@ public class DdSdkImplementation: NSObject { RUMMonitorInternalProvider: { RUMMonitor.shared()._internal } ) } - + init( mainDispatchQueue: DispatchQueueType, jsDispatchQueue: DispatchQueueType, @@ -62,10 +64,13 @@ public class DdSdkImplementation: NSObject { self.RUMMonitorInternalProvider = RUMMonitorInternalProvider super.init() } - + // Using @escaping RCTPromiseResolveBlock type will result in an issue when compiling the Swift header file. @objc - public func initialize(configuration: NSDictionary, resolve:@escaping ((Any?) -> Void), reject:RCTPromiseRejectBlock) -> Void { + public func initialize( + configuration: NSDictionary, resolve: @escaping ((Any?) -> Void), + reject: RCTPromiseRejectBlock + ) { let sdkConfiguration = configuration.asDdSdkConfiguration() let nativeInitialization = DdSdkNativeInitialization() @@ -117,7 +122,9 @@ public class DdSdkImplementation: NSObject { } @objc - public func setUserInfo(userInfo: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func setUserInfo( + userInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { let castedUserInfo = castAttributesToSwift(userInfo) let id = castedUserInfo["id"] as? String let name = castedUserInfo["name"] as? String @@ -125,21 +132,22 @@ public class DdSdkImplementation: NSObject { var extraInfo: [AttributeKey: AttributeValue] = [:] if let extraInfoEncodable = castedUserInfo["extraInfo"] as? AnyEncodable, - let extraInfoDict = extraInfoEncodable.value as? [String: Any] { + let extraInfoDict = extraInfoEncodable.value as? [String: Any] + { extraInfo = castAttributesToSwift(extraInfoDict) } if let validId = id { Datadog.setUserInfo(id: validId, name: name, email: email, extraInfo: extraInfo) - } else { - // TO DO - log warning message? } resolve(nil) } - + @objc - public func addUserExtraInfo(extraInfo: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func addUserExtraInfo( + extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { let castedExtraInfo = castAttributesToSwift(extraInfo) Datadog.addUserExtraInfo(castedExtraInfo) @@ -147,35 +155,37 @@ public class DdSdkImplementation: NSObject { } @objc - public func clearUserInfo(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func clearUserInfo(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { Datadog.clearUserInfo() resolve(nil) } @objc - public func setTrackingConsent(trackingConsent: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func setTrackingConsent( + trackingConsent: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { Datadog.set(trackingConsent: (trackingConsent as NSString?).asTrackingConsent()) resolve(nil) } - - + @objc - public func sendTelemetryLog(message: NSString, attributes: NSDictionary, config: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func sendTelemetryLog( + message: NSString, attributes: NSDictionary, config: NSDictionary, + resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { let castedAttributes = castAttributesToSwift(attributes) let castedConfig = castAttributesToSwift(config) - DdTelemetry.sendTelemetryLog(message: message as String, attributes: castedAttributes, config: castedConfig) + DdTelemetry.sendTelemetryLog( + message: message as String, attributes: castedAttributes, config: castedConfig) resolve(nil) } @objc - public func telemetryDebug(message: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DdTelemetry.telemetryDebug(id: "datadog_react_native:\(message)", message: message as String) - resolve(nil) - } - - @objc - public func telemetryError(message: NSString, stack: NSString, kind: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DdTelemetry.telemetryError(id: "datadog_react_native:\(String(describing: kind)):\(message)", message: message as String, kind: kind as String, stack: stack as String) + public func telemetryDebug( + message: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + DdTelemetry.telemetryDebug( + id: "datadog_react_native:\(message)", message: message as String) resolve(nil) } <<<<<<< HEAD @@ -185,27 +195,50 @@ public class DdSdkImplementation: NSObject { >>>>>>> 0443e0ff (iOS: Always use SDK default core instance) @objc - public func consumeWebviewEvent(message: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - do{ + public func telemetryError( + message: NSString, stack: NSString, kind: NSString, resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { + DdTelemetry.telemetryError( + id: "datadog_react_native:\(String(describing: kind)):\(message)", + message: message as String, kind: kind as String, stack: stack as String) + resolve(nil) + } + + @objc + public func consumeWebviewEvent( + message: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + do { try DatadogSDKWrapper.shared.sendWebviewMessage(body: message) } catch { - DdTelemetry.telemetryError(id: "datadog_react_native:\(error.localizedDescription)", message: "The message being sent was:\(message)" as String, kind: "WebViewEventBridgeError" as String, stack: String(describing: error) as String) + DdTelemetry.telemetryError( + id: "datadog_react_native:\(error.localizedDescription)", + message: "The message being sent was:\(message)" as String, + kind: "WebViewEventBridgeError" as String, + stack: String(describing: error) as String) } resolve(nil) } +<<<<<<< HEAD #endif +======= + +>>>>>>> 1f781a51 (Expose view Attributes API) @objc - public func clearAllData(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func clearAllData(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { Datadog.clearAllData() resolve(nil) } - func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration) -> Void { + func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration) { DdTelemetry.overrideTelemetryConfiguration( - initializationType: rnConfiguration.configurationForTelemetry?.initializationType as? String, - reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion as? String, + initializationType: rnConfiguration.configurationForTelemetry?.initializationType + as? String, + reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion + as? String, reactVersion: rnConfiguration.configurationForTelemetry?.reactVersion as? String, trackCrossPlatformLongTasks: rnConfiguration.longTaskThresholdMs != 0, trackErrors: rnConfiguration.configurationForTelemetry?.trackErrors, @@ -220,24 +253,28 @@ public class DdSdkImplementation: NSObject { func startJSRefreshRateMonitoring(sdkConfiguration: DdSdkConfiguration) { if let frameTimeCallback = buildFrameTimeCallback(sdkConfiguration: sdkConfiguration) { // Falling back to mainDispatchQueue if bridge is nil is only useful for tests - self.jsRefreshRateMonitor.startMonitoring(jsQueue: jsDispatchQueue, frameTimeCallback: frameTimeCallback) + self.jsRefreshRateMonitor.startMonitoring( + jsQueue: jsDispatchQueue, frameTimeCallback: frameTimeCallback) } } - func buildFrameTimeCallback(sdkConfiguration: DdSdkConfiguration)-> ((Double) -> ())? { + func buildFrameTimeCallback(sdkConfiguration: DdSdkConfiguration) -> ((Double) -> Void)? { let jsRefreshRateMonitoringEnabled = sdkConfiguration.vitalsUpdateFrequency != nil let jsLongTaskMonitoringEnabled = sdkConfiguration.longTaskThresholdMs != 0 - - if (!jsRefreshRateMonitoringEnabled && !jsLongTaskMonitoringEnabled) { + + if !jsRefreshRateMonitoringEnabled && !jsLongTaskMonitoringEnabled { return nil } func frameTimeCallback(frameTime: Double) { // These checks happen before dispatching because they are quick and less overhead than the dispatch itself. let shouldRecordFrameTime = jsRefreshRateMonitoringEnabled && frameTime > 0 - let shouldRecordLongTask = jsLongTaskMonitoringEnabled && frameTime > sdkConfiguration.longTaskThresholdMs / 1_000 + let shouldRecordLongTask = + jsLongTaskMonitoringEnabled + && frameTime > sdkConfiguration.longTaskThresholdMs / 1_000 guard shouldRecordFrameTime || shouldRecordLongTask, - let rumMonitorInternal = RUMMonitorInternalProvider() else { return } + let rumMonitorInternal = RUMMonitorInternalProvider() + else { return } // Record current timestamp, it may change slightly before event is created on background thread. let now = Date() @@ -247,12 +284,14 @@ public class DdSdkImplementation: NSObject { let normalizedFrameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(frameTime) rumMonitorInternal.updatePerformanceMetric(at: now, metric: .jsFrameTimeSeconds, value: normalizedFrameTimeSeconds, attributes: [:]) } - if (shouldRecordLongTask) { - rumMonitorInternal.addLongTask(at: now, duration: frameTime, attributes: ["long_task.target": "javascript"]) + if shouldRecordLongTask { + rumMonitorInternal.addLongTask( + at: now, duration: frameTime, attributes: ["long_task.target": "javascript"] + ) } } } - + return frameTimeCallback } diff --git a/packages/core/ios/Tests/DdRumTests.swift b/packages/core/ios/Tests/DdRumTests.swift index 1102b7b6b..5f6adc016 100644 --- a/packages/core/ios/Tests/DdRumTests.swift +++ b/packages/core/ios/Tests/DdRumTests.swift @@ -253,6 +253,65 @@ internal class DdRumTests: XCTestCase { XCTAssertEqual(mockNativeRUM.receivedAttributes.count, 0) } + func testAddViewAttribute() throws { + let viewAttributeKey = "attributeKey" + let viewAttributes = NSDictionary( + dictionary: [ + "value": 123, + ] + ) + + rum.addViewAttribute(key: viewAttributeKey, value: viewAttributes, resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) + XCTAssertEqual(mockNativeRUM.calledMethods.last, .addViewAttribute(key: viewAttributeKey)) + XCTAssertEqual(mockNativeRUM.receivedAttributes.count, 1) + let lastAttributes = try XCTUnwrap(mockNativeRUM.receivedAttributes.last) + XCTAssertEqual(lastAttributes.count, 1) + XCTAssertEqual(lastAttributes["attributeKey"] as? Int64, 123) + } + + func testRemoveViewAttribute() throws { + let viewAttributeKey = "attributeKey" + + rum.removeViewAttribute(key: viewAttributeKey, resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) + XCTAssertEqual(mockNativeRUM.calledMethods.last, .removeViewAttribute(key: viewAttributeKey)) + } + + func testAddViewAttributes() throws { + let viewAttributes = NSDictionary( + dictionary: [ + "attribute-1": 123, + "attribute-2": "abc", + "attribute-3": true, + ] + ) + + rum.addViewAttributes(attributes: viewAttributes, resolve: mockResolve, reject: mockReject) + + + XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) + XCTAssertEqual(mockNativeRUM.calledMethods.last, .addViewAttributes()) + XCTAssertEqual(mockNativeRUM.receivedAttributes.count, 1) + let lastAttributes = try XCTUnwrap(mockNativeRUM.receivedAttributes.last) + XCTAssertEqual(lastAttributes.count, 3) + XCTAssertEqual(lastAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(lastAttributes["attribute-2"] as? String, "abc") + XCTAssertEqual(lastAttributes["attribute-3"] as? Bool, true) + } + + + func testRemoveViewAttributes() throws { + let viewAttributeKeys = ["attributeKey1", "attributeKey2", "attributeKey3"] + + rum.removeViewAttributes(keys: viewAttributeKeys, resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(mockNativeRUM.calledMethods.count, 1) + XCTAssertEqual(mockNativeRUM.calledMethods.last, .removeViewAttributes(keys: viewAttributeKeys)) + } + func testAddViewLoadingTime() throws { rum.addViewLoadingTime(overwrite: true, resolve: mockResolve, reject: mockReject) diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index 3a882ed47..4a1d0cd92 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,22 +10,6 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { - func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { - // not implemented - } - - func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { - // not implemented - } - - func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { - // not implemented - } - - func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { - // not implemented - } - func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } @@ -71,6 +55,10 @@ internal class MockRUMMonitor: RUMMonitorProtocol { case stopUserAction(type: RUMActionType, name: String?) case addUserAction(type: RUMActionType, name: String) case addTiming(name: String) + case addViewAttribute(key: String) + case removeViewAttribute(key: String) + case addViewAttributes(_: Int? = nil) // We need an attribute for the case to be Equatable + case removeViewAttributes(keys: [String]) case addViewLoadingTime(overwrite: Bool) case stopSession(_: Int? = nil) // We need an attribute for the case to be Equatable case addResourceMetrics(resourceKey: String, @@ -131,6 +119,24 @@ internal class MockRUMMonitor: RUMMonitorProtocol { func addTiming(name: String) { calledMethods.append(.addTiming(name: name)) } + func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { + calledMethods.append(.addViewAttribute(key: key)) + receivedAttributes.append([key :value]) + } + + func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { + calledMethods.append(.removeViewAttribute(key: key)) + } + + func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { + calledMethods.append(.addViewAttributes()) + receivedAttributes.append(attributes) + } + + func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { + calledMethods.append(.removeViewAttributes(keys: keys)) + } + func addViewLoadingTime(overwrite: Bool) { calledMethods.append(.addViewLoadingTime(overwrite: overwrite)) } diff --git a/packages/core/jest/mock.js b/packages/core/jest/mock.js index c49d13f48..0dc9ec138 100644 --- a/packages/core/jest/mock.js +++ b/packages/core/jest/mock.js @@ -119,6 +119,18 @@ module.exports = { addTiming: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), + addViewAttribute: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + removeViewAttribute: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + addViewAttributes: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + removeViewAttributes: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), addViewLoadingTime: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index b4a8632d7..555b144de 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -10,6 +10,7 @@ import { InternalLog } from '../InternalLog'; import { SdkVerbosity } from '../SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; import type { DdNativeRumType } from '../nativeModulesTypes'; +import type { Attributes } from '../sdk/AttributesSingleton/types'; import { bufferVoidNativeCall } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; import { DdSdk } from '../sdk/DdSdk'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; @@ -298,6 +299,50 @@ class DdRumWrapper implements DdRumType { return bufferVoidNativeCall(() => this.nativeRum.addTiming(name)); }; + addViewAttribute = (key: string, value: unknown): Promise => { + InternalLog.log( + `Adding view attribute “${key}" with value “${JSON.stringify( + value + )}” to RUM View`, + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.addViewAttribute(key, { value }) + ); + }; + + removeViewAttribute = (key: string): Promise => { + InternalLog.log( + `Removing view attribute “${key}" from RUM View`, + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.removeViewAttribute(key) + ); + }; + + addViewAttributes = (attributes: Attributes): Promise => { + InternalLog.log( + `Adding view attributes "${JSON.stringify( + attributes + )}” to RUM View`, + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.addViewAttributes(attributes) + ); + }; + + removeViewAttributes = (keys: string[]): Promise => { + InternalLog.log( + `Removing view attributes “${keys}" from RUM View`, + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.removeViewAttributes(keys) + ); + }; + addViewLoadingTime = (overwrite: boolean): Promise => { InternalLog.log( overwrite diff --git a/packages/core/src/rum/__tests__/DdRum.test.ts b/packages/core/src/rum/__tests__/DdRum.test.ts index ec765ed5d..af1f4fbfa 100644 --- a/packages/core/src/rum/__tests__/DdRum.test.ts +++ b/packages/core/src/rum/__tests__/DdRum.test.ts @@ -1091,6 +1091,98 @@ describe('DdRum', () => { }); }); + describe('DdRum.addTiming', () => { + it('calls the native SDK when setting a timing', async () => { + // GIVEN + const timingName = 'testTiming'; + + // WHEN + await DdRum.addTiming(timingName); + + // THEN + expect(NativeModules.DdRum.addTiming).toHaveBeenCalledTimes(1); + expect(NativeModules.DdRum.addTiming).toHaveBeenCalledWith( + timingName + ); + }); + }); + + describe('DdRum.addViewAttribute', () => { + it('calls the native SDK when setting a view attribute', async () => { + // GIVEN + const key = 'testAttribute'; + const value = { test: 'attribute' }; + + // WHEN + + await DdRum.addViewAttribute(key, value); + + // THEN + expect( + NativeModules.DdRum.addViewAttribute + ).toHaveBeenCalledTimes(1); + expect( + NativeModules.DdRum.addViewAttribute + ).toHaveBeenCalledWith(key, { value }); + }); + }); + + describe('DdRum.removViewAttribute', () => { + it('calls the native SDK when removing a view attribute', async () => { + // GIVEN + const key = 'testAttribute'; + + // WHEN + await DdRum.removeViewAttribute(key); + + // THEN + expect( + NativeModules.DdRum.removeViewAttribute + ).toHaveBeenCalledTimes(1); + expect( + NativeModules.DdRum.removeViewAttribute + ).toHaveBeenCalledWith(key); + }); + }); + + describe('DdRum.addViewAttributes', () => { + it('calls the native SDK when setting view attributes', async () => { + // GIVEN + const attributes = { + test: 'attribute' + }; + + // WHEN + await DdRum.addViewAttributes(attributes); + + // THEN + expect( + NativeModules.DdRum.addViewAttributes + ).toHaveBeenCalledTimes(1); + expect( + NativeModules.DdRum.addViewAttributes + ).toHaveBeenCalledWith(attributes); + }); + }); + + describe('DdRum.removViewAttributes', () => { + it('calls the native SDK when removing view attributes', async () => { + // GIVEN + const keysToDelete = ['test1', 'test2']; + + // WHEN + await DdRum.removeViewAttributes(keysToDelete); + + // THEN + expect( + NativeModules.DdRum.removeViewAttributes + ).toHaveBeenCalledTimes(1); + expect( + NativeModules.DdRum.removeViewAttributes + ).toHaveBeenCalledWith(keysToDelete); + }); + }); + describe('DdRum.addAction', () => { test('uses given context when context is valid', async () => { const context = { diff --git a/packages/core/src/rum/types.ts b/packages/core/src/rum/types.ts index fc8d07c02..3def7f0e6 100644 --- a/packages/core/src/rum/types.ts +++ b/packages/core/src/rum/types.ts @@ -4,6 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ +import type { Attributes } from '../sdk/AttributesSingleton/types'; import type { ErrorSource } from '../types'; import type { DatadogTracingContext } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingContext'; @@ -148,6 +149,31 @@ export type DdRumType = { */ addTiming(name: string): Promise; + /** + * Adds a custom attribute to the active RUM View. It will be propagated to all future RUM events associated with the active View. + * @param key: key for this view attribute. + * @param value: value for this attribute. + */ + addViewAttribute(key: string, value: unknown): Promise; + + /** + * Removes an attribute from the active RUM View. + * @param key: key for the attribute to be removed from the view. + */ + removeViewAttribute(key: string): Promise; + + /** + * Adds multiple attributes to the active RUM View. They will be propagated to all future RUM events associated with the active View. + * @param attributes: key/value object containing all attributes to be added to the view. + */ + addViewAttributes(attributes: Attributes): Promise; + + /** + * Removes multiple attributes from the active RUM View. + * @param keys: keys for the attributes to be removed from the view. + */ + removeViewAttributes(keys: string[]): Promise; + /** * Adds the loading time of the view to the active view. * It is calculated as the difference between the current time and the start time of the view. diff --git a/packages/core/src/specs/NativeDdRum.ts b/packages/core/src/specs/NativeDdRum.ts index f6f7b3daa..e31f5b925 100644 --- a/packages/core/src/specs/NativeDdRum.ts +++ b/packages/core/src/specs/NativeDdRum.ts @@ -136,6 +136,31 @@ export interface Spec extends TurboModule { */ addTiming(name: string): Promise; + /** + * Adds a custom attribute to the active RUM View. It will be propagated to all future RUM events associated with the active View. + * @param key: key for this view attribute. + * @param value: value for this attribute. + */ + addViewAttribute(key: string, value: Object): Promise; + + /** + * Removes an attribute from the active RUM View. + * @param key: key for the attribute to be removed from the view. + */ + removeViewAttribute(key: string): Promise; + + /** + * Adds multiple attributes to the active RUM View. They will be propagated to all future RUM events associated with the active View. + * @param attributes: key/value object containing all attributes to be added to the view. + */ + addViewAttributes(attributes: Object): Promise; + + /** + * Removes multiple attributes from the active RUM View. + * @param keys: keys for the attributes to be removed from the view. + */ + removeViewAttributes(keys: string[]): Promise; + /** * Adds the loading time of the view to the active view. * It is calculated as the difference between the current time and the start time of the view. From d6bae19e3f8c77a7f23ee25a32deb5e47c633cde Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 22 Aug 2025 15:43:01 +0200 Subject: [PATCH 033/168] Fix FileBasedConfiguration related issues --- example/datadog-configuration.json | 20 ++ example/src/App.tsx | 20 +- .../codepush/src/__tests__/index.test.tsx | 4 +- .../FileBasedConfiguration.ts | 54 ++--- .../__tests__/FileBasedConfiguration.test.ts | 192 ++++++++++-------- .../__fixtures__/malformed-configuration.json | 1 - 6 files changed, 157 insertions(+), 134 deletions(-) create mode 100644 example/datadog-configuration.json diff --git a/example/datadog-configuration.json b/example/datadog-configuration.json new file mode 100644 index 000000000..684e60304 --- /dev/null +++ b/example/datadog-configuration.json @@ -0,0 +1,20 @@ +{ + "$schema": "./node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json", + "configuration": { + "applicationId": "APP_ID", + "batchSize": "SMALL", + "clientToken": "CLIENT_TOKEN", + "env": "ENVIRONMENT", + "longTaskThresholdMs": 1000, + "nativeCrashReportEnabled": true, + "sessionSamplingRate": 100, + "site": "US1", + "telemetrySampleRate": 20, + "trackBackgroundEvents": false, + "trackErrors": true, + "trackInteractions": true, + "trackResources": true, + "trackingConsent": "GRANTED", + "verbosity": "DEBUG" + } +} diff --git a/example/src/App.tsx b/example/src/App.tsx index cefcafffe..c29982a97 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -7,7 +7,7 @@ import AboutScreen from './screens/AboutScreen'; import style from './screens/styles'; import { navigationRef } from './NavigationRoot'; import { DdRumReactNavigationTracking, ViewNamePredicate } from '@datadog/mobile-react-navigation'; -import {DatadogProvider} from '@datadog/mobile-react-native' +import {DatadogProvider, FileBasedConfiguration} from '@datadog/mobile-react-native' import { Route } from "@react-navigation/native"; import { NestedNavigator } from './screens/NestedNavigator/NestedNavigator'; import { getDatadogConfig, onDatadogInitialization } from './ddUtils'; @@ -19,9 +19,25 @@ const viewPredicate: ViewNamePredicate = function customViewNamePredicate(route: return "Custom RN " + trackedName; } +// === Datadog Provider Configuration schemes === + +// 1.- Direct configuration +const configuration = getDatadogConfig(TrackingConsent.GRANTED) + +// 2.- File based configuration from .json +// const configuration = new FileBasedConfiguration(require("../datadog-configuration.json")); + +// 3.- File based configuration from .json and custom mapper setup +// const configuration = new FileBasedConfiguration( { +// configuration: require("../datadog-configuration.json").configuration, +// errorEventMapper: (event) => event, +// resourceEventMapper: (event) => event, +// actionEventMapper: (event) => event}); + + export default function App() { return ( - + { DdRumReactNavigationTracking.startTrackingViews(navigationRef.current, viewPredicate) }}> diff --git a/packages/codepush/src/__tests__/index.test.tsx b/packages/codepush/src/__tests__/index.test.tsx index 5c94c16ef..63e1af7d6 100644 --- a/packages/codepush/src/__tests__/index.test.tsx +++ b/packages/codepush/src/__tests__/index.test.tsx @@ -279,7 +279,7 @@ describe('AppCenter Codepush integration', () => { }; const configuration = new FileBasedConfiguration({ - configuration: { configuration: autoInstrumentationConfig } + configuration: autoInstrumentationConfig }); render(); @@ -346,7 +346,7 @@ describe('AppCenter Codepush integration', () => { }; const configuration = new FileBasedConfiguration({ - configuration: { configuration: autoInstrumentationConfig } + configuration: autoInstrumentationConfig }); render(); diff --git a/packages/core/src/sdk/FileBasedConfiguration/FileBasedConfiguration.ts b/packages/core/src/sdk/FileBasedConfiguration/FileBasedConfiguration.ts index ddd1943aa..3fc69f1f3 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/FileBasedConfiguration.ts +++ b/packages/core/src/sdk/FileBasedConfiguration/FileBasedConfiguration.ts @@ -56,44 +56,7 @@ export class FileBasedConfiguration extends DatadogProviderConfiguration { const resolveJSONConfiguration = ( userSpecifiedConfiguration: unknown ): Record => { - if ( - userSpecifiedConfiguration === undefined || - userSpecifiedConfiguration === null - ) { - try { - // This corresponds to a file located at the root of a RN project. - // /!\ We have to write the require this way as dynamic requires are not supported by Hermes. - // eslint-disable-next-line global-require, @typescript-eslint/no-var-requires - const jsonContent = require('../../../../../../datadog-configuration.json'); - - if ( - typeof jsonContent !== 'object' || - !jsonContent['configuration'] - ) { - console.error(`Failed to parse the Datadog configuration file located at the root of the project. -Your configuration must validate the node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json JSON schema. -You can use VSCode to check your configuration by adding the following line to your JSON file: -{ - "$schema": "./node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json", -}`); - - return {}; - } - - return jsonContent.configuration as Record; - } catch (error) { - console.error(`Failed to read Datadog configuration file at the root of the project. -If you don't have a datadog-configuration.json file at the same level as your node_modules directory,\ -please use the following syntax:\n -new FileBasedConfiguration({configuration: require('./file/to/configuration-file.json')}) -`); - return {}; - } - } - if ( - typeof userSpecifiedConfiguration !== 'object' || - !(userSpecifiedConfiguration as any)['configuration'] - ) { + if (typeof userSpecifiedConfiguration !== 'object') { console.error(`Failed to parse the Datadog configuration file you provided. Your configuration must validate the node_modules/@datadog/mobile-react-native/datadog-configuration.schema.json JSON schema. You can use VSCode to check your configuration by adding the following line to your JSON file: @@ -104,10 +67,7 @@ You can use VSCode to check your configuration by adding the following line to y return {}; } - return (userSpecifiedConfiguration as any)['configuration'] as Record< - string, - any - >; + return (userSpecifiedConfiguration as any) as Record; }; export const getJSONConfiguration = ( @@ -130,6 +90,16 @@ export const getJSONConfiguration = ( } => { const configuration = resolveJSONConfiguration(userSpecifiedConfiguration); + if ( + configuration.clientToken === undefined || + configuration.env === undefined || + configuration.applicationId === undefined + ) { + console.warn( + 'DATADOG: Warning: Malformed json configuration file - clientToken, applicationId and env are mandatory properties.' + ); + } + return { clientToken: configuration.clientToken, env: configuration.env, diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts index 716243e86..6d3ee2e44 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts +++ b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts @@ -16,35 +16,99 @@ import malformedConfiguration from './__fixtures__/malformed-configuration.json' describe('FileBasedConfiguration', () => { describe('with user-specified configuration', () => { + it('resolves configuration fields', () => { + const configuration = new FileBasedConfiguration( + configurationAllFields + ); + + expect(configuration).toMatchInlineSnapshot(` + FileBasedConfiguration { + "actionEventMapper": null, + "actionNameAttribute": "action-name-attr", + "additionalConfiguration": {}, + "applicationId": "fake-app-id", + "batchProcessingLevel": "MEDIUM", + "batchSize": "MEDIUM", + "bundleLogsWithRum": true, + "bundleLogsWithTraces": true, + "clientToken": "fake-client-token", + "customEndpoints": {}, + "env": "fake-env", + "errorEventMapper": null, + "firstPartyHosts": [ + { + "match": "example.com", + "propagatorTypes": [ + "b3multi", + "tracecontext", + ], + }, + ], + "initializationMode": "SYNC", + "logEventMapper": null, + "longTaskThresholdMs": 44, + "nativeCrashReportEnabled": false, + "nativeInteractionTracking": false, + "nativeLongTaskThresholdMs": 200, + "nativeViewTracking": false, + "proxyConfig": undefined, + "resourceEventMapper": null, + "resourceTracingSamplingRate": 33, + "serviceName": undefined, + "sessionSamplingRate": 100, + "site": "US5", + "telemetrySampleRate": 20, + "trackBackgroundEvents": false, + "trackErrors": true, + "trackFrustrations": true, + "trackInteractions": true, + "trackResources": true, + "trackWatchdogTerminations": false, + "trackingConsent": "not_granted", + "uploadFrequency": "AVERAGE", + "useAccessibilityLabel": false, + "verbosity": "warn", + "vitalsUpdateFrequency": "AVERAGE", + } + `); + }); + + it('prints a warning message when the configuration file cannot be parsed correctly', () => { + const warnSpy = jest.spyOn(console, 'warn'); + getJSONConfiguration(malformedConfiguration); + + expect(warnSpy).toHaveBeenCalledWith( + 'DATADOG: Warning: Malformed json configuration file - clientToken, applicationId and env are mandatory properties.' + ); + }); + it('resolves all properties from a given file path', () => { const config = new FileBasedConfiguration({ configuration: { - configuration: { - applicationId: 'fake-app-id', - env: 'fake-env', - clientToken: 'fake-client-token', - trackInteractions: true, - trackResources: true, - trackErrors: true, - trackingConsent: 'NOT_GRANTED', - longTaskThresholdMs: 44, - site: 'US5', - verbosity: 'WARN', - actionNameAttribute: 'action-name-attr', - useAccessibilityLabel: false, - resourceTracingSamplingRate: 33, - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: [ - 'B3MULTI', - 'TRACECONTEXT', - 'B3', - 'DATADOG' - ] - } - ] - } + applicationId: 'fake-app-id', + env: 'fake-env', + clientToken: 'fake-client-token', + trackInteractions: true, + trackResources: true, + trackErrors: true, + trackingConsent: 'NOT_GRANTED', + longTaskThresholdMs: 44, + site: 'US5', + verbosity: 'WARN', + actionNameAttribute: 'action-name-attr', + useAccessibilityLabel: false, + resourceTracingSamplingRate: 33, + firstPartyHosts: [ + { + match: 'example.com', + propagatorTypes: [ + 'B3MULTI', + 'TRACECONTEXT', + 'B3', + 'DATADOG' + ] + } + ] } }); expect(config).toMatchInlineSnapshot(` @@ -103,11 +167,9 @@ describe('FileBasedConfiguration', () => { it('applies default values to configuration from a given file path', () => { const config = new FileBasedConfiguration({ configuration: { - configuration: { - applicationId: 'fake-app-id', - env: 'fake-env', - clientToken: 'fake-client-token' - } + applicationId: 'fake-app-id', + env: 'fake-env', + clientToken: 'fake-client-token' } }); expect(config).toMatchInlineSnapshot(` @@ -159,11 +221,9 @@ describe('FileBasedConfiguration', () => { const resourceEventMapper = () => null; const config = new FileBasedConfiguration({ configuration: { - configuration: { - applicationId: 'fake-app-id', - env: 'fake-env', - clientToken: 'fake-client-token' - } + applicationId: 'fake-app-id', + env: 'fake-env', + clientToken: 'fake-client-token' }, actionEventMapper, errorEventMapper, @@ -188,62 +248,20 @@ describe('FileBasedConfiguration', () => { it('prints a warning message when the first party hosts contain unknown propagator types', () => { const config = new FileBasedConfiguration({ configuration: { - configuration: { - applicationId: 'fake-app-id', - env: 'fake-env', - clientToken: 'fake-client-token', - firstPartyHosts: [ - { - match: 'example.com', - propagatorTypes: ['UNKNOWN'] - } - ] - } + applicationId: 'fake-app-id', + env: 'fake-env', + clientToken: 'fake-client-token', + firstPartyHosts: [ + { + match: 'example.com', + propagatorTypes: ['UNKNOWN'] + } + ] } }); expect(config.firstPartyHosts).toHaveLength(0); }); }); - describe('with resolved file configuration', () => { - it('resolves configuration fields', () => { - const configuration = getJSONConfiguration(configurationAllFields); - - expect(configuration).toMatchInlineSnapshot(` - { - "actionNameAttribute": "action-name-attr", - "applicationId": "fake-app-id", - "clientToken": "fake-client-token", - "env": "fake-env", - "firstPartyHosts": [ - { - "match": "example.com", - "propagatorTypes": [ - "b3multi", - "tracecontext", - ], - }, - ], - "longTaskThresholdMs": 44, - "resourceTracingSamplingRate": 33, - "site": "US5", - "trackErrors": true, - "trackInteractions": true, - "trackResources": true, - "trackingConsent": "not_granted", - "useAccessibilityLabel": false, - "verbosity": "warn", - } - `); - }); - it('prints a warning message when the configuration file is not found', () => { - expect(() => getJSONConfiguration(undefined)).not.toThrow(); - }); - it('prints a warning message when the configuration file cannot be parsed correctly', () => { - expect(() => - getJSONConfiguration(malformedConfiguration) - ).not.toThrow(); - }); - }); describe('formatPropagatorType', () => { it('formats all propagatorTypes correctly', () => { diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/malformed-configuration.json b/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/malformed-configuration.json index 28423084d..0e1b26639 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/malformed-configuration.json +++ b/packages/core/src/sdk/FileBasedConfiguration/__tests__/__fixtures__/malformed-configuration.json @@ -1,5 +1,4 @@ { "clientToken": "clientToken", - "env": "env", "applicationId": "applicationId" } From 2783e9bfca0c9c154b61764712cf4cc240d44173 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 27 Oct 2025 14:45:54 +0100 Subject: [PATCH 034/168] Bump Android Native SDK to 3.2.0 and regenerate app podfiles --- benchmarks/android/app/build.gradle | 2 +- benchmarks/ios/Podfile.lock | 10 +++++----- example-new-architecture/ios/Podfile.lock | 6 +++--- example/ios/Podfile.lock | 14 +++++++------- packages/core/android/build.gradle | 10 +++++----- .../android/build.gradle | 4 ++-- packages/react-native-webview/android/build.gradle | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/benchmarks/android/app/build.gradle b/benchmarks/android/app/build.gradle index 04c240bd0..6d9f28764 100644 --- a/benchmarks/android/app/build.gradle +++ b/benchmarks/android/app/build.gradle @@ -129,5 +129,5 @@ dependencies { // Benchmark tools from dd-sdk-android are used for vitals recording // Remember to bump thid alongside the main dd-sdk-android dependencies - implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.1.0") + implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.2.0") } diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index fcf687c21..fdd718bf3 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - DatadogInternal (= 3.1.0) - DatadogRUM (3.1.0): - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.12.1): + - DatadogSDKReactNative (2.13.0): - DatadogCore (= 3.1.0) - DatadogCrashReporting (= 3.1.0) - DatadogLogs (= 3.1.0) @@ -60,7 +60,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.12.1): + - DatadogSDKReactNativeWebView (2.13.0): - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - DatadogWebViewTracking (= 3.1.0) @@ -2075,9 +2075,9 @@ SPEC CHECKSUMS: DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: 8e0f39de38621d4d7ed961a74d8a216fd3a38321 - DatadogSDKReactNativeSessionReplay: f9288c8e981dcc65d1f727b01421ee9a7601e75f - DatadogSDKReactNativeWebView: 993527f6c5d38e0fcc4804a6a60c334dd199dc5b + DatadogSDKReactNative: 620018df2896abcfad6b338c633cc8eccd5de406 + DatadogSDKReactNativeSessionReplay: b2ef22431dd0816adea8d65df13180cf40533f9d + DatadogSDKReactNativeWebView: 299629cf348a5e8f1dabb8289920a00eee625d6a DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index 0378f5d72..051f25e11 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - DatadogInternal (= 3.1.0) - DatadogRUM (3.1.0): - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.12.1): + - DatadogSDKReactNative (2.13.0): - DatadogCore (= 3.1.0) - DatadogCrashReporting (= 3.1.0) - DatadogLogs (= 3.1.0) @@ -37,7 +37,7 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNative/Tests (2.12.1): + - DatadogSDKReactNative/Tests (2.13.0): - DatadogCore (= 3.1.0) - DatadogCrashReporting (= 3.1.0) - DatadogLogs (= 3.1.0) @@ -1855,7 +1855,7 @@ SPEC CHECKSUMS: DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: 069ea9876220b2d09b0f4b180ce571b1b6ecbb35 + DatadogSDKReactNative: 2f11191b56e18680f633bfb125ab1832b327d9b4 DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 8a658e34f..d2df985ff 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -10,7 +10,7 @@ PODS: - DatadogInternal (= 3.1.0) - DatadogRUM (3.1.0): - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.12.1): + - DatadogSDKReactNative (2.13.0): - DatadogCore (= 3.1.0) - DatadogCrashReporting (= 3.1.0) - DatadogLogs (= 3.1.0) @@ -18,7 +18,7 @@ PODS: - DatadogTrace (= 3.1.0) - DatadogWebViewTracking (= 3.1.0) - React-Core - - DatadogSDKReactNative/Tests (2.12.1): + - DatadogSDKReactNative/Tests (2.13.0): - DatadogCore (= 3.1.0) - DatadogCrashReporting (= 3.1.0) - DatadogLogs (= 3.1.0) @@ -73,12 +73,12 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.12.1): + - DatadogSDKReactNativeWebView (2.13.0): - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - DatadogWebViewTracking (= 3.1.0) - React-Core - - DatadogSDKReactNativeWebView/Tests (2.12.1): + - DatadogSDKReactNativeWebView/Tests (2.13.0): - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - DatadogWebViewTracking (= 3.1.0) @@ -1993,9 +1993,9 @@ SPEC CHECKSUMS: DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: af351a4e1ce08124c290c52de94b0062a166cc67 - DatadogSDKReactNativeSessionReplay: dcbd55d9d0f2b86026996a8b7ec9654922d5dfe1 - DatadogSDKReactNativeWebView: 096ac87eb753b6a217b93441983264b9837c3b7e + DatadogSDKReactNative: 822ff8092666172584d4d5e56f79c3799887d408 + DatadogSDKReactNativeSessionReplay: afc4e2b1db34ba8af3a442b0691359faaf5e586e + DatadogSDKReactNativeWebView: 00affefdaca0cf2375e669fa03925d8fa75263d0 DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index 59bb75cc3..080185d41 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -201,16 +201,16 @@ dependencies { // This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0. // To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0 if (reactNativeMinorVersion < 76) { - implementation("com.datadoghq:dd-sdk-android-rum:3.1.0") { + implementation("com.datadoghq:dd-sdk-android-rum:3.2.0") { exclude group: "androidx.metrics", module: "metrics-performance" } implementation "androidx.metrics:metrics-performance:1.0.0-beta01" } else { - implementation "com.datadoghq:dd-sdk-android-rum:3.1.0" + implementation "com.datadoghq:dd-sdk-android-rum:3.2.0" } - implementation "com.datadoghq:dd-sdk-android-logs:3.1.0" - implementation "com.datadoghq:dd-sdk-android-trace:3.1.0" - implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" + implementation "com.datadoghq:dd-sdk-android-logs:3.2.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.2.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.2.0" implementation "com.google.code.gson:gson:2.10.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/react-native-session-replay/android/build.gradle b/packages/react-native-session-replay/android/build.gradle index a0d77f2ff..bea305684 100644 --- a/packages/react-native-session-replay/android/build.gradle +++ b/packages/react-native-session-replay/android/build.gradle @@ -216,8 +216,8 @@ dependencies { api "com.facebook.react:react-android:$reactNativeVersion" } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.datadoghq:dd-sdk-android-session-replay:3.1.0" - implementation "com.datadoghq:dd-sdk-android-internal:3.1.0" + implementation "com.datadoghq:dd-sdk-android-session-replay:3.2.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.2.0" implementation project(path: ':datadog_mobile-react-native') testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-webview/android/build.gradle b/packages/react-native-webview/android/build.gradle index e1fb081f0..95e1e42e0 100644 --- a/packages/react-native-webview/android/build.gradle +++ b/packages/react-native-webview/android/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.facebook.react:react-android:$reactNativeVersion" } - implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.2.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native') From bbe8ca0174825f4b92f6c53c02ba503b6ecbc84e Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 9 Sep 2025 12:08:35 +0200 Subject: [PATCH 035/168] iOS: Always use SDK default core instance --- .../core/ios/Sources/DatadogSDKWrapper.swift | 113 +----------------- .../ios/Sources/DdSdkImplementation.swift | 37 ++---- .../Sources/DdSdkNativeInitialization.swift | 3 + 3 files changed, 17 insertions(+), 136 deletions(-) diff --git a/packages/core/ios/Sources/DatadogSDKWrapper.swift b/packages/core/ios/Sources/DatadogSDKWrapper.swift index 3c56688b0..894f0a09f 100644 --- a/packages/core/ios/Sources/DatadogSDKWrapper.swift +++ b/packages/core/ios/Sources/DatadogSDKWrapper.swift @@ -13,15 +13,12 @@ import DatadogCrashReporting import DatadogInternal import Foundation -<<<<<<< HEAD + #if os(iOS) import DatadogWebViewTracking #endif -public typealias OnCoreInitializedListener = (DatadogCoreProtocol) -> Void -======= -public typealias OnSdkInitializedListener = () -> Void ->>>>>>> 0443e0ff (iOS: Always use SDK default core instance) +public typealias OnSdkInitializedListener = (DatadogCoreProtocol) -> Void /// Wrapper around the Datadog SDK. Use DatadogSDKWrapper.shared to access the instance. public class DatadogSDKWrapper { @@ -45,118 +42,16 @@ public class DatadogSDKWrapper { loggerConfiguration: DatadogLogs.Logger.Configuration, trackingConsent: TrackingConsent ) -> Void { - Datadog.initialize(with: coreConfiguration, trackingConsent: trackingConsent) + let core = Datadog.initialize(with: coreConfiguration, trackingConsent: trackingConsent) for listener in onSdkInitializedListeners { - listener() + listener(core) } self.loggerConfiguration = loggerConfiguration } -<<<<<<< HEAD - internal func isInitialized() -> Bool { - return Datadog.isInitialized() - } - - internal func clearAllData() -> Void { - if let core = coreInstance { - Datadog.clearAllData(in: core) - } else { - Datadog.clearAllData() - } - } - - // Features - internal func enableRUM(with configuration: RUM.Configuration) { - if let core = coreInstance { - RUM.enable(with: configuration, in: core) - } else { - consolePrint("Core instance was not found when initializing RUM.", .critical) - } - } - - internal func enableLogs(with configuration: Logs.Configuration) { - if let core = coreInstance { - Logs.enable(with: configuration, in: core) - } else { - consolePrint("Core instance was not found when initializing Logs.", .critical) - } - } - - internal func enableTrace(with configuration: Trace.Configuration) { - if let core = coreInstance { - Trace.enable(with: configuration, in: core) - } else { - consolePrint("Core instance was not found when initializing Trace.", .critical) - } - } - - internal func enableCrashReporting() { - if let core = coreInstance { - CrashReporting.enable(in: core) - } else { - consolePrint("Core instance was not found when initializing CrashReporting.", .critical) - } - } - - internal func createLogger() -> LoggerProtocol { - let core = coreInstance ?? { - consolePrint("Core instance was not found when creating Logger.", .critical) - return CoreRegistry.default - }() - - return DatadogLogs.Logger.create(with: loggerConfiguration, in: core) - } - - // Telemetry - internal func sendTelemetryLog(message: String, attributes: [String: any Encodable], config: [String: any Encodable]) { - if let core = coreInstance { - let id = (config["onlyOnce"] as? Bool) == true ? message : UUID().uuidString - core.telemetry.debug(id: id, message: message, attributes: attributes) - } else { - consolePrint("Core instance was not found when calling sendTelemetryLog.", .warn) - } - } - - internal func telemetryDebug(id: String, message: String) { - return Datadog._internal.telemetry.debug(id: id, message: message) - } - - internal func telemetryError(id: String, message: String, kind: String?, stack: String?) { - return Datadog._internal.telemetry.error(id: id, message: message, kind: kind, stack: stack) - } - - internal func overrideTelemetryConfiguration( - initializationType: String? = nil, - reactNativeVersion: String? = nil, - reactVersion: String? = nil, - trackCrossPlatformLongTasks: Bool? = nil, - trackErrors: Bool? = nil, - trackInteractions: Bool? = nil, - trackLongTask: Bool? = nil, - trackNativeErrors: Bool? = nil, - trackNativeLongTasks: Bool? = nil, - trackNetworkRequests: Bool? = nil - ) { - coreInstance?.telemetry.configuration( - initializationType: initializationType, - reactNativeVersion: reactNativeVersion, - reactVersion: reactVersion, - trackCrossPlatformLongTasks: trackCrossPlatformLongTasks, - trackErrors: trackErrors, - trackLongTask: trackLongTask, - trackNativeErrors: trackNativeErrors, - trackNativeLongTasks: trackNativeLongTasks, - trackNetworkRequests: trackNetworkRequests, - trackUserInteractions: trackInteractions - ) - } - - #if os(iOS) -======= ->>>>>>> 0443e0ff (iOS: Always use SDK default core instance) // Webview private var webviewMessageEmitter: InternalExtension.AbstractMessageEmitter? diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 9c3fe980f..997068ddf 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -175,33 +175,21 @@ public class DdSdkImplementation: NSObject { ) { let castedAttributes = castAttributesToSwift(attributes) let castedConfig = castAttributesToSwift(config) - DdTelemetry.sendTelemetryLog( - message: message as String, attributes: castedAttributes, config: castedConfig) + DdTelemetry.sendTelemetryLog(message: message as String, attributes: castedAttributes, config: castedConfig) resolve(nil) } @objc - public func telemetryDebug( - message: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock - ) { - DdTelemetry.telemetryDebug( - id: "datadog_react_native:\(message)", message: message as String) + + public func telemetryDebug(message: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + DdTelemetry.telemetryDebug(id: "datadog_react_native:\(message)", message: message as String) resolve(nil) } -<<<<<<< HEAD #if os(iOS) -======= - ->>>>>>> 0443e0ff (iOS: Always use SDK default core instance) @objc - public func telemetryError( - message: NSString, stack: NSString, kind: NSString, resolve: RCTPromiseResolveBlock, - reject: RCTPromiseRejectBlock - ) { - DdTelemetry.telemetryError( - id: "datadog_react_native:\(String(describing: kind)):\(message)", - message: message as String, kind: kind as String, stack: stack as String) + public func telemetryError(message: NSString, stack: NSString, kind: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + DdTelemetry.telemetryError(id: "datadog_react_native:\(String(describing: kind)):\(message)", message: message as String, kind: kind as String, stack: stack as String) resolve(nil) } @@ -221,24 +209,19 @@ public class DdSdkImplementation: NSObject { resolve(nil) } -<<<<<<< HEAD #endif -======= ->>>>>>> 1f781a51 (Expose view Attributes API) @objc - public func clearAllData(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { + public func clearAllData(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { Datadog.clearAllData() resolve(nil) } - func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration) { + func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration) -> Void { DdTelemetry.overrideTelemetryConfiguration( - initializationType: rnConfiguration.configurationForTelemetry?.initializationType - as? String, - reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion - as? String, + initializationType: rnConfiguration.configurationForTelemetry?.initializationType as? String, + reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion as? String, reactVersion: rnConfiguration.configurationForTelemetry?.reactVersion as? String, trackCrossPlatformLongTasks: rnConfiguration.longTaskThresholdMs != 0, trackErrors: rnConfiguration.configurationForTelemetry?.trackErrors, diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index 4ea229871..b55f90561 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -94,12 +94,15 @@ public class DdSdkNativeInitialization: NSObject { CrashReporting.enable() } <<<<<<< HEAD +<<<<<<< HEAD #if os(iOS) DatadogSDKWrapper.shared.enableWebviewTracking() #endif ======= >>>>>>> 0443e0ff (iOS: Always use SDK default core instance) +======= +>>>>>>> 93aa6125 (iOS: Always use SDK default core instance) } func buildSDKConfiguration(configuration: DdSdkConfiguration, defaultAppVersion: String = getDefaultAppVersion()) -> Datadog.Configuration { From bc52612bcc3e6a70fa1713dd8d8c554afceb41c3 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 8 Sep 2025 15:27:17 +0200 Subject: [PATCH 036/168] Bump native SDK dependencies to 3.0.0 --- benchmarks/android/app/build.gradle | 2 +- benchmarks/ios/Podfile.lock | 178 ++++++++-------- example-new-architecture/ios/Podfile.lock | 156 +++++++------- example/ios/Podfile.lock | 196 +++++++++--------- packages/core/DatadogSDKReactNative.podspec | 12 +- packages/core/android/build.gradle | 10 +- ...DatadogSDKReactNativeSessionReplay.podspec | 2 +- .../android/build.gradle | 4 +- .../DatadogSDKReactNativeWebView.podspec | 4 +- .../react-native-webview/android/build.gradle | 2 +- 10 files changed, 283 insertions(+), 283 deletions(-) diff --git a/benchmarks/android/app/build.gradle b/benchmarks/android/app/build.gradle index 6d9f28764..7dc4b3707 100644 --- a/benchmarks/android/app/build.gradle +++ b/benchmarks/android/app/build.gradle @@ -129,5 +129,5 @@ dependencies { // Benchmark tools from dd-sdk-android are used for vitals recording // Remember to bump thid alongside the main dd-sdk-android dependencies - implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.2.0") + implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.0.0") } diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index fdd718bf3..46262383d 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogCrashReporting (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogCore (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogCrashReporting (3.0.0): + - DatadogInternal (= 3.0.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.1.0) - - DatadogLogs (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogRUM (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.13.0): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogInternal (3.0.0) + - DatadogLogs (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogRUM (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogSDKReactNative (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -39,7 +39,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.1.0) + - DatadogSessionReplay (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -60,10 +60,10 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.13.0): - - DatadogInternal (= 3.1.0) + - DatadogSDKReactNativeWebView (2.12.1): + - DatadogInternal (= 3.0.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.1.0) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -84,13 +84,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSessionReplay (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogTrace (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogSessionReplay (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogTrace (3.0.0): + - DatadogInternal (= 3.0.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogWebViewTracking (3.0.0): + - DatadogInternal (= 3.0.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.78.2) @@ -2070,17 +2070,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d - DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d - DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc - DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 - DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: 620018df2896abcfad6b338c633cc8eccd5de406 - DatadogSDKReactNativeSessionReplay: b2ef22431dd0816adea8d65df13180cf40533f9d - DatadogSDKReactNativeWebView: 299629cf348a5e8f1dabb8289920a00eee625d6a - DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 - DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 - DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 + DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 + DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e + DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 + DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd + DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f + DatadogSDKReactNative: 241bf982c16ceff03d94a58e6d005e55c47b99c6 + DatadogSDKReactNativeSessionReplay: bba36092686e3183e97c1a0c7f4ca8142582ae28 + DatadogSDKReactNativeWebView: 6da060df20e235abac533e582d9fc2b3a5070840 + DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 + DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 + DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: e32d34492c519a2194ec9d7f5e7a79d11b73f91c @@ -2089,70 +2089,70 @@ SPEC CHECKSUMS: hermes-engine: 2771b98fb813fdc6f92edd7c9c0035ecabf9fee7 OpenTelemetrySwiftApi: aaee576ed961e0c348af78df58b61300e95bd104 PLCrashReporter: db59ef96fa3d25f3650040d02ec2798cffee75f2 - RCT-Folly: 36fe2295e44b10d831836cc0d1daec5f8abcf809 + RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82 RCTDeprecation: be794de7dc6ed8f9f7fbf525f86e7651b8b68746 RCTRequired: a83787b092ec554c2eb6019ff3f5b8d125472b3b RCTTypeSafety: 48ad3c858926b1c46f46a81a58822b476e178e2c React: 3b5754191f1b65f1dbc52fbea7959c3d2d9e39c9 React-callinvoker: 6beeaf4c7db11b6cc953fac45f2c76e3fb125013 - React-Core: 88e817c42de035378cc71e009193b9a044d3f595 - React-CoreModules: dcf764d71efb4f75d38fcae8d4513b6729f49360 - React-cxxreact: 8cdcc937c5fbc406fe843a381102fd69440ca78a + React-Core: 8a10ac9de53373a3ecb5dfcbcf56df1d3dad0861 + React-CoreModules: af6999b35c7c01b0e12b59d27f3e054e13da43b1 + React-cxxreact: 833f00155ce8c2fda17f6d286f8eaeff2ececc69 React-debug: 440175830c448e7e53e61ebb8d8468c3256b645e - React-defaultsnativemodule: 4824bcd7b96ee2d75c28b1ca21f58976867f5535 - React-domnativemodule: a421118b475618961cf282e8ea85347cc9bb453c - React-Fabric: 6ac7de06009eb96b609a770b17abba6e460b5f45 - React-FabricComponents: e3bc2680a5a9a4917ff0c8d7f390688c30ef753c - React-FabricImage: 8bad558dec7478077974caa96acc79692d6b71f5 + React-defaultsnativemodule: a970effe18fe50bdbbb7115c3297f873b666d0d4 + React-domnativemodule: 45f886342a724e61531b18fba1859bb6782e5d62 + React-Fabric: 69f1881f2177a8512304a64157943548ab6df0cf + React-FabricComponents: f54111c8e2439fc273ab07483e3a7054ca1e75af + React-FabricImage: 9ad2619dfe8c386d79e8aaa87da6e8f018ab9592 React-featureflags: b9cf9b35baca1c7f20c06a104ffc325a02752faa - React-featureflagsnativemodule: dc93d81da9f41f7132e24455ec8b4b60802fd5b0 - React-graphics: aaa5a38bea15d7b895b210d95d554af45a07002a - React-hermes: 08ad9fb832d1b9faef391be17309aa6a69fad23b - React-idlecallbacksnativemodule: aacea33ef6c511a9781f9286cc7cdf93f39bba14 - React-ImageManager: c596c3b658c9c14607f9183ed0f635c8dd77987c - React-jserrorhandler: 987609b2f16b7d79d63fcd621bf0110dd7400b35 - React-jsi: afa286d7e0c102c2478dc420d4f8935e13c973fc - React-jsiexecutor: 08f5b512b4db9e2f147416d60a0a797576b9cfef - React-jsinspector: 5a94bcae66e3637711c4d96a00038ab9ec935bf5 - React-jsinspectortracing: a12589a0adbb2703cbc4380dabe9a58800810923 - React-jsitracing: 0b1a403d7757cec66b7dd8b308d04db85eef75f3 - React-logger: 304814ae37503c8eb54359851cc55bd4f936b39c - React-Mapbuffer: b588d1ca18d2ce626f868f04ab12d8b1f004f12c - React-microtasksnativemodule: 11831d070aa47755bb5739069eb04ec621fec548 - react-native-config: 3367df9c1f25bb96197007ec531c7087ed4554c3 - react-native-safe-area-context: 9b169299f9dc95f1d7fe1dd266fde53bd899cd0c - react-native-slider: 27263d134d55db948a4706f1e47d0ec88fb354dd - react-native-webview: be9957759cb73cb64f2ed5359e32a85f1f5bdff8 - React-NativeModulesApple: 79a4404ac301b40bec3b367879c5e9a9ce81683c - React-perflogger: 0ea25c109dba33d47dec36b2634bf7ea67c1a555 - React-performancetimeline: f74480de6efbcd8541c34317c0baedb433f27296 + React-featureflagsnativemodule: 7f1bc76d1d2c5bede5e753b8d188dbde7c59b12f + React-graphics: 069e0d0b31ed1e80feb023ad4f7e97f00e84f7b9 + React-hermes: 63df5ac5a944889c8758a6213b39ed825863adb7 + React-idlecallbacksnativemodule: 4c700bd7c0012adf904929075a79418b828b5ffc + React-ImageManager: 5d1ba8a7bae44ebba43fc93da64937c713d42941 + React-jserrorhandler: 0defd58f8bb797cdd0a820f733bf42d8bee708ce + React-jsi: 99d6207ec802ad73473a0dad3c9ad48cd98463f6 + React-jsiexecutor: 8c8097b4ba7e7f480582d6e6238b01be5dcc01c0 + React-jsinspector: ea148ec45bc7ff830e443383ea715f9780c15934 + React-jsinspectortracing: 46bb2841982f01e7b63eaab98140fa1de5b2a1db + React-jsitracing: c1063fc2233960d1c8322291e74bca51d25c10d7 + React-logger: 763728cf4eebc9c5dc9bfc3649e22295784f69f3 + React-Mapbuffer: 63278529b5cf531a7eaf8fc71244fabb062ca90c + React-microtasksnativemodule: 6a39463c32ce831c4c2aa8469273114d894b6be9 + react-native-config: 644074ab88db883fcfaa584f03520ec29589d7df + react-native-safe-area-context: afcc2e2b3e78ae8ef90d81e658aacee34ebc27ea + react-native-slider: 310d3f89edd6ca8344a974bfe83a29a3fbb60e5a + react-native-webview: 80ef603d1df42e24fdde765686fbb9b8a6ecd554 + React-NativeModulesApple: fd0545efbb7f936f78edd15a6564a72d2c34bb32 + React-perflogger: 5f8fa36a8e168fb355efe72099efe77213bc2ac6 + React-performancetimeline: 8c0ecfa1ae459cc5678a65f95ac3bf85644d6feb React-RCTActionSheet: 2ef95837e89b9b154f13cd8401f9054fc3076aff - React-RCTAnimation: 33d960d7f58a81779eea6dea47ad0364c67e1517 - React-RCTAppDelegate: 85c13403fd6f6b6cc630428d52bd8bd76a670dc9 - React-RCTBlob: 74c986a02d951931d2f6ed0e07ed5a7eb385bfc0 - React-RCTFabric: 384a8fea4f22fc0f21299d771971862883ba630a - React-RCTFBReactNativeSpec: eb1c3ec5149f76133593a516ff9d5efe32ebcecd - React-RCTImage: 2c58b5ddeb3c65e52f942bbe13ff9c59bd649b09 - React-RCTLinking: b6b14f8a3e62c02fc627ac4f3fb0c7bd941f907c - React-RCTNetwork: 1d050f2466c1541b339587d46f78d5eee218d626 - React-RCTSettings: 8148f6be0ccc0cfe6e313417ebf8a479caaa2146 - React-RCTText: 64114531ad1359e4e02a4a8af60df606dbbabc25 - React-RCTVibration: f4859417a7dd859b6bf18b1aba897e52beb72ef6 + React-RCTAnimation: 46abefd5acfda7e6629f9e153646deecc70babd2 + React-RCTAppDelegate: 7e58e0299e304cceee3f7019fa77bc6990f66b22 + React-RCTBlob: f68c63a801ef1d27e83c4011e3b083cc86a200d7 + React-RCTFabric: c59f41d0c4edbaac8baa232731ca09925ae4dda7 + React-RCTFBReactNativeSpec: 3240b9b8d792aa4be0fb85c9898fc183125ba8de + React-RCTImage: 34e0bba1507e55f1c614bd759eb91d9be48c8c5b + React-RCTLinking: a0b6c9f4871c18b0b81ea952f43e752718bd5f1d + React-RCTNetwork: bdafd661ac2b20d23b779e45bf7ac3e4c8bd1b60 + React-RCTSettings: 98aa5163796f43789314787b584a84eba47787a9 + React-RCTText: 424a274fc9015b29de89cf3cbcdf4dd85dd69f83 + React-RCTVibration: 92d9875a955b0adb34b4b773528fdbbbc5addd6c React-rendererconsistency: 5ac4164ec18cfdd76ed5f864dbfdc56a5a948bc9 - React-rendererdebug: 3dc1d97bbee0c0c13191e501a96ed9325bbd920e + React-rendererdebug: 710dbd7990e355852c786aa6bc7753f6028f357a React-rncore: 0bace3b991d8843bb5b57c5f2301ec6e9c94718b - React-RuntimeApple: 1e1e0a0c6086bc8c3b07e8f1a2f6ca99b50419a0 - React-RuntimeCore: d39322c59bef2a4b343fda663d20649f29f57fcc + React-RuntimeApple: 701ec44a8b5d863ee9b6a2b2447b6a26bb6805a1 + React-RuntimeCore: a82767065b9a936b05e209dc6987bc1ea9eb5d2d React-runtimeexecutor: 876dfc1d8daa819dfd039c40f78f277c5a3e66a6 - React-RuntimeHermes: 44f5f2baf039f249b31ea4f3e224484fd1731e0e - React-runtimescheduler: 3b3c5b50743bb8743ca49b9e5a70c2c385f156e1 + React-RuntimeHermes: e7a051fd91cab8849df56ac917022ef6064ad621 + React-runtimescheduler: c544141f2124ee3d5f3d5bf0d69f4029a61a68b0 React-timing: 1ee3572c398f5579c9df5bf76aacddf5683ff74e - React-utils: 0cfb7c7fb37d4e5f31cc18ffc7426be0ae6bf907 - ReactAppDependencyProvider: b48473fe434569ff8f6cb6ed4421217ebcbda878 - ReactCodegen: 653a0d8532d8c7dab50c391392044d98e20c9f79 - ReactCommon: 547db015202a80a5b3e7e041586ea54c4a087180 - RNCPicker: ffbd7b9fc7c1341929e61dbef6219f7860f57418 - RNScreens: 0f01bbed9bd8045a8d58e4b46993c28c7f498f3c + React-utils: 18703928768cb37e70cf2efff09def12d74a399e + ReactAppDependencyProvider: 4893bde33952f997a323eb1a1ee87a72764018ff + ReactCodegen: da30aff1cea9b5993dcbc33bf1ef47a463c55194 + ReactCommon: 865ebe76504a95e115b6229dd00a31e56d2d4bfe + RNCPicker: cfb51a08c6e10357d9a65832e791825b0747b483 + RNScreens: 790123c4a28783d80a342ce42e8c7381bed62db1 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: e14bad835e12b6c7e2260fc320bd00e0f4b45add diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index 051f25e11..281513566 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogCrashReporting (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogCore (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogCrashReporting (3.0.0): + - DatadogInternal (= 3.0.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.1.0) - - DatadogLogs (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogRUM (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.13.0): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogInternal (3.0.0) + - DatadogLogs (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogRUM (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogSDKReactNative (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -37,13 +37,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNative/Tests (2.13.0): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogSDKReactNative/Tests (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -64,11 +64,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogTrace (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogTrace (3.0.0): + - DatadogInternal (= 3.0.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogWebViewTracking (3.0.0): + - DatadogInternal (= 3.0.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1850,14 +1850,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d - DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d - DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc - DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 - DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: 2f11191b56e18680f633bfb125ab1832b327d9b4 - DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 - DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 + DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 + DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e + DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 + DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd + DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f + DatadogSDKReactNative: 0a80aa75958d595a99be54d2838db53eda7a08af + DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 + DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 @@ -1866,62 +1866,62 @@ SPEC CHECKSUMS: hermes-engine: 9e868dc7be781364296d6ee2f56d0c1a9ef0bb11 OpenTelemetrySwiftApi: aaee576ed961e0c348af78df58b61300e95bd104 PLCrashReporter: db59ef96fa3d25f3650040d02ec2798cffee75f2 - RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1 + RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17 RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83 RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716 RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f React: 4641770499c39f45d4e7cde1eba30e081f9d8a3d React-callinvoker: 4bef67b5c7f3f68db5929ab6a4d44b8a002998ea - React-Core: 0a06707a0b34982efc4a556aff5dae4b22863455 - React-CoreModules: 907334e94314189c2e5eed4877f3efe7b26d85b0 - React-cxxreact: 3a1d5e8f4faa5e09be26614e9c8bbcae8d11b73d + React-Core: a68cea3e762814e60ecc3fa521c7f14c36c99245 + React-CoreModules: d81b1eaf8066add66299bab9d23c9f00c9484c7c + React-cxxreact: 984f8b1feeca37181d4e95301fcd6f5f6501c6ab React-debug: 817160c07dc8d24d020fbd1eac7b3558ffc08964 - React-defaultsnativemodule: 814830ccbc3fb08d67d0190e63b179ee4098c67b - React-domnativemodule: 270acf94bd0960b026bc3bfb327e703665d27fb4 - React-Fabric: 64586dc191fc1c170372a638b8e722e4f1d0a09b - React-FabricComponents: b0ebd032387468ea700574c581b139f57a7497fb - React-FabricImage: 81f0e0794caf25ad1224fa406d288fbc1986607f + React-defaultsnativemodule: 18a684542f82ce1897552a1c4b847be414c9566e + React-domnativemodule: 90bdd4ec3ab38c47cfc3461c1e9283a8507d613f + React-Fabric: f6dade7007533daeb785ba5925039d83f343be4b + React-FabricComponents: b0655cc3e1b5ae12a4a1119aa7d8308f0ad33520 + React-FabricImage: 9b157c4c01ac2bf433f834f0e1e5fe234113a576 React-featureflags: f2792b067a351d86fdc7bec23db3b9a2f2c8d26c - React-featureflagsnativemodule: 0d7091ae344d6160c0557048e127897654a5c00f - React-graphics: cbebe910e4a15b65b0bff94a4d3ed278894d6386 - React-hermes: ec18c10f5a69d49fb9b5e17ae95494e9ea13d4d3 - React-idlecallbacksnativemodule: 6b84add48971da9c40403bd1860d4896462590f2 - React-ImageManager: f2a4c01c2ccb2193e60a20c135da74c7ca4d36f2 - React-jserrorhandler: 61d205b5a7cbc57fed3371dd7eed48c97f49fc64 - React-jsi: 95f7676103137861b79b0f319467627bcfa629ee - React-jsiexecutor: 41e0fe87cda9ea3970ffb872ef10f1ff8dbd1932 - React-jsinspector: 15578208796723e5c6f39069b6e8bf36863ef6e2 - React-jsitracing: 3758cdb155ea7711f0e77952572ea62d90c69f0b - React-logger: dbca7bdfd4aa5ef69431362bde6b36d49403cb20 - React-Mapbuffer: 6efad4a606c1fae7e4a93385ee096681ef0300dc - React-microtasksnativemodule: a645237a841d733861c70b69908ab4a1707b52ad + React-featureflagsnativemodule: 742a8325b3c821d2a1ca13a6d2a0fc72d04555e0 + React-graphics: 68969e4e49d73f89da7abef4116c9b5f466aa121 + React-hermes: ac0bcba26a5d288ebc99b500e1097da2d0297ddf + React-idlecallbacksnativemodule: d61d9c9816131bf70d3d80cd04889fc625ee523f + React-ImageManager: e906eec93a9eb6102a06576b89d48d80a4683020 + React-jserrorhandler: ac5dde01104ff444e043cad8f574ca02756e20d6 + React-jsi: 496fa2b9d63b726aeb07d0ac800064617d71211d + React-jsiexecutor: dd22ab48371b80f37a0a30d0e8915b6d0f43a893 + React-jsinspector: 4629ac376f5765e684d19064f2093e55c97fd086 + React-jsitracing: 7a1c9cd484248870cf660733cd3b8114d54c035f + React-logger: c4052eb941cca9a097ef01b59543a656dc088559 + React-Mapbuffer: 33546a3ebefbccb8770c33a1f8a5554fa96a54de + React-microtasksnativemodule: d80ff86c8902872d397d9622f1a97aadcc12cead React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678 - React-NativeModulesApple: 958d4f6c5c2ace4c0f427cf7ef82e28ae6538a22 - React-perflogger: 9b4f13c0afe56bc7b4a0e93ec74b1150421ee22d - React-performancetimeline: 359db1cb889aa0282fafc5838331b0987c4915a9 + React-NativeModulesApple: cebca2e5320a3d66e123cade23bd90a167ffce5e + React-perflogger: 72e653eb3aba9122f9e57cf012d22d2486f33358 + React-performancetimeline: cd6a9374a72001165995d2ab632f672df04076dc React-RCTActionSheet: aacf2375084dea6e7c221f4a727e579f732ff342 - React-RCTAnimation: d8c82deebebe3aaf7a843affac1b57cb2dc073d4 - React-RCTAppDelegate: 1774aa421a29a41a704ecaf789811ef73c4634b6 - React-RCTBlob: 70a58c11a6a3500d1a12f2e51ca4f6c99babcff8 - React-RCTFabric: 731cda82aed592aacce2d32ead69d78cde5d9274 - React-RCTImage: 5e9d655ba6a790c31e3176016f9b47fd0978fbf0 - React-RCTLinking: 2a48338252805091f7521eaf92687206401bdf2a - React-RCTNetwork: 0c1282b377257f6b1c81934f72d8a1d0c010e4c3 - React-RCTSettings: f757b679a74e5962be64ea08d7865a7debd67b40 - React-RCTText: e7d20c490b407d3b4a2daa48db4bcd8ec1032af2 - React-RCTVibration: 8228e37144ca3122a91f1de16ba8e0707159cfec + React-RCTAnimation: 395ab53fd064dff81507c15efb781c8684d9a585 + React-RCTAppDelegate: 345a6f1b82abc578437df0ce7e9c48740eca827c + React-RCTBlob: 13311e554c1a367de063c10ee7c5e6573b2dd1d6 + React-RCTFabric: 007b1a98201cc49b5bc6e1417d7fe3f6fc6e2b78 + React-RCTImage: 1b1f914bcc12187c49ba5d949dac38c2eb9f5cc8 + React-RCTLinking: 4ac7c42beb65e36fba0376f3498f3cd8dd0be7fa + React-RCTNetwork: 938902773add4381e84426a7aa17a2414f5f94f7 + React-RCTSettings: e848f1ba17a7a18479cf5a31d28145f567da8223 + React-RCTText: 7e98fafdde7d29e888b80f0b35544e0cb07913cf + React-RCTVibration: cd7d80affd97dc7afa62f9acd491419558b64b78 React-rendererconsistency: b4917053ecbaa91469c67a4319701c9dc0d40be6 - React-rendererdebug: 81becbc8852b38d9b1b68672aa504556481330d5 + React-rendererdebug: aa181c36dd6cf5b35511d1ed875d6638fd38f0ec React-rncore: 120d21715c9b4ba8f798bffe986cb769b988dd74 - React-RuntimeApple: 52ed0e9e84a7c2607a901149fb13599a3c057655 - React-RuntimeCore: ca6189d2e53d86db826e2673fe8af6571b8be157 + React-RuntimeApple: d033becbbd1eba6f9f6e3af6f1893030ce203edd + React-RuntimeCore: 38af280bb678e66ba000a3c3d42920b2a138eebb React-runtimeexecutor: 877596f82f5632d073e121cba2d2084b76a76899 - React-RuntimeHermes: 3b752dc5d8a1661c9d1687391d6d96acfa385549 - React-runtimescheduler: 8321bb09175ace2a4f0b3e3834637eb85bf42ebe + React-RuntimeHermes: 37aad735ff21ca6de2d8450a96de1afe9f86c385 + React-runtimescheduler: 8ec34cc885281a34696ea16c4fd86892d631f38d React-timing: 331cbf9f2668c67faddfd2e46bb7f41cbd9320b9 - React-utils: 54df9ada708578c8ad40d92895d6fed03e0e8a9e - ReactCodegen: 21a52ccddc6479448fc91903a437dd23ddc7366c - ReactCommon: bfd3600989d79bc3acbe7704161b171a1480b9fd + React-utils: ed818f19ab445000d6b5c4efa9d462449326cc9f + ReactCodegen: f853a20cc9125c5521c8766b4b49375fec20648b + ReactCommon: 300d8d9c5cb1a6cd79a67cf5d8f91e4d477195f9 SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index d2df985ff..7e4806da2 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,34 +1,34 @@ PODS: - boost (1.84.0) - - DatadogCore (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogCrashReporting (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogCore (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogCrashReporting (3.0.0): + - DatadogInternal (= 3.0.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.1.0) - - DatadogLogs (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogRUM (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.13.0): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogInternal (3.0.0) + - DatadogLogs (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogRUM (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogSDKReactNative (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - React-Core - - DatadogSDKReactNative/Tests (2.13.0): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogSDKReactNative/Tests (2.12.1): + - DatadogCore (= 3.0.0) + - DatadogCrashReporting (= 3.0.0) + - DatadogLogs (= 3.0.0) + - DatadogRUM (= 3.0.0) + - DatadogTrace (= 3.0.0) + - DatadogWebViewTracking (= 3.0.0) - React-Core - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.1.0) + - DatadogSessionReplay (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -51,7 +51,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay/Tests (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.1.0) + - DatadogSessionReplay (= 3.0.0) - DoubleConversion - glog - hermes-engine @@ -73,25 +73,25 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.13.0): - - DatadogInternal (= 3.1.0) + - DatadogSDKReactNativeWebView (2.12.1): + - DatadogInternal (= 3.0.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.1.0) + - DatadogWebViewTracking (= 3.0.0) - React-Core - - DatadogSDKReactNativeWebView/Tests (2.13.0): - - DatadogInternal (= 3.1.0) + - DatadogSDKReactNativeWebView/Tests (2.12.1): + - DatadogInternal (= 3.0.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.1.0) + - DatadogWebViewTracking (= 3.0.0) - React-Core - react-native-webview - React-RCTText - - DatadogSessionReplay (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogTrace (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogSessionReplay (3.0.0): + - DatadogInternal (= 3.0.0) + - DatadogTrace (3.0.0): + - DatadogInternal (= 3.0.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogWebViewTracking (3.0.0): + - DatadogInternal (= 3.0.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1988,17 +1988,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d - DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d - DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc - DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 - DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: 822ff8092666172584d4d5e56f79c3799887d408 - DatadogSDKReactNativeSessionReplay: afc4e2b1db34ba8af3a442b0691359faaf5e586e - DatadogSDKReactNativeWebView: 00affefdaca0cf2375e669fa03925d8fa75263d0 - DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 - DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 - DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 + DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 + DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e + DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 + DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd + DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f + DatadogSDKReactNative: e430b3f4d7adb0fac07b61e2fb65ceacdaf82b3e + DatadogSDKReactNativeSessionReplay: 4b2a3d166a79581f18522795b40141c34cf3685d + DatadogSDKReactNativeWebView: 35dc2b9736e1aaa82b366bf6b8a8a959a9b088c5 + DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 + DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 + DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 @@ -2008,69 +2008,69 @@ SPEC CHECKSUMS: HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352 OpenTelemetrySwiftApi: aaee576ed961e0c348af78df58b61300e95bd104 PLCrashReporter: db59ef96fa3d25f3650040d02ec2798cffee75f2 - RCT-Folly: 7b4f73a92ad9571b9dbdb05bb30fad927fa971e1 + RCT-Folly: ea9d9256ba7f9322ef911169a9f696e5857b9e17 RCTDeprecation: ebe712bb05077934b16c6bf25228bdec34b64f83 RCTRequired: ca91e5dd26b64f577b528044c962baf171c6b716 RCTTypeSafety: e7678bd60850ca5a41df9b8dc7154638cb66871f React: 4641770499c39f45d4e7cde1eba30e081f9d8a3d React-callinvoker: 4bef67b5c7f3f68db5929ab6a4d44b8a002998ea - React-Core: 0a06707a0b34982efc4a556aff5dae4b22863455 - React-CoreModules: 907334e94314189c2e5eed4877f3efe7b26d85b0 - React-cxxreact: 3a1d5e8f4faa5e09be26614e9c8bbcae8d11b73d + React-Core: a68cea3e762814e60ecc3fa521c7f14c36c99245 + React-CoreModules: d81b1eaf8066add66299bab9d23c9f00c9484c7c + React-cxxreact: 984f8b1feeca37181d4e95301fcd6f5f6501c6ab React-debug: 817160c07dc8d24d020fbd1eac7b3558ffc08964 - React-defaultsnativemodule: a965cb39fb0a79276ab611793d39f52e59a9a851 - React-domnativemodule: d647f94e503c62c44f54291334b1aa22a30fa08b - React-Fabric: 64586dc191fc1c170372a638b8e722e4f1d0a09b - React-FabricComponents: b0ebd032387468ea700574c581b139f57a7497fb - React-FabricImage: 81f0e0794caf25ad1224fa406d288fbc1986607f + React-defaultsnativemodule: 21f216e8db975897eb32b5f13247f5bbfaa97f41 + React-domnativemodule: 19270ad4b8d33312838d257f24731a0026809d49 + React-Fabric: f6dade7007533daeb785ba5925039d83f343be4b + React-FabricComponents: b0655cc3e1b5ae12a4a1119aa7d8308f0ad33520 + React-FabricImage: 9b157c4c01ac2bf433f834f0e1e5fe234113a576 React-featureflags: f2792b067a351d86fdc7bec23db3b9a2f2c8d26c - React-featureflagsnativemodule: 95a02d895475de8ace78fedd76143866838bb720 - React-graphics: cbebe910e4a15b65b0bff94a4d3ed278894d6386 - React-hermes: ec18c10f5a69d49fb9b5e17ae95494e9ea13d4d3 - React-idlecallbacksnativemodule: 0c1ae840cc5587197cd926a3cb76828ad059d116 - React-ImageManager: f2a4c01c2ccb2193e60a20c135da74c7ca4d36f2 - React-jserrorhandler: 61d205b5a7cbc57fed3371dd7eed48c97f49fc64 - React-jsi: 95f7676103137861b79b0f319467627bcfa629ee - React-jsiexecutor: 41e0fe87cda9ea3970ffb872ef10f1ff8dbd1932 - React-jsinspector: 15578208796723e5c6f39069b6e8bf36863ef6e2 - React-jsitracing: 3758cdb155ea7711f0e77952572ea62d90c69f0b - React-logger: dbca7bdfd4aa5ef69431362bde6b36d49403cb20 - React-Mapbuffer: 6efad4a606c1fae7e4a93385ee096681ef0300dc - React-microtasksnativemodule: 8732b71aa66045da4bb341ddee1bb539f71e5f38 - react-native-crash-tester: 3ffaa64141427ca362079cb53559fe9a532487ae - react-native-safe-area-context: 04803a01f39f31cc6605a5531280b477b48f8a88 - react-native-webview: 1e12de2fad74c17b4f8b1b53ebd1e3baa0148d71 + React-featureflagsnativemodule: 3a8731d8fd9f755be57e00d9fa8a7f92aa77e87d + React-graphics: 68969e4e49d73f89da7abef4116c9b5f466aa121 + React-hermes: ac0bcba26a5d288ebc99b500e1097da2d0297ddf + React-idlecallbacksnativemodule: 9a2c5b5c174c0c476f039bedc1b9497a8272133e + React-ImageManager: e906eec93a9eb6102a06576b89d48d80a4683020 + React-jserrorhandler: ac5dde01104ff444e043cad8f574ca02756e20d6 + React-jsi: 496fa2b9d63b726aeb07d0ac800064617d71211d + React-jsiexecutor: dd22ab48371b80f37a0a30d0e8915b6d0f43a893 + React-jsinspector: 4629ac376f5765e684d19064f2093e55c97fd086 + React-jsitracing: 7a1c9cd484248870cf660733cd3b8114d54c035f + React-logger: c4052eb941cca9a097ef01b59543a656dc088559 + React-Mapbuffer: 33546a3ebefbccb8770c33a1f8a5554fa96a54de + React-microtasksnativemodule: 5c3d795318c22ab8df55100e50b151384a4a60b3 + react-native-crash-tester: 48bde9d6f5256c61ef2e0c52dfc74256b26e55eb + react-native-safe-area-context: e134b241010ebe2aacdcea013565963d13826faa + react-native-webview: 2ea635bc43fd8a4b89de61133e8cc0607084e9f8 React-nativeconfig: 8efdb1ef1e9158c77098a93085438f7e7b463678 - React-NativeModulesApple: 958d4f6c5c2ace4c0f427cf7ef82e28ae6538a22 - React-perflogger: 9b4f13c0afe56bc7b4a0e93ec74b1150421ee22d - React-performancetimeline: 359db1cb889aa0282fafc5838331b0987c4915a9 + React-NativeModulesApple: cebca2e5320a3d66e123cade23bd90a167ffce5e + React-perflogger: 72e653eb3aba9122f9e57cf012d22d2486f33358 + React-performancetimeline: cd6a9374a72001165995d2ab632f672df04076dc React-RCTActionSheet: aacf2375084dea6e7c221f4a727e579f732ff342 - React-RCTAnimation: d8c82deebebe3aaf7a843affac1b57cb2dc073d4 - React-RCTAppDelegate: 6c0377d9c4058773ea7073bb34bb9ebd6ddf5a84 - React-RCTBlob: 70a58c11a6a3500d1a12f2e51ca4f6c99babcff8 - React-RCTFabric: 7eb6dd2c8fda98cb860a572e3f4e4eb60d62c89e - React-RCTImage: 5e9d655ba6a790c31e3176016f9b47fd0978fbf0 - React-RCTLinking: 2a48338252805091f7521eaf92687206401bdf2a - React-RCTNetwork: 0c1282b377257f6b1c81934f72d8a1d0c010e4c3 - React-RCTSettings: f757b679a74e5962be64ea08d7865a7debd67b40 - React-RCTText: e7d20c490b407d3b4a2daa48db4bcd8ec1032af2 - React-RCTVibration: 8228e37144ca3122a91f1de16ba8e0707159cfec + React-RCTAnimation: 395ab53fd064dff81507c15efb781c8684d9a585 + React-RCTAppDelegate: 1e5b43833e3e36e9fa34eec20be98174bc0e14a2 + React-RCTBlob: 13311e554c1a367de063c10ee7c5e6573b2dd1d6 + React-RCTFabric: bd906861a4e971e21d8df496c2d8f3ca6956f840 + React-RCTImage: 1b1f914bcc12187c49ba5d949dac38c2eb9f5cc8 + React-RCTLinking: 4ac7c42beb65e36fba0376f3498f3cd8dd0be7fa + React-RCTNetwork: 938902773add4381e84426a7aa17a2414f5f94f7 + React-RCTSettings: e848f1ba17a7a18479cf5a31d28145f567da8223 + React-RCTText: 7e98fafdde7d29e888b80f0b35544e0cb07913cf + React-RCTVibration: cd7d80affd97dc7afa62f9acd491419558b64b78 React-rendererconsistency: b4917053ecbaa91469c67a4319701c9dc0d40be6 - React-rendererdebug: 81becbc8852b38d9b1b68672aa504556481330d5 + React-rendererdebug: aa181c36dd6cf5b35511d1ed875d6638fd38f0ec React-rncore: 120d21715c9b4ba8f798bffe986cb769b988dd74 - React-RuntimeApple: 52ed0e9e84a7c2607a901149fb13599a3c057655 - React-RuntimeCore: ca6189d2e53d86db826e2673fe8af6571b8be157 + React-RuntimeApple: d033becbbd1eba6f9f6e3af6f1893030ce203edd + React-RuntimeCore: 38af280bb678e66ba000a3c3d42920b2a138eebb React-runtimeexecutor: 877596f82f5632d073e121cba2d2084b76a76899 - React-RuntimeHermes: 3b752dc5d8a1661c9d1687391d6d96acfa385549 - React-runtimescheduler: 8321bb09175ace2a4f0b3e3834637eb85bf42ebe + React-RuntimeHermes: 37aad735ff21ca6de2d8450a96de1afe9f86c385 + React-runtimescheduler: 8ec34cc885281a34696ea16c4fd86892d631f38d React-timing: 331cbf9f2668c67faddfd2e46bb7f41cbd9320b9 - React-utils: 54df9ada708578c8ad40d92895d6fed03e0e8a9e - ReactCodegen: 21a52ccddc6479448fc91903a437dd23ddc7366c - ReactCommon: bfd3600989d79bc3acbe7704161b171a1480b9fd - ReactNativeNavigation: 50c1eef68b821e7265eff3a391d27ed18fdce459 - RNCAsyncStorage: 23e56519cc41d3bade3c8d4479f7760cb1c11996 - RNGestureHandler: 950dfa674dbf481460ca389c65b9036ac4ab8ada - RNScreens: 606ab1cf68162f7ba0d049a31f2a84089a6fffb4 + React-utils: ed818f19ab445000d6b5c4efa9d462449326cc9f + ReactCodegen: f853a20cc9125c5521c8766b4b49375fec20648b + ReactCommon: 300d8d9c5cb1a6cd79a67cf5d8f91e4d477195f9 + ReactNativeNavigation: 445f86273eb245d15b14023ee4ef9d6e4f891ad6 + RNCAsyncStorage: b44e8a4e798c3e1f56bffccd0f591f674fb9198f + RNGestureHandler: cb711d56ee3b03a5adea1d38324d4459ab55653f + RNScreens: f75b26fd4777848c216e27b0a09e1bf9c9f4760a SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: feb4910aba9742cfedc059e2b2902e22ffe9954a diff --git a/packages/core/DatadogSDKReactNative.podspec b/packages/core/DatadogSDKReactNative.podspec index c0d235304..a2f41ddd1 100644 --- a/packages/core/DatadogSDKReactNative.podspec +++ b/packages/core/DatadogSDKReactNative.podspec @@ -19,14 +19,14 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the versions in sync with DatadogSDKReactNativeSessionReplay.podspec - s.dependency 'DatadogCore', '3.1.0' - s.dependency 'DatadogLogs', '3.1.0' - s.dependency 'DatadogTrace', '3.1.0' - s.dependency 'DatadogRUM', '3.1.0' - s.dependency 'DatadogCrashReporting', '3.1.0' + s.dependency 'DatadogCore', '3.0.0' + s.dependency 'DatadogLogs', '3.0.0' + s.dependency 'DatadogTrace', '3.0.0' + s.dependency 'DatadogRUM', '3.0.0' + s.dependency 'DatadogCrashReporting', '3.0.0' # DatadogWebViewTracking is not available for tvOS - s.ios.dependency 'DatadogWebViewTracking', '3.1.0' + s.ios.dependency 'DatadogWebViewTracking', '3.0.0' s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'ios/Tests/**/*.{swift,json}' diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index 080185d41..a8d09d4d1 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -201,16 +201,16 @@ dependencies { // This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0. // To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0 if (reactNativeMinorVersion < 76) { - implementation("com.datadoghq:dd-sdk-android-rum:3.2.0") { + implementation("com.datadoghq:dd-sdk-android-rum:3.0.0") { exclude group: "androidx.metrics", module: "metrics-performance" } implementation "androidx.metrics:metrics-performance:1.0.0-beta01" } else { - implementation "com.datadoghq:dd-sdk-android-rum:3.2.0" + implementation "com.datadoghq:dd-sdk-android-rum:3.0.0" } - implementation "com.datadoghq:dd-sdk-android-logs:3.2.0" - implementation "com.datadoghq:dd-sdk-android-trace:3.2.0" - implementation "com.datadoghq:dd-sdk-android-webview:3.2.0" + implementation "com.datadoghq:dd-sdk-android-logs:3.0.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.0.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" implementation "com.google.code.gson:gson:2.10.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec index 6a5d0b78f..3a2d5ff48 100644 --- a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec +++ b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogSessionReplay', '3.1.0' + s.dependency 'DatadogSessionReplay', '3.0.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-session-replay/android/build.gradle b/packages/react-native-session-replay/android/build.gradle index bea305684..6d6fe20b1 100644 --- a/packages/react-native-session-replay/android/build.gradle +++ b/packages/react-native-session-replay/android/build.gradle @@ -216,8 +216,8 @@ dependencies { api "com.facebook.react:react-android:$reactNativeVersion" } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.datadoghq:dd-sdk-android-session-replay:3.2.0" - implementation "com.datadoghq:dd-sdk-android-internal:3.2.0" + implementation "com.datadoghq:dd-sdk-android-session-replay:3.0.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.0.0" implementation project(path: ':datadog_mobile-react-native') testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec index 080a853d8..26e160bbc 100644 --- a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec +++ b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec @@ -23,8 +23,8 @@ Pod::Spec.new do |s| end # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogWebViewTracking', '3.1.0' - s.dependency 'DatadogInternal', '3.1.0' + s.dependency 'DatadogWebViewTracking', '3.0.0' + s.dependency 'DatadogInternal', '3.0.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-webview/android/build.gradle b/packages/react-native-webview/android/build.gradle index 95e1e42e0..8eeaea88e 100644 --- a/packages/react-native-webview/android/build.gradle +++ b/packages/react-native-webview/android/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.facebook.react:react-android:$reactNativeVersion" } - implementation "com.datadoghq:dd-sdk-android-webview:3.2.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native') From ca6819cd2ad3fbd1f3c1328d4172e8dc49ac4feb Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 12 Sep 2025 14:43:25 +0200 Subject: [PATCH 037/168] Remove setUser --- packages/core/ios/Tests/MockRUMMonitor.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index 4a1d0cd92..c692f6e7c 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,6 +10,22 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { + func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { + // not implemented + } + + func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { + // not implemented + } + + func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { + // not implemented + } + + func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { + // not implemented + } + func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } From a4260330ffeab532afbb2042726072a73e9b5564 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 19 Sep 2025 11:45:00 +0200 Subject: [PATCH 038/168] Bump Native SDKs to 3.1.0 --- benchmarks/android/app/build.gradle | 2 +- benchmarks/ios/Podfile.lock | 70 +++++++-------- example-new-architecture/ios/Podfile.lock | 66 +++++++------- example/ios/Podfile.lock | 88 +++++++++---------- packages/core/DatadogSDKReactNative.podspec | 12 +-- packages/core/android/build.gradle | 10 +-- ...DatadogSDKReactNativeSessionReplay.podspec | 2 +- .../android/build.gradle | 4 +- .../DatadogSDKReactNativeWebView.podspec | 4 +- .../react-native-webview/android/build.gradle | 2 +- 10 files changed, 130 insertions(+), 130 deletions(-) diff --git a/benchmarks/android/app/build.gradle b/benchmarks/android/app/build.gradle index 7dc4b3707..04c240bd0 100644 --- a/benchmarks/android/app/build.gradle +++ b/benchmarks/android/app/build.gradle @@ -129,5 +129,5 @@ dependencies { // Benchmark tools from dd-sdk-android are used for vitals recording // Remember to bump thid alongside the main dd-sdk-android dependencies - implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.0.0") + implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.1.0") } diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index 46262383d..1d484ae0c 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogCrashReporting (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogCore (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogCrashReporting (3.1.0): + - DatadogInternal (= 3.1.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.0.0) - - DatadogLogs (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogRUM (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogInternal (3.1.0) + - DatadogLogs (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogRUM (3.1.0): + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -39,7 +39,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.0.0) + - DatadogSessionReplay (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -61,9 +61,9 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNativeWebView (2.12.1): - - DatadogInternal (= 3.0.0) + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.0.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -84,13 +84,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSessionReplay (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogTrace (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogSessionReplay (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogTrace (3.1.0): + - DatadogInternal (= 3.1.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogWebViewTracking (3.1.0): + - DatadogInternal (= 3.1.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.78.2) @@ -2070,17 +2070,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 - DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e - DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 - DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd - DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f - DatadogSDKReactNative: 241bf982c16ceff03d94a58e6d005e55c47b99c6 - DatadogSDKReactNativeSessionReplay: bba36092686e3183e97c1a0c7f4ca8142582ae28 - DatadogSDKReactNativeWebView: 6da060df20e235abac533e582d9fc2b3a5070840 - DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 - DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 - DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 + DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d + DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d + DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc + DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 + DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 + DatadogSDKReactNative: 8e0f39de38621d4d7ed961a74d8a216fd3a38321 + DatadogSDKReactNativeSessionReplay: f9288c8e981dcc65d1f727b01421ee9a7601e75f + DatadogSDKReactNativeWebView: 993527f6c5d38e0fcc4804a6a60c334dd199dc5b + DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 + DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 + DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: e32d34492c519a2194ec9d7f5e7a79d11b73f91c diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index 281513566..c0cd90bf6 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogCrashReporting (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogCore (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogCrashReporting (3.1.0): + - DatadogInternal (= 3.1.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.0.0) - - DatadogLogs (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogRUM (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogInternal (3.1.0) + - DatadogLogs (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogRUM (3.1.0): + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -38,12 +38,12 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNative/Tests (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -64,11 +64,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogTrace (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogTrace (3.1.0): + - DatadogInternal (= 3.1.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogWebViewTracking (3.1.0): + - DatadogInternal (= 3.1.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1850,14 +1850,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 - DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e - DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 - DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd - DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f - DatadogSDKReactNative: 0a80aa75958d595a99be54d2838db53eda7a08af - DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 - DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 + DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d + DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d + DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc + DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 + DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 + DatadogSDKReactNative: 069ea9876220b2d09b0f4b180ce571b1b6ecbb35 + DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 + DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 7e4806da2..3849c9558 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,34 +1,34 @@ PODS: - boost (1.84.0) - - DatadogCore (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogCrashReporting (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogCore (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogCrashReporting (3.1.0): + - DatadogInternal (= 3.1.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.0.0) - - DatadogLogs (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogRUM (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogInternal (3.1.0) + - DatadogLogs (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogRUM (3.1.0): + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - DatadogSDKReactNative/Tests (2.12.1): - - DatadogCore (= 3.0.0) - - DatadogCrashReporting (= 3.0.0) - - DatadogLogs (= 3.0.0) - - DatadogRUM (= 3.0.0) - - DatadogTrace (= 3.0.0) - - DatadogWebViewTracking (= 3.0.0) + - DatadogCore (= 3.1.0) + - DatadogCrashReporting (= 3.1.0) + - DatadogLogs (= 3.1.0) + - DatadogRUM (= 3.1.0) + - DatadogTrace (= 3.1.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.0.0) + - DatadogSessionReplay (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -51,7 +51,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay/Tests (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.0.0) + - DatadogSessionReplay (= 3.1.0) - DoubleConversion - glog - hermes-engine @@ -74,24 +74,24 @@ PODS: - ReactCommon/turbomodule/core - Yoga - DatadogSDKReactNativeWebView (2.12.1): - - DatadogInternal (= 3.0.0) + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.0.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - DatadogSDKReactNativeWebView/Tests (2.12.1): - - DatadogInternal (= 3.0.0) + - DatadogInternal (= 3.1.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.0.0) + - DatadogWebViewTracking (= 3.1.0) - React-Core - react-native-webview - React-RCTText - - DatadogSessionReplay (3.0.0): - - DatadogInternal (= 3.0.0) - - DatadogTrace (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogSessionReplay (3.1.0): + - DatadogInternal (= 3.1.0) + - DatadogTrace (3.1.0): + - DatadogInternal (= 3.1.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.0.0): - - DatadogInternal (= 3.0.0) + - DatadogWebViewTracking (3.1.0): + - DatadogInternal (= 3.1.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1988,17 +1988,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: 0c39ba4ef3dee02071f235f2fb56af7e29f4ceb0 - DatadogCrashReporting: 604e65e524cb2fc22cda39fd9c9ea5fd3bddf24e - DatadogInternal: 442673a7fb5329299b489b91ed705aa2085380f7 - DatadogLogs: 0146a0e3140ba62fd42729593c0910d692aea5fd - DatadogRUM: f97fbd0290ecec5bc952fb03a6a1ae068649243f - DatadogSDKReactNative: e430b3f4d7adb0fac07b61e2fb65ceacdaf82b3e - DatadogSDKReactNativeSessionReplay: 4b2a3d166a79581f18522795b40141c34cf3685d - DatadogSDKReactNativeWebView: 35dc2b9736e1aaa82b366bf6b8a8a959a9b088c5 - DatadogSessionReplay: 0357b911c6ab42c77c93b29761ecc20d9282e971 - DatadogTrace: d714e7c456e612b7f171483d08086a59aa1c4213 - DatadogWebViewTracking: fb9591c09fca82b143f3843a7164a7e782175c53 + DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d + DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d + DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc + DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 + DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 + DatadogSDKReactNative: af351a4e1ce08124c290c52de94b0062a166cc67 + DatadogSDKReactNativeSessionReplay: dcbd55d9d0f2b86026996a8b7ec9654922d5dfe1 + DatadogSDKReactNativeWebView: 096ac87eb753b6a217b93441983264b9837c3b7e + DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 + DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 + DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/packages/core/DatadogSDKReactNative.podspec b/packages/core/DatadogSDKReactNative.podspec index a2f41ddd1..c0d235304 100644 --- a/packages/core/DatadogSDKReactNative.podspec +++ b/packages/core/DatadogSDKReactNative.podspec @@ -19,14 +19,14 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the versions in sync with DatadogSDKReactNativeSessionReplay.podspec - s.dependency 'DatadogCore', '3.0.0' - s.dependency 'DatadogLogs', '3.0.0' - s.dependency 'DatadogTrace', '3.0.0' - s.dependency 'DatadogRUM', '3.0.0' - s.dependency 'DatadogCrashReporting', '3.0.0' + s.dependency 'DatadogCore', '3.1.0' + s.dependency 'DatadogLogs', '3.1.0' + s.dependency 'DatadogTrace', '3.1.0' + s.dependency 'DatadogRUM', '3.1.0' + s.dependency 'DatadogCrashReporting', '3.1.0' # DatadogWebViewTracking is not available for tvOS - s.ios.dependency 'DatadogWebViewTracking', '3.0.0' + s.ios.dependency 'DatadogWebViewTracking', '3.1.0' s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'ios/Tests/**/*.{swift,json}' diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index a8d09d4d1..59bb75cc3 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -201,16 +201,16 @@ dependencies { // This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0. // To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0 if (reactNativeMinorVersion < 76) { - implementation("com.datadoghq:dd-sdk-android-rum:3.0.0") { + implementation("com.datadoghq:dd-sdk-android-rum:3.1.0") { exclude group: "androidx.metrics", module: "metrics-performance" } implementation "androidx.metrics:metrics-performance:1.0.0-beta01" } else { - implementation "com.datadoghq:dd-sdk-android-rum:3.0.0" + implementation "com.datadoghq:dd-sdk-android-rum:3.1.0" } - implementation "com.datadoghq:dd-sdk-android-logs:3.0.0" - implementation "com.datadoghq:dd-sdk-android-trace:3.0.0" - implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" + implementation "com.datadoghq:dd-sdk-android-logs:3.1.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.1.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" implementation "com.google.code.gson:gson:2.10.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec index 3a2d5ff48..6a5d0b78f 100644 --- a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec +++ b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogSessionReplay', '3.0.0' + s.dependency 'DatadogSessionReplay', '3.1.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-session-replay/android/build.gradle b/packages/react-native-session-replay/android/build.gradle index 6d6fe20b1..a0d77f2ff 100644 --- a/packages/react-native-session-replay/android/build.gradle +++ b/packages/react-native-session-replay/android/build.gradle @@ -216,8 +216,8 @@ dependencies { api "com.facebook.react:react-android:$reactNativeVersion" } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.datadoghq:dd-sdk-android-session-replay:3.0.0" - implementation "com.datadoghq:dd-sdk-android-internal:3.0.0" + implementation "com.datadoghq:dd-sdk-android-session-replay:3.1.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.1.0" implementation project(path: ':datadog_mobile-react-native') testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec index 26e160bbc..080a853d8 100644 --- a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec +++ b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec @@ -23,8 +23,8 @@ Pod::Spec.new do |s| end # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogWebViewTracking', '3.0.0' - s.dependency 'DatadogInternal', '3.0.0' + s.dependency 'DatadogWebViewTracking', '3.1.0' + s.dependency 'DatadogInternal', '3.1.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-webview/android/build.gradle b/packages/react-native-webview/android/build.gradle index 8eeaea88e..e1fb081f0 100644 --- a/packages/react-native-webview/android/build.gradle +++ b/packages/react-native-webview/android/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.facebook.react:react-android:$reactNativeVersion" } - implementation "com.datadoghq:dd-sdk-android-webview:3.0.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native') From 513895f9982e742da18e645cf6065b820b57cdd7 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 5 Sep 2025 17:47:29 +0200 Subject: [PATCH 039/168] Use native sdk's core instance instead of the one inside RN SDK wrapper --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 ++ .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +----- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 +------ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index 4388ad5f6..ee55d08fe 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,7 +65,9 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) + Logs.enable(logsConfiguration, Datadog.getInstance()) + Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 958ba521b..df44a6b5f 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -22,11 +22,7 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation( - reactContext, - datadog = datadogWrapper, - ddTelemetry - ) + private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 327d8ffc0..f034b292c 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -164,12 +164,7 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation( - mockReactContext, - mockDatadog, - mockDdTelemetry, - TestUiThreadExecutor() - ) + testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From 9102f8e14a9ccc94c4ab781a2b20f0cd8171caef Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Mon, 22 Sep 2025 16:20:27 +0200 Subject: [PATCH 040/168] Fixed internal testing tools and unit tests --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 -- .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +++++- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 ++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index ee55d08fe..4388ad5f6 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,9 +65,7 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) - Logs.enable(logsConfiguration, Datadog.getInstance()) - Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index df44a6b5f..958ba521b 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -22,7 +22,11 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) + private val implementation = DdSdkImplementation( + reactContext, + datadog = datadogWrapper, + ddTelemetry + ) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index f034b292c..327d8ffc0 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -164,7 +164,12 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) + testedBridgeSdk = DdSdkImplementation( + mockReactContext, + mockDatadog, + mockDdTelemetry, + TestUiThreadExecutor() + ) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From c37a1ecb45846a84d0735bcaf7a42c55eeec6cb1 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 19 Sep 2025 17:37:31 +0200 Subject: [PATCH 041/168] Expose clearUserInfo API --- .../ios/Sources/DdSdkImplementation.swift | 8 +-- packages/core/ios/Tests/DdSdkTests.swift | 67 +++++++++++++++++++ 2 files changed, 70 insertions(+), 5 deletions(-) diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 997068ddf..2c5d4cdf3 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -143,7 +143,7 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - + @objc public func addUserExtraInfo( extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock @@ -155,15 +155,13 @@ public class DdSdkImplementation: NSObject { } @objc - public func clearUserInfo(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { + public func clearUserInfo(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { Datadog.clearUserInfo() resolve(nil) } @objc - public func setTrackingConsent( - trackingConsent: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock - ) { + public func setTrackingConsent(trackingConsent: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { Datadog.set(trackingConsent: (trackingConsent as NSString?).asTrackingConsent()) resolve(nil) } diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 4a5d13f2e..5d0237530 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -651,6 +651,73 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } + + func testClearUserInfo() throws { + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ) + bridge.initialize( + configuration: .mockAny(), + resolve: mockResolve, + reject: mockReject + ) + + bridge.setUserInfo( + userInfo: NSDictionary( + dictionary: [ + "id": "id_123", + "name": "John Doe", + "email": "john@doe.com", + "extraInfo": [ + "extra-info-1": 123, + "extra-info-2": "abc", + "extra-info-3": true, + "extra-info-4": [ + "nested-extra-info-1": 456 + ], + ], + ] + ), + resolve: mockResolve, + reject: mockReject + ) + + var ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + var userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, "id_123") + XCTAssertEqual(userInfo.name, "John Doe") + XCTAssertEqual(userInfo.email, "john@doe.com") + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) + + if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] + as? DatadogSDKReactNative.AnyEncodable, + let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] + { + XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) + } else { + XCTFail("extra-info-4 is not of expected type or value") + } + + bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) + + ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, nil) + XCTAssertEqual(userInfo.name, nil) + XCTAssertEqual(userInfo.email, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) + } func testClearUserInfo() throws { let bridge = DdSdkImplementation( From d481aabe73c4c7c7da8da6397a78ea972002fde1 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 29 Sep 2025 12:20:44 +0200 Subject: [PATCH 042/168] Update attribute API --- packages/core/ios/Tests/DdSdkTests.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 5d0237530..cba49f84f 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -651,7 +651,7 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } - + func testClearUserInfo() throws { let bridge = DdSdkImplementation( mainDispatchQueue: DispatchQueueMock(), @@ -704,12 +704,12 @@ class DdSdkTests: XCTestCase { } else { XCTFail("extra-info-4 is not of expected type or value") } - + bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) - + ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() userInfo = try XCTUnwrap(ddContext.userInfo) - + XCTAssertEqual(userInfo.id, nil) XCTAssertEqual(userInfo.name, nil) XCTAssertEqual(userInfo.email, nil) From a255d9ce2c725f5b92516933853b5954554cc746 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 16 Oct 2025 11:01:21 +0200 Subject: [PATCH 043/168] JS refresh rate normalization --- .../reactnative/DdSdkImplementation.kt | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 6741e971e..29fdcdce0 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -354,6 +354,32 @@ class DdSdkImplementation( return display.supportedModes.maxOf { it.refreshRate.toDouble() } } + // endregion + + val maxDeviceFrameTimeMs = 1000.0 / maxDeviceDisplayHz + val budgetFrameTimeMs = 1000.0 / frameBudgetHz + + if (listOf( + maxDeviceDisplayHz, frameTimeMs, frameBudgetHz, budgetFrameTimeMs, maxDeviceFrameTimeMs + ).any { !it.isFinite() || it <= 0.0 } + ) return 1.0 / DEFAULT_REFRESH_HZ + + + var normalizedFrameTimeMs = frameTimeMs / (maxDeviceFrameTimeMs / budgetFrameTimeMs) + + normalizedFrameTimeMs = max(normalizedFrameTimeMs, maxDeviceFrameTimeMs) + + return normalizedFrameTimeMs / 1000.0 // in seconds + } + + @Suppress("CyclomaticComplexMethod") + private fun getMaxDisplayRefreshRate(context: Context?): Double { + val dm = context?.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: return 60.0 + val display: Display = dm.getDisplay(Display.DEFAULT_DISPLAY) ?: return DEFAULT_REFRESH_HZ + + return display.supportedModes.maxOf { it.refreshRate.toDouble() } + } + // endregion internal companion object { internal const val DEFAULT_APP_VERSION = "?" From 510e57e12140326b7a6766276b8060ba9e08a32c Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 10 Oct 2025 14:52:21 +0200 Subject: [PATCH 044/168] Expose view Attributes API --- .../ios/Sources/DdSdkImplementation.swift | 25 +++++++++++-------- packages/core/ios/Tests/MockRUMMonitor.swift | 16 ------------ 2 files changed, 15 insertions(+), 26 deletions(-) diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 2c5d4cdf3..5b91039b3 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -143,7 +143,7 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - + @objc public func addUserExtraInfo( extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock @@ -155,13 +155,15 @@ public class DdSdkImplementation: NSObject { } @objc - public func clearUserInfo(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func clearUserInfo(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { Datadog.clearUserInfo() resolve(nil) } @objc - public func setTrackingConsent(trackingConsent: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func setTrackingConsent( + trackingConsent: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { Datadog.set(trackingConsent: (trackingConsent as NSString?).asTrackingConsent()) resolve(nil) } @@ -173,7 +175,8 @@ public class DdSdkImplementation: NSObject { ) { let castedAttributes = castAttributesToSwift(attributes) let castedConfig = castAttributesToSwift(config) - DdTelemetry.sendTelemetryLog(message: message as String, attributes: castedAttributes, config: castedConfig) + DdTelemetry.sendTelemetryLog( + message: message as String, attributes: castedAttributes, config: castedConfig) resolve(nil) } @@ -184,13 +187,13 @@ public class DdSdkImplementation: NSObject { resolve(nil) } -#if os(iOS) @objc public func telemetryError(message: NSString, stack: NSString, kind: NSString, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { DdTelemetry.telemetryError(id: "datadog_react_native:\(String(describing: kind)):\(message)", message: message as String, kind: kind as String, stack: stack as String) resolve(nil) } +#if os(iOS) @objc public func consumeWebviewEvent( message: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock @@ -207,19 +210,21 @@ public class DdSdkImplementation: NSObject { resolve(nil) } + #endif - @objc - public func clearAllData(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + public func clearAllData(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { Datadog.clearAllData() resolve(nil) } - func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration) -> Void { + func overrideReactNativeTelemetry(rnConfiguration: DdSdkConfiguration) { DdTelemetry.overrideTelemetryConfiguration( - initializationType: rnConfiguration.configurationForTelemetry?.initializationType as? String, - reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion as? String, + initializationType: rnConfiguration.configurationForTelemetry?.initializationType + as? String, + reactNativeVersion: rnConfiguration.configurationForTelemetry?.reactNativeVersion + as? String, reactVersion: rnConfiguration.configurationForTelemetry?.reactVersion as? String, trackCrossPlatformLongTasks: rnConfiguration.longTaskThresholdMs != 0, trackErrors: rnConfiguration.configurationForTelemetry?.trackErrors, diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index c692f6e7c..4a1d0cd92 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,22 +10,6 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { - func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { - // not implemented - } - - func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { - // not implemented - } - - func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { - // not implemented - } - - func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { - // not implemented - } - func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } From e7a4270b3df2f126725b9b5f89037ba0c60e58a2 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 27 Oct 2025 16:28:37 +0100 Subject: [PATCH 045/168] Handle optional String on removeAttributes --- .../main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 29fdcdce0..f02395ed8 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -115,8 +115,7 @@ class DdSdkImplementation( fun removeAttributes(keys: ReadableArray, promise: Promise) { val keysArray = mutableListOf() for (i in 0 until keys.size()) { - val key: String = keys.getString(i) - keysArray.add(key) + keys.getString(i)?.let { if (it.isNotBlank()) keysArray.add(it) } } val keysStringArray = keysArray.toTypedArray() From 0199cf3f48038b0ca9fd333b4709bf259cf1e90d Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 27 Oct 2025 11:37:44 +0100 Subject: [PATCH 046/168] Bump minSdkVersion to 23 --- packages/core/android/build.gradle | 20 ++++--------------- packages/core/android/gradle.properties | 2 +- .../android/gradle.properties | 2 +- .../android/gradle.properties | 2 +- .../android/gradle.properties | 2 +- 5 files changed, 8 insertions(+), 20 deletions(-) diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index 59bb75cc3..1344b2531 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -195,22 +195,10 @@ dependencies { } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compileOnly "com.squareup.okhttp3:okhttp:3.12.13" - - // dd-sdk-android-rum requires androidx.metrics:metrics-performance. - // From 2.21.0, it uses 1.0.0-beta02, which requires Gradle 8.6.0. - // This breaks builds if the React Native target is below 0.76.0. as it relies on Gradle 8.5.0. - // To avoid this, we enforce 1.0.0-beta01 on RN < 0.76.0 - if (reactNativeMinorVersion < 76) { - implementation("com.datadoghq:dd-sdk-android-rum:3.1.0") { - exclude group: "androidx.metrics", module: "metrics-performance" - } - implementation "androidx.metrics:metrics-performance:1.0.0-beta01" - } else { - implementation "com.datadoghq:dd-sdk-android-rum:3.1.0" - } - implementation "com.datadoghq:dd-sdk-android-logs:3.1.0" - implementation "com.datadoghq:dd-sdk-android-trace:3.1.0" - implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" + implementation "com.datadoghq:dd-sdk-android-rum:3.2.0" + implementation "com.datadoghq:dd-sdk-android-logs:3.2.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.2.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.2.0" implementation "com.google.code.gson:gson:2.10.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/core/android/gradle.properties b/packages/core/android/gradle.properties index c9f7a205e..65f975c3e 100644 --- a/packages/core/android/gradle.properties +++ b/packages/core/android/gradle.properties @@ -1,5 +1,5 @@ DdSdkReactNative_kotlinVersion=1.8.21 -DdSdkReactNative_minSdkVersion=21 +DdSdkReactNative_minSdkVersion=23 DdSdkReactNative_compileSdkVersion=33 DdSdkReactNative_buildToolsVersion=33.0.0 DdSdkReactNative_targetSdkVersion=33 diff --git a/packages/internal-testing-tools/android/gradle.properties b/packages/internal-testing-tools/android/gradle.properties index 2a3186ab3..7a783c97d 100644 --- a/packages/internal-testing-tools/android/gradle.properties +++ b/packages/internal-testing-tools/android/gradle.properties @@ -1,5 +1,5 @@ DatadogInternalTesting_kotlinVersion=1.8.21 -DatadogInternalTesting_minSdkVersion=21 +DatadogInternalTesting_minSdkVersion=23 DatadogInternalTesting_compileSdkVersion=33 DatadogInternalTesting_buildToolsVersion=33.0.0 DatadogInternalTesting_targetSdkVersion=33 diff --git a/packages/react-native-session-replay/android/gradle.properties b/packages/react-native-session-replay/android/gradle.properties index 9f1573be5..d072f557e 100644 --- a/packages/react-native-session-replay/android/gradle.properties +++ b/packages/react-native-session-replay/android/gradle.properties @@ -1,6 +1,6 @@ DatadogSDKReactNativeSessionReplay_kotlinVersion=1.8.21 DatadogSDKReactNativeSessionReplay_compileSdkVersion=33 -DatadogSDKReactNativeSessionReplay_minSdkVersion=21 +DatadogSDKReactNativeSessionReplay_minSdkVersion=23 DatadogSDKReactNativeSessionReplay_buildToolsVersion=33.0.0 DatadogSDKReactNativeSessionReplay_targetSdkVersion=33 android.useAndroidX=true diff --git a/packages/react-native-webview/android/gradle.properties b/packages/react-native-webview/android/gradle.properties index 622c7b6b9..25112d024 100644 --- a/packages/react-native-webview/android/gradle.properties +++ b/packages/react-native-webview/android/gradle.properties @@ -1,5 +1,5 @@ DatadogSDKReactNativeWebView_kotlinVersion=1.7.21 -DatadogSDKReactNativeWebView_minSdkVersion=21 +DatadogSDKReactNativeWebView_minSdkVersion=23 DatadogSDKReactNativeWebView_compileSdkVersion=33 DatadogSDKReactNativeWebView_buildToolsVersion=33.0.0 DatadogSDKReactNativeWebView_targetSdkVersion=33 From ff36b0725aaecc246ab8c4cad1a92edcc1a32704 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Wed, 5 Nov 2025 15:58:05 +0000 Subject: [PATCH 047/168] Expose sdk iOS config option `trackMemoryWarnings` --- packages/core/ios/Sources/DdSdkConfiguration.swift | 5 ++++- .../ios/Sources/DdSdkNativeInitialization.swift | 1 + .../core/ios/Sources/RNDdSdkConfiguration.swift | 9 +++++++-- packages/core/src/DdSdkReactNative.tsx | 3 ++- .../core/src/DdSdkReactNativeConfiguration.tsx | 14 +++++++++++++- .../DdSdkReactNativeConfiguration.test.ts | 3 +++ .../__tests__/initialization.test.tsx | 1 + .../__tests__/FileBasedConfiguration.test.ts | 3 +++ packages/core/src/types.tsx | 3 ++- 9 files changed, 36 insertions(+), 6 deletions(-) diff --git a/packages/core/ios/Sources/DdSdkConfiguration.swift b/packages/core/ios/Sources/DdSdkConfiguration.swift index 7a76cf39d..288e2a539 100644 --- a/packages/core/ios/Sources/DdSdkConfiguration.swift +++ b/packages/core/ios/Sources/DdSdkConfiguration.swift @@ -76,6 +76,7 @@ public class DdSdkConfiguration: NSObject { public var trackWatchdogTerminations: Bool public var batchProcessingLevel: Datadog.Configuration.BatchProcessingLevel public var initialResourceThreshold: Double? = nil + public var trackMemoryWarnings: Bool public init( clientToken: String, @@ -108,7 +109,8 @@ public class DdSdkConfiguration: NSObject { appHangThreshold: Double?, trackWatchdogTerminations: Bool, batchProcessingLevel: Datadog.Configuration.BatchProcessingLevel, - initialResourceThreshold: Double? + initialResourceThreshold: Double?, + trackMemoryWarnings: Bool = true ) { self.clientToken = clientToken self.env = env @@ -141,6 +143,7 @@ public class DdSdkConfiguration: NSObject { self.trackWatchdogTerminations = trackWatchdogTerminations self.batchProcessingLevel = batchProcessingLevel self.initialResourceThreshold = initialResourceThreshold + self.trackMemoryWarnings = trackMemoryWarnings } } diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index b55f90561..969f72abf 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -201,6 +201,7 @@ public class DdSdkNativeInitialization: NSObject { }, onSessionStart: DdSdkSessionStartedListener.instance.rumSessionListener, customEndpoint: customRUMEndpointURL, + trackMemoryWarnings: configuration.trackMemoryWarnings, telemetrySampleRate: (configuration.telemetrySampleRate as? NSNumber)?.floatValue ?? Float(DefaultConfiguration.telemetrySampleRate) ) } diff --git a/packages/core/ios/Sources/RNDdSdkConfiguration.swift b/packages/core/ios/Sources/RNDdSdkConfiguration.swift index 66437c481..6869d1711 100644 --- a/packages/core/ios/Sources/RNDdSdkConfiguration.swift +++ b/packages/core/ios/Sources/RNDdSdkConfiguration.swift @@ -43,6 +43,7 @@ extension NSDictionary { let trackWatchdogTerminations = object(forKey: "trackWatchdogTerminations") as? Bool let batchProcessingLevel = object(forKey: "batchProcessingLevel") as? NSString let initialResourceThreshold = object(forKey: "initialResourceThreshold") as? Double + let trackMemoryWarnings = object(forKey: "trackMemoryWarnings") as? Bool return DdSdkConfiguration( clientToken: (clientToken != nil) ? clientToken! : String(), @@ -75,7 +76,8 @@ extension NSDictionary { appHangThreshold: appHangThreshold, trackWatchdogTerminations: trackWatchdogTerminations ?? DefaultConfiguration.trackWatchdogTerminations, batchProcessingLevel: batchProcessingLevel.asBatchProcessingLevel(), - initialResourceThreshold: initialResourceThreshold + initialResourceThreshold: initialResourceThreshold, + trackMemoryWarnings: trackMemoryWarnings ?? DefaultConfiguration.trackMemoryWarnings ) } @@ -206,6 +208,7 @@ internal struct DefaultConfiguration { static let bundleLogsWithRum = true static let bundleLogsWithTraces = true static let trackWatchdogTerminations = false + static let trackMemoryWarnings = true } extension Dictionary where Key == String, Value == AnyObject { @@ -244,6 +247,7 @@ extension Dictionary where Key == String, Value == AnyObject { let trackWatchdogTerminations = configuration["trackWatchdogTerminations"] as? Bool let batchProcessingLevel = configuration["batchProcessingLevel"] as? NSString let initialResourceThreshold = configuration["initialResourceThreshold"] as? Double + let trackMemoryWarnings = configuration["trackMemoryWarnings"] as? Bool return DdSdkConfiguration( clientToken: clientToken ?? String(), @@ -279,7 +283,8 @@ extension Dictionary where Key == String, Value == AnyObject { appHangThreshold: appHangThreshold, trackWatchdogTerminations: trackWatchdogTerminations ?? DefaultConfiguration.trackWatchdogTerminations, batchProcessingLevel: batchProcessingLevel.asBatchProcessingLevel(), - initialResourceThreshold: initialResourceThreshold + initialResourceThreshold: initialResourceThreshold, + trackMemoryWarnings: trackMemoryWarnings ?? DefaultConfiguration.trackMemoryWarnings ) } } diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index 8360a695b..b1ea4f4c1 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -397,7 +397,8 @@ export class DdSdkReactNative { configuration.resourceTracingSamplingRate, configuration.trackWatchdogTerminations, configuration.batchProcessingLevel, - configuration.initialResourceThreshold + configuration.initialResourceThreshold, + configuration.trackMemoryWarnings ); }; diff --git a/packages/core/src/DdSdkReactNativeConfiguration.tsx b/packages/core/src/DdSdkReactNativeConfiguration.tsx index 44debb2d2..4ec1ef883 100644 --- a/packages/core/src/DdSdkReactNativeConfiguration.tsx +++ b/packages/core/src/DdSdkReactNativeConfiguration.tsx @@ -133,7 +133,8 @@ export const DEFAULTS = { bundleLogsWithTraces: true, useAccessibilityLabel: true, trackWatchdogTerminations: false, - batchProcessingLevel: BatchProcessingLevel.MEDIUM + batchProcessingLevel: BatchProcessingLevel.MEDIUM, + trackMemoryWarnings: true }; /** @@ -328,6 +329,16 @@ export class DdSdkReactNativeConfiguration { public trackWatchdogTerminations: boolean = DEFAULTS.trackWatchdogTerminations; + /** + * Enables tracking of memory warnings as RUM events. + * + * When enabled, the SDK will automatically record a RUM event each time the app + * receives a memory warning from the operating system. + * + * **Note:** This setting is only supported on **iOS**. It has no effect on other platforms. + */ + public trackMemoryWarnings: boolean = DEFAULTS.trackMemoryWarnings; + /** * Specifies a custom prop to name RUM actions on elements having an `onPress` prop. * @@ -469,6 +480,7 @@ export type PartialInitializationConfiguration = { readonly bundleLogsWithTraces?: boolean; readonly batchProcessingLevel?: BatchProcessingLevel; readonly initialResourceThreshold?: number; + readonly trackMemoryWarnings?: boolean; }; const setConfigurationAttribute = < diff --git a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts index b1522ef7f..60a9d13ff 100644 --- a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts +++ b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts @@ -60,6 +60,7 @@ describe('DdSdkReactNativeConfiguration', () => { "trackErrors": false, "trackFrustrations": true, "trackInteractions": false, + "trackMemoryWarnings": true, "trackResources": false, "trackWatchdogTerminations": false, "trackingConsent": "granted", @@ -170,6 +171,7 @@ describe('DdSdkReactNativeConfiguration', () => { "trackErrors": true, "trackFrustrations": true, "trackInteractions": true, + "trackMemoryWarnings": true, "trackResources": true, "trackWatchdogTerminations": false, "trackingConsent": "pending", @@ -246,6 +248,7 @@ describe('DdSdkReactNativeConfiguration', () => { "trackErrors": false, "trackFrustrations": false, "trackInteractions": false, + "trackMemoryWarnings": true, "trackResources": false, "trackWatchdogTerminations": false, "trackingConsent": "granted", diff --git a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx index c654cd24b..8ace4b731 100644 --- a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx +++ b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx @@ -100,6 +100,7 @@ describe('DatadogProvider', () => { "telemetrySampleRate": 20, "trackBackgroundEvents": false, "trackFrustrations": true, + "trackMemoryWarnings": true, "trackNonFatalAnrs": undefined, "trackWatchdogTerminations": false, "trackingConsent": "granted", diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts index 6d3ee2e44..1670e5fe2 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts +++ b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts @@ -62,6 +62,7 @@ describe('FileBasedConfiguration', () => { "trackErrors": true, "trackFrustrations": true, "trackInteractions": true, + "trackMemoryWarnings": true, "trackResources": true, "trackWatchdogTerminations": false, "trackingConsent": "not_granted", @@ -154,6 +155,7 @@ describe('FileBasedConfiguration', () => { "trackErrors": true, "trackFrustrations": true, "trackInteractions": true, + "trackMemoryWarnings": true, "trackResources": true, "trackWatchdogTerminations": false, "trackingConsent": "not_granted", @@ -205,6 +207,7 @@ describe('FileBasedConfiguration', () => { "trackErrors": false, "trackFrustrations": true, "trackInteractions": false, + "trackMemoryWarnings": true, "trackResources": false, "trackWatchdogTerminations": false, "trackingConsent": "granted", diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index c8d9821cc..5c7d64cec 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -69,7 +69,8 @@ export class DdSdkConfiguration { readonly resourceTracingSamplingRate: number, readonly trackWatchdogTerminations: boolean | undefined, readonly batchProcessingLevel: BatchProcessingLevel, // eslint-disable-next-line no-empty-function - readonly initialResourceThreshold: number | undefined + readonly initialResourceThreshold: number | undefined, + readonly trackMemoryWarnings: boolean ) {} } From 6b4e913311e1603bb7c022aab3352eb255718cce Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Mon, 3 Nov 2025 18:05:04 +0000 Subject: [PATCH 048/168] Expose `setAccountInfo` API to JS layer add account info to `applyEventMapper` --- .../datadog/reactnative/DatadogSDKWrapper.kt | 20 ++++- .../com/datadog/reactnative/DatadogWrapper.kt | 27 ++++++ .../reactnative/DdSdkImplementation.kt | 41 +++++++++ .../kotlin/com/datadog/reactnative/DdSdk.kt | 26 ++++++ .../kotlin/com/datadog/reactnative/DdSdk.kt | 26 ++++++ packages/core/ios/Sources/DdSdk.mm | 32 +++++++ .../ios/Sources/DdSdkImplementation.swift | 38 ++++++++ packages/core/jest/mock.js | 9 ++ packages/core/src/DdSdkReactNative.tsx | 66 ++++++++++++++ .../AccountInfoSingleton.ts | 46 ++++++++++ .../__tests__/AccountInfoSingleton.test.ts | 90 +++++++++++++++++++ .../src/sdk/AccountInfoSingleton/types.ts | 11 +++ .../core/src/sdk/EventMappers/EventMapper.ts | 5 ++ packages/core/src/specs/NativeDdSdk.ts | 17 ++++ packages/core/src/types.tsx | 27 ++++++ 15 files changed, 480 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts create mode 100644 packages/core/src/sdk/AccountInfoSingleton/__tests__/AccountInfoSingleton.test.ts create mode 100644 packages/core/src/sdk/AccountInfoSingleton/types.ts diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index 06151d834..56a15373c 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -89,7 +89,25 @@ internal class DatadogSDKWrapper : DatadogWrapper { override fun clearUserInfo() { Datadog.clearUserInfo() } - + + override fun setAccountInfo( + id: String, + name: String?, + extraInfo: Map + ) { + Datadog.setAccountInfo(id, name, extraInfo) + } + + override fun addAccountExtraInfo( + extraInfo: Map + ) { + Datadog.addAccountExtraInfo(extraInfo) + } + + override fun clearAccountInfo() { + Datadog.clearAccountInfo() + } + override fun addRumGlobalAttribute(key: String, value: Any?) { this.getRumMonitor().addAttribute(key, value) } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index d6395b18b..c72f2faef 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -91,6 +91,33 @@ interface DatadogWrapper { */ fun clearUserInfo() + /** + * Sets the account information. + * + * @param id a unique account identifier (relevant to your business domain) + * @param name (nullable) the account name + * @param extraInfo additional information. An extra information can be + * nested up to 8 levels deep. Keys using more than 8 levels will be sanitized by SDK. + */ + fun setAccountInfo( + id: String, + name: String?, + extraInfo: Map + ) + + /** + * Sets the account information. + * @param extraInfo: The additional information. (To set the id or name please use setAccountInfo). + */ + fun addAccountExtraInfo( + extraInfo: Map + ) + + /** + * Clears the account information. + */ + fun clearAccountInfo() + /** Adds a global attribute. * diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index f02395ed8..02123ee8f 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -168,6 +168,47 @@ class DdSdkImplementation( promise.resolve(null) } + /** + * Set the account information. + * @param accountInfo The account object (use builtin attributes: 'id', 'name', and any custom + * attribute inside 'extraInfo'). + */ + fun setAccountInfo(accountInfo: ReadableMap, promise: Promise) { + val accountInfoMap = accountInfo.toHashMap().toMutableMap() + val id = accountInfoMap["id"] as? String + val name = accountInfoMap["name"] as? String + val extraInfo = (accountInfoMap["extraInfo"] as? Map<*, *>)?.filterKeys { it is String } + ?.mapKeys { it.key as String } + ?.mapValues { it.value } ?: emptyMap() + + if (id != null) { + datadog.setAccountInfo(id, name, extraInfo) + } + + promise.resolve(null) + } + + /** + * Sets the account extra information. + * @param accountExtraInfo: The additional information. (To set the id or name please use setAccountInfo). + */ + fun addAccountExtraInfo( + accountExtraInfo: ReadableMap, promise: Promise + ) { + val extraInfoMap = accountExtraInfo.toHashMap().toMutableMap() + + datadog.addAccountExtraInfo(extraInfoMap) + promise.resolve(null) + } + + /** + * Clears the account information. + */ + fun clearAccountInfo(promise: Promise) { + datadog.clearAccountInfo() + promise.resolve(null) + } + /** * Set the tracking consent regarding the data collection. * @param trackingConsent Consent, which can take one of the following values: 'pending', diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt index a9d430081..421812545 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -106,6 +106,32 @@ class DdSdk( implementation.clearUserInfo(promise) } + /** + * Set the account information. + * @param account The account object (use builtin attributes: 'id', 'name', and any custom * attribute inside 'extraInfo'). + */ + @ReactMethod + override fun setAccountInfo(account: ReadableMap, promise: Promise) { + implementation.setAccountInfo(account, promise) + } + + /** + * Sets the account information. + * @param extraAccountInfo: The additional information. (To set the id or name please use setAccountInfo). + */ + @ReactMethod + override fun addAccountExtraInfo(extraInfo: ReadableMap, promise: Promise) { + implementation.addAccountExtraInfo(extraInfo, promise) + } + + /** + * Clears the account information. + */ + @ReactMethod + override fun clearAccountInfo(promise: Promise) { + implementation.clearAccountInfo(promise) + } + /** * Set the tracking consent regarding the data collection. * @param trackingConsent Consent, which can take one of the following values: 'pending', diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 958ba521b..ef91ca549 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -132,6 +132,32 @@ class DdSdk( implementation.clearUserInfo(promise) } + /** + * Set the account information. + * @param account The account object (use builtin attributes: 'id', 'name', and any custom * attribute inside 'extraInfo'). + */ + @ReactMethod + fun setAccountInfo(account: ReadableMap, promise: Promise) { + implementation.setAccountInfo(account, promise) + } + + /** + * Sets the account information. + * @param extraAccountInfo: The additional information. (To set the id or name please use setAccountInfo). + */ + @ReactMethod + fun addAccountExtraInfo(extraInfo: ReadableMap, promise: Promise) { + implementation.addAccountExtraInfo(extraInfo, promise) + } + + /** + * Clears the account information. + */ + @ReactMethod + fun clearAccountInfo(promise: Promise) { + implementation.clearAccountInfo(promise) + } + /** * Set the tracking consent regarding the data collection. * @param trackingConsent Consent, which can take one of the following values: 'pending', diff --git a/packages/core/ios/Sources/DdSdk.mm b/packages/core/ios/Sources/DdSdk.mm index 06736a69e..c05ac6a7c 100644 --- a/packages/core/ios/Sources/DdSdk.mm +++ b/packages/core/ios/Sources/DdSdk.mm @@ -79,6 +79,26 @@ + (void)initFromNative { [self clearUserInfo:resolve reject:reject]; } +RCT_REMAP_METHOD(setAccountInfo, withAccountInfo:(NSDictionary*)accountInfo + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self setAccountInfo:accountInfo resolve:resolve reject:reject]; +} + +RCT_REMAP_METHOD(addAccountExtraInfo, withAccountExtraInfo:(NSDictionary*)extraInfo + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self addAccountExtraInfo:extraInfo resolve:resolve reject:reject]; +} + +RCT_EXPORT_METHOD(clearAccountInfo:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self clearAccountInfo:resolve reject:reject]; +} + RCT_REMAP_METHOD(setTrackingConsent, withTrackingConsent:(NSString*)trackingConsent withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) @@ -191,6 +211,18 @@ -(void)addUserExtraInfo:(NSDictionary *)extraInfo resolve:(RCTPromiseResolveBloc [self.ddSdkImplementation addUserExtraInfoWithExtraInfo:extraInfo resolve:resolve reject:reject]; } +- (void)setAccountInfo:(NSDictionary *)accountInfo resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation setAccountInfoWithAccountInfo:accountInfo resolve:resolve reject:reject]; +} + +- (void)clearAccountInfo:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation clearAccountInfoWithResolve:resolve reject:reject]; +} + +-(void)addAccountExtraInfo:(NSDictionary *)extraInfo resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddSdkImplementation addAccountExtraInfoWithExtraInfo:extraInfo resolve:resolve reject:reject]; +} + - (void)sendTelemetryLog:(NSString *)message attributes:(NSDictionary *)attributes config:(NSDictionary *)config resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { [self.ddSdkImplementation sendTelemetryLogWithMessage:message attributes:attributes config:config resolve:resolve reject:reject]; } diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index 5b91039b3..db8c4e938 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -160,6 +160,44 @@ public class DdSdkImplementation: NSObject { resolve(nil) } + @objc + public func setAccountInfo( + accountInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + let castedAccountInfo = castAttributesToSwift(accountInfo) + let id = castedAccountInfo["id"] as? String + let name = castedAccountInfo["name"] as? String + var extraInfo: [AttributeKey: AttributeValue] = [:] + + if let extraInfoEncodable = castedAccountInfo["extraInfo"] as? AnyEncodable, + let extraInfoDict = extraInfoEncodable.value as? [String: Any] + { + extraInfo = castAttributesToSwift(extraInfoDict) + } + + if let validId = id { + Datadog.setAccountInfo(id: validId, name: name, extraInfo: extraInfo) + } + + resolve(nil) + } + + @objc + public func addAccountExtraInfo( + extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock + ) { + let castedExtraInfo = castAttributesToSwift(extraInfo) + + Datadog.addAccountExtraInfo(castedExtraInfo) + resolve(nil) + } + + @objc + public func clearAccountInfo(resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) { + Datadog.clearAccountInfo() + resolve(nil) + } + @objc public func setTrackingConsent( trackingConsent: NSString, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock diff --git a/packages/core/jest/mock.js b/packages/core/jest/mock.js index 0dc9ec138..9f5ee2c41 100644 --- a/packages/core/jest/mock.js +++ b/packages/core/jest/mock.js @@ -36,6 +36,15 @@ module.exports = { clearUserInfo: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), + setAccountInfo: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + addAccountExtraInfo: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + clearAccountInfo: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), addAttribute: jest .fn() .mockImplementation(() => new Promise(resolve => resolve())), diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index b1ea4f4c1..e07ba4e34 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -30,6 +30,7 @@ import { DdRumErrorTracking } from './rum/instrumentation/DdRumErrorTracking'; import { DdBabelInteractionTracking } from './rum/instrumentation/interactionTracking/DdBabelInteractionTracking'; import { DdRumUserInteractionTracking } from './rum/instrumentation/interactionTracking/DdRumUserInteractionTracking'; import { DdRumResourceTracking } from './rum/instrumentation/resourceTracking/DdRumResourceTracking'; +import { AccountInfoSingleton } from './sdk/AccountInfoSingleton/AccountInfoSingleton'; import { AttributesSingleton } from './sdk/AttributesSingleton/AttributesSingleton'; import type { Attributes } from './sdk/AttributesSingleton/types'; import { registerNativeBridge } from './sdk/DatadogInternalBridge/DdSdkInternalNativeBridge'; @@ -299,6 +300,71 @@ export class DdSdkReactNative { UserInfoSingleton.getInstance().setUserInfo(updatedUserInfo); }; + /** + * Sets the account information. + * @param id: A mandatory unique account identifier (relevant to your business domain). + * @param name: The account name. + * @param extraInfo: Additional information. + * @returns a Promise. + */ + static setAccountInfo = async (accountInfo: { + id: string; + name?: string; + extraInfo?: Record; + }): Promise => { + InternalLog.log( + `Setting account ${JSON.stringify(accountInfo)}`, + SdkVerbosity.DEBUG + ); + + await DdSdk.setAccountInfo(accountInfo); + AccountInfoSingleton.getInstance().setAccountInfo(accountInfo); + }; + + /** + * Clears the account information. + * @returns a Promise. + */ + static clearAccountInfo = async (): Promise => { + InternalLog.log('Clearing account info', SdkVerbosity.DEBUG); + await DdSdk.clearAccountInfo(); + AccountInfoSingleton.getInstance().clearAccountInfo(); + }; + + /** + * Set the account information. + * @param extraAccountInfo: The additional information. (To set the id or name please use setAccountInfo). + * @returns a Promise. + */ + static addAccountExtraInfo = async ( + extraAccountInfo: Record + ): Promise => { + InternalLog.log( + `Adding extra account info ${JSON.stringify(extraAccountInfo)}`, + SdkVerbosity.DEBUG + ); + + const accountInfo = AccountInfoSingleton.getInstance().getAccountInfo(); + if (!accountInfo) { + InternalLog.log( + 'Skipped adding Account Extra Info: Account Info is currently undefined. An account ID must be set before adding extra info. Please call setAccountInfo() first.', + SdkVerbosity.WARN + ); + + return; + } + + const extraInfo = { + ...accountInfo.extraInfo, + ...extraAccountInfo + }; + + await DdSdk.addAccountExtraInfo(extraInfo); + AccountInfoSingleton.getInstance().addAccountExtraInfo( + extraAccountInfo + ); + }; + /** * Set the tracking consent regarding the data collection. * @param trackingConsent: One of TrackingConsent values. diff --git a/packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts b/packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts new file mode 100644 index 000000000..5f2be8dea --- /dev/null +++ b/packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts @@ -0,0 +1,46 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import type { AccountInfo } from './types'; + +class AccountInfoProvider { + private accountInfo: AccountInfo | undefined = undefined; + + setAccountInfo = (accountInfo: AccountInfo) => { + this.accountInfo = accountInfo; + }; + + addAccountExtraInfo = (extraInfo: AccountInfo['extraInfo']) => { + if (!this.accountInfo) { + return; + } + + this.accountInfo.extraInfo = { + ...this.accountInfo.extraInfo, + ...extraInfo + }; + }; + + getAccountInfo = (): AccountInfo | undefined => { + return this.accountInfo; + }; + + clearAccountInfo = () => { + this.accountInfo = undefined; + }; +} + +export class AccountInfoSingleton { + private static accountInfoProvider = new AccountInfoProvider(); + + static getInstance = (): AccountInfoProvider => { + return AccountInfoSingleton.accountInfoProvider; + }; + + static reset = () => { + AccountInfoSingleton.accountInfoProvider = new AccountInfoProvider(); + }; +} diff --git a/packages/core/src/sdk/AccountInfoSingleton/__tests__/AccountInfoSingleton.test.ts b/packages/core/src/sdk/AccountInfoSingleton/__tests__/AccountInfoSingleton.test.ts new file mode 100644 index 000000000..9af387619 --- /dev/null +++ b/packages/core/src/sdk/AccountInfoSingleton/__tests__/AccountInfoSingleton.test.ts @@ -0,0 +1,90 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import { AccountInfoSingleton } from '../AccountInfoSingleton'; + +describe('AccountInfoSingleton', () => { + beforeEach(() => { + AccountInfoSingleton.reset(); + }); + + it('returns undefined by default', () => { + expect( + AccountInfoSingleton.getInstance().getAccountInfo() + ).toBeUndefined(); + }); + + it('stores and returns account info after `setAccountInfo`', () => { + const info = { + id: 'test', + name: 'test user', + extraInfo: { premium: true } + }; + + AccountInfoSingleton.getInstance().setAccountInfo(info); + + expect(AccountInfoSingleton.getInstance().getAccountInfo()).toEqual( + info + ); + }); + + it('adds extra account info with `addAccountExtraInfo`', () => { + const info = { + id: 'test', + name: 'test user', + extraInfo: { premium: true } + }; + + AccountInfoSingleton.getInstance().setAccountInfo(info); + AccountInfoSingleton.getInstance().addAccountExtraInfo({ + testGroup: 'A' + }); + + expect(AccountInfoSingleton.getInstance().getAccountInfo()).toEqual({ + ...info, + extraInfo: { ...info.extraInfo, testGroup: 'A' } + }); + }); + + it('clears account info with `clearAccountInfo`', () => { + AccountInfoSingleton.getInstance().setAccountInfo({ + id: 'test', + name: 'test user', + extraInfo: { premium: true } + }); + + AccountInfoSingleton.getInstance().clearAccountInfo(); + + expect( + AccountInfoSingleton.getInstance().getAccountInfo() + ).toBeUndefined(); + }); + + it('`reset()` replaces the provider and clears stored account info', () => { + const instanceBefore = AccountInfoSingleton.getInstance(); + + AccountInfoSingleton.getInstance().setAccountInfo({ + id: 'test', + name: 'test user', + extraInfo: { premium: true } + }); + + AccountInfoSingleton.reset(); + + const instanceAfter = AccountInfoSingleton.getInstance(); + + expect(instanceAfter).not.toBe(instanceBefore); + + expect(instanceAfter.getAccountInfo()).toBeUndefined(); + }); + + it('getInstance returns the same provider between calls (singleton behavior)', () => { + const a = AccountInfoSingleton.getInstance(); + const b = AccountInfoSingleton.getInstance(); + + expect(a).toBe(b); + }); +}); diff --git a/packages/core/src/sdk/AccountInfoSingleton/types.ts b/packages/core/src/sdk/AccountInfoSingleton/types.ts new file mode 100644 index 000000000..1dceb0958 --- /dev/null +++ b/packages/core/src/sdk/AccountInfoSingleton/types.ts @@ -0,0 +1,11 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +export type AccountInfo = { + readonly id: string; + readonly name?: string; + extraInfo?: Record; +}; diff --git a/packages/core/src/sdk/EventMappers/EventMapper.ts b/packages/core/src/sdk/EventMappers/EventMapper.ts index 9ca252d72..e1cbaae19 100644 --- a/packages/core/src/sdk/EventMappers/EventMapper.ts +++ b/packages/core/src/sdk/EventMappers/EventMapper.ts @@ -7,6 +7,8 @@ import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; import { DdSdk } from '../../sdk/DdSdk'; +import { AccountInfoSingleton } from '../AccountInfoSingleton/AccountInfoSingleton'; +import type { AccountInfo } from '../AccountInfoSingleton/types'; import { AttributesSingleton } from '../AttributesSingleton/AttributesSingleton'; import type { Attributes } from '../AttributesSingleton/types'; import { UserInfoSingleton } from '../UserInfoSingleton/UserInfoSingleton'; @@ -16,6 +18,7 @@ import { deepClone } from './utils/deepClone'; export type AdditionalEventDataForMapper = { userInfo?: UserInfo; + accountInfo?: AccountInfo; attributes: Attributes; }; @@ -66,9 +69,11 @@ export class EventMapper { // formatting const userInfo = UserInfoSingleton.getInstance().getUserInfo(); + const accountInfo = AccountInfoSingleton.getInstance().getAccountInfo(); const attributes = AttributesSingleton.getInstance().getAttributes(); const initialEvent = this.formatRawEventForMapper(rawEvent, { userInfo, + accountInfo, attributes }); diff --git a/packages/core/src/specs/NativeDdSdk.ts b/packages/core/src/specs/NativeDdSdk.ts index 70401fe3c..68c9c4711 100644 --- a/packages/core/src/specs/NativeDdSdk.ts +++ b/packages/core/src/specs/NativeDdSdk.ts @@ -67,6 +67,23 @@ export interface Spec extends TurboModule { */ addUserExtraInfo(extraInfo: Object): Promise; + /** + * Set the account information. + * @param account: The account object (use builtin attributes: 'id', 'name', and any custom attribute under extraInfo). + */ + setAccountInfo(account: Object): Promise; + + /** + * Clears the account information. + */ + clearAccountInfo(): Promise; + + /** + * Add custom attributes to the current account information + * @param extraInfo: The extraInfo object containing additional custom attributes + */ + addAccountExtraInfo(extraInfo: Object): Promise; + /** * Set the tracking consent regarding the data collection. * @param trackingConsent: Consent, which can take one of the following values: 'pending', 'granted', 'not_granted'. diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 5c7d64cec..7f97779ee 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -129,6 +129,27 @@ export type DdSdkType = { */ addUserExtraInfo(extraUserInfo: Record): Promise; + /** + * Sets the account information. + * @param id: A unique account identifier (relevant to your business domain) + * @param name: The account name. + * @param extraInfo: Additional information. + */ + setAccountInfo(accountInfo: AccountInfo): Promise; + + /** + * Clears the account information. + */ + clearAccountInfo(): Promise; + + /** + * Add additional account information. + * @param extraAccountInfo: The additional information. (To set the id or name please use setAccountInfo). + */ + addAccountExtraInfo( + extraAccountInfo: Record + ): Promise; + /** * Set the tracking consent regarding the data collection. * @param trackingConsent: Consent, which can take one of the following values: 'pending', 'granted', 'not_granted'. @@ -176,6 +197,12 @@ export type UserInfo = { extraInfo?: object; }; +export type AccountInfo = { + id: string; + name?: string; + extraInfo?: object; +}; + // DdLogs export type LogStatus = 'debug' | 'info' | 'warn' | 'error'; From fc6230cea5f1bed597d51e1f072be826388186b9 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Fri, 7 Nov 2025 14:49:40 +0000 Subject: [PATCH 049/168] Add `userId` and `accountId`to baggage headers Preserve user baggage header when setting session ID Enforced W3 specification for 'baggage' header Only inject Session ID header if propagator=Datadog|W3C Additional test for baggage header and minor warn message improvement --- packages/core/src/rum/DdRum.ts | 32 ++++++++------ packages/core/src/rum/__tests__/DdRum.test.ts | 2 +- packages/core/src/rum/helper.ts | 36 +++++++++++++++ .../distributedTracing/distributedTracing.tsx | 21 +++++++-- .../distributedTracingHeaders.ts | 44 ++++++++++++++++--- .../requestProxy/XHRProxy/XHRProxy.ts | 10 ++++- .../XHRProxy/__tests__/XHRProxy.test.ts | 2 +- .../XHRProxy/baggageHeaderUtils.ts | 36 +-------------- .../DdSdkInternalNativeBridge.tsx | 2 +- 9 files changed, 122 insertions(+), 63 deletions(-) create mode 100644 packages/core/src/rum/helper.ts diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index 555b144de..1e231a197 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -20,12 +20,19 @@ import { getGlobalInstance } from '../utils/singletonUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; import type { TimeProvider } from '../utils/time-provider/TimeProvider'; -import { generateActionEventMapper } from './eventMappers/actionEventMapper'; import type { ActionEventMapper } from './eventMappers/actionEventMapper'; -import { generateErrorEventMapper } from './eventMappers/errorEventMapper'; +import { generateActionEventMapper } from './eventMappers/actionEventMapper'; import type { ErrorEventMapper } from './eventMappers/errorEventMapper'; -import { generateResourceEventMapper } from './eventMappers/resourceEventMapper'; +import { generateErrorEventMapper } from './eventMappers/errorEventMapper'; import type { ResourceEventMapper } from './eventMappers/resourceEventMapper'; +import { generateResourceEventMapper } from './eventMappers/resourceEventMapper'; +import { + clearCachedSessionId, + getCachedAccountId, + getCachedSessionId, + getCachedUserId, + setCachedSessionId +} from './helper'; import type { DatadogTracingContext } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingContext'; import { DatadogTracingIdentifier } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingIdentifier'; import { TracingIdentifier } from './instrumentation/resourceTracking/distributedTracing/TracingIdentifier'; @@ -33,17 +40,12 @@ import { getTracingContext, getTracingContextForPropagators } from './instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders'; -import { - clearCachedSessionId, - getCachedSessionId, - setCachedSessionId -} from './sessionId/sessionIdHelper'; import type { DdRumType, - RumActionType, - ResourceKind, FirstPartyHost, - PropagatorType + PropagatorType, + ResourceKind, + RumActionType } from './types'; const RUM_MODULE = 'com.datadog.reactnative.rum'; @@ -396,7 +398,9 @@ class DdRumWrapper implements DdRumType { url, tracingSamplingRate, firstPartyHosts, - getCachedSessionId() + getCachedSessionId(), + getCachedUserId(), + getCachedAccountId() ); }; @@ -407,7 +411,9 @@ class DdRumWrapper implements DdRumType { return getTracingContextForPropagators( propagators, tracingSamplingRate, - getCachedSessionId() + getCachedSessionId(), + getCachedUserId(), + getCachedAccountId() ); }; diff --git a/packages/core/src/rum/__tests__/DdRum.test.ts b/packages/core/src/rum/__tests__/DdRum.test.ts index af1f4fbfa..8bdc86aac 100644 --- a/packages/core/src/rum/__tests__/DdRum.test.ts +++ b/packages/core/src/rum/__tests__/DdRum.test.ts @@ -17,11 +17,11 @@ import { DdRum } from '../DdRum'; import type { ActionEventMapper } from '../eventMappers/actionEventMapper'; import type { ErrorEventMapper } from '../eventMappers/errorEventMapper'; import type { ResourceEventMapper } from '../eventMappers/resourceEventMapper'; +import { setCachedSessionId } from '../helper'; import { DatadogTracingContext } from '../instrumentation/resourceTracking/distributedTracing/DatadogTracingContext'; import { DatadogTracingIdentifier } from '../instrumentation/resourceTracking/distributedTracing/DatadogTracingIdentifier'; import { TracingIdFormat } from '../instrumentation/resourceTracking/distributedTracing/TracingIdentifier'; import { TracingIdentifierUtils } from '../instrumentation/resourceTracking/distributedTracing/__tests__/__utils__/TracingIdentifierUtils'; -import { setCachedSessionId } from '../sessionId/sessionIdHelper'; import type { FirstPartyHost } from '../types'; import { PropagatorType, RumActionType } from '../types'; diff --git a/packages/core/src/rum/helper.ts b/packages/core/src/rum/helper.ts new file mode 100644 index 000000000..a153b79b5 --- /dev/null +++ b/packages/core/src/rum/helper.ts @@ -0,0 +1,36 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +let _cachedSessionId: string | undefined; +let _cachedUserId: string | undefined; +let _cachedAccountId: string | undefined; + +export const getCachedSessionId = () => { + return _cachedSessionId; +}; + +export const setCachedSessionId = (sessionId: string) => { + _cachedSessionId = sessionId; +}; + +export const clearCachedSessionId = () => { + _cachedSessionId = undefined; +}; + +export const getCachedUserId = () => { + return _cachedUserId; +}; + +export const setCachedUserId = (userId: string) => { + _cachedUserId = userId; +}; + +export const getCachedAccountId = () => { + return _cachedAccountId; +}; + +export const setCachedAccountId = (accountId: string) => { + _cachedAccountId = accountId; +}; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx index 9c4fcbff7..2ebb98233 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracing.tsx @@ -26,6 +26,9 @@ export type DdRumResourceTracingAttributes = rulePsr: number; propagatorTypes: PropagatorType[]; rumSessionId?: string; + userId?: string; + accountId?: string; + baggageHeaders?: Set; } | { tracingStrategy: 'DISCARD'; @@ -43,12 +46,16 @@ export const getTracingAttributes = ({ hostname, firstPartyHostsRegexMap, tracingSamplingRate, - rumSessionId + rumSessionId, + userId, + accountId }: { hostname: Hostname | null; firstPartyHostsRegexMap: RegexMap; tracingSamplingRate: number; rumSessionId?: string; + userId?: string; + accountId?: string; }): DdRumResourceTracingAttributes => { if (hostname === null) { return DISCARDED_TRACE_ATTRIBUTES; @@ -61,7 +68,9 @@ export const getTracingAttributes = ({ return generateTracingAttributesWithSampling( tracingSamplingRate, propagatorsForHost, - rumSessionId + rumSessionId, + userId, + accountId ); } return DISCARDED_TRACE_ATTRIBUTES; @@ -70,7 +79,9 @@ export const getTracingAttributes = ({ export const generateTracingAttributesWithSampling = ( tracingSamplingRate: number, propagatorTypes: PropagatorType[], - rumSessionId?: string + rumSessionId?: string, + userId?: string, + accountId?: string ): DdRumResourceTracingAttributes => { if (!propagatorTypes || propagatorTypes.length === 0) { return DISCARDED_TRACE_ATTRIBUTES; @@ -93,7 +104,9 @@ export const generateTracingAttributesWithSampling = ( tracingStrategy: 'KEEP', rulePsr: tracingSamplingRate / 100, propagatorTypes, - rumSessionId + rumSessionId, + userId, + accountId }; return tracingAttributes; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts index 8bfaec669..8ad9137c7 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/distributedTracing/distributedTracingHeaders.ts @@ -1,6 +1,5 @@ /* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ @@ -29,6 +28,8 @@ export const PARENT_ID_HEADER_KEY = 'x-datadog-parent-id'; export const TAGS_HEADER_KEY = 'x-datadog-tags'; export const DD_TRACE_ID_TAG = '_dd.p.tid'; export const DD_RUM_SESSION_ID_TAG = 'session.id'; +export const DD_RUM_USER_ID_TAG = 'user.id'; +export const DD_RUM_ACCOUNT_ID_TAG = 'account.id'; /** * OTel headers @@ -143,6 +144,29 @@ export const getTracingHeadersFromAttributes = ( } }); + if (hasDatadogOrW3CPropagator) { + if (tracingAttributes.rumSessionId) { + headers.push({ + header: BAGGAGE_HEADER_KEY, + value: `${DD_RUM_SESSION_ID_TAG}=${tracingAttributes.rumSessionId}` + }); + } + + if (tracingAttributes.userId) { + headers.push({ + header: BAGGAGE_HEADER_KEY, + value: `${DD_RUM_USER_ID_TAG}=${tracingAttributes.userId}` + }); + } + + if (tracingAttributes.accountId) { + headers.push({ + header: BAGGAGE_HEADER_KEY, + value: `${DD_RUM_ACCOUNT_ID_TAG}=${tracingAttributes.accountId}` + }); + } + } + if (hasDatadogOrW3CPropagator && tracingAttributes.rumSessionId) { headers.push({ header: BAGGAGE_HEADER_KEY, @@ -157,7 +181,9 @@ export const getTracingContext = ( url: string, tracingSamplingRate: number, firstPartyHosts: FirstPartyHost[], - rumSessionId?: string + rumSessionId?: string, + userId?: string, + accountId?: string ): DatadogTracingContext => { const hostname = URLHostParser(url); const firstPartyHostsRegexMap = firstPartyHostsRegexMapBuilder( @@ -167,7 +193,9 @@ export const getTracingContext = ( hostname, firstPartyHostsRegexMap, tracingSamplingRate, - rumSessionId + rumSessionId, + userId, + accountId }); return getTracingContextForAttributes( @@ -179,13 +207,17 @@ export const getTracingContext = ( export const getTracingContextForPropagators = ( propagators: PropagatorType[], tracingSamplingRate: number, - rumSessionId?: string + rumSessionId?: string, + userId?: string, + accountId?: string ): DatadogTracingContext => { return getTracingContextForAttributes( generateTracingAttributesWithSampling( tracingSamplingRate, propagators, - rumSessionId + rumSessionId, + userId, + accountId ), tracingSamplingRate ); diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts index d48c4f01a..723ace5ed 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/XHRProxy.ts @@ -5,7 +5,11 @@ */ import { Timer } from '../../../../../utils/Timer'; -import { getCachedSessionId } from '../../../../sessionId/sessionIdHelper'; +import { + getCachedAccountId, + getCachedSessionId, + getCachedUserId +} from '../../../../helper'; import { BAGGAGE_HEADER_KEY, getTracingHeadersFromAttributes @@ -115,7 +119,9 @@ const proxyOpen = ( hostname, firstPartyHostsRegexMap, tracingSamplingRate, - rumSessionId: getCachedSessionId() + rumSessionId: getCachedSessionId(), + userId: getCachedUserId(), + accountId: getCachedAccountId() }), baggageHeaderEntries: new Set() }; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index cfc5e0178..048dbadec 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -11,7 +11,7 @@ import { InternalLog } from '../../../../../../InternalLog'; import { SdkVerbosity } from '../../../../../../SdkVerbosity'; import { BufferSingleton } from '../../../../../../sdk/DatadogProvider/Buffer/BufferSingleton'; import { DdRum } from '../../../../../DdRum'; -import { setCachedSessionId } from '../../../../../sessionId/sessionIdHelper'; +import { setCachedSessionId } from '../../../../../helper'; import { PropagatorType } from '../../../../../types'; import { XMLHttpRequestMock } from '../../../__tests__/__utils__/XMLHttpRequestMock'; import { TracingIdentifierUtils } from '../../../distributedTracing/__tests__/__utils__/TracingIdentifierUtils'; diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts index dccae9ddf..7094ec4e8 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/baggageHeaderUtils.ts @@ -110,7 +110,7 @@ export function formatBaggageHeader(entries: Set): string | null { } const headerValue = formattedParts.join(','); - const byteLength = utf8ByteLength(headerValue); + const byteLength = Buffer.byteLength(headerValue, 'utf8'); if (byteLength > MAX_BYTES) { InternalLog.log( @@ -122,40 +122,6 @@ export function formatBaggageHeader(entries: Set): string | null { return headerValue; } -/** - * Returns the number of bytes needed to encode a string in UTF-8. - * - * Useful as a lightweight alternative to Node.js `Buffer.byteLength()` - * for older environments that do not support it. - * - * @param text - The input string. - * @returns The UTF-8 byte length of the string. - */ -function utf8ByteLength(text: string): number { - let byteLength = text.length; - for (let i = text.length - 1; i >= 0; i--) { - const code = text.charCodeAt(i); - - // 2-byte characters (U+0080 to U+07FF) - if (code > 0x7f && code <= 0x7ff) { - byteLength++; - } - // 3-byte characters (U+0800 to U+FFFF) - else if (code > 0x7ff && code <= 0xffff) { - byteLength += 2; - } - - // Handle surrogate pairs (4-byte characters, e.g. emoji) - // These characters already count as 2 in the initial length - // Encountering the low surrogate already accounts for the full 4 bytes - // (2 from the initial length + 2 for the 3-byte characters logic above) - if (code >= 0xdc00 && code <= 0xdfff) { - i--; // prevents double counting the same character by skipping high surrogate - } - } - return byteLength; -} - /** * Returns a set of valid baggage header characters. */ diff --git a/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx b/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx index bbae2cd12..34a1e623a 100644 --- a/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx +++ b/packages/core/src/sdk/DatadogInternalBridge/DdSdkInternalNativeBridge.tsx @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; -import { setCachedSessionId } from '../../rum/sessionId/sessionIdHelper'; +import { setCachedSessionId } from '../../rum/helper'; import { DatadogDefaultEventEmitter } from '../DatadogEventEmitter/DatadogDefaultEventEmitter'; import type { DatadogEventEmitter } from '../DatadogEventEmitter/DatadogEventEmitter'; From b4bff4f3489576614fd71188da8d057d6d3c7171 Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Thu, 13 Nov 2025 17:24:34 +0000 Subject: [PATCH 050/168] Cache `userId` and `accountId` when first set --- .../requestProxy/XHRProxy/__tests__/XHRProxy.test.ts | 10 +++++++++- .../sdk/AccountInfoSingleton/AccountInfoSingleton.ts | 3 +++ .../src/sdk/UserInfoSingleton/UserInfoSingleton.ts | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index 048dbadec..a18825771 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -11,7 +11,11 @@ import { InternalLog } from '../../../../../../InternalLog'; import { SdkVerbosity } from '../../../../../../SdkVerbosity'; import { BufferSingleton } from '../../../../../../sdk/DatadogProvider/Buffer/BufferSingleton'; import { DdRum } from '../../../../../DdRum'; -import { setCachedSessionId } from '../../../../../helper'; +import { + setCachedSessionId, + setCachedUserId, + setCachedAccountId +} from '../../../../../helper'; import { PropagatorType } from '../../../../../types'; import { XMLHttpRequestMock } from '../../../__tests__/__utils__/XMLHttpRequestMock'; import { TracingIdentifierUtils } from '../../../distributedTracing/__tests__/__utils__/TracingIdentifierUtils'; @@ -90,6 +94,10 @@ afterEach(() => { (Date.now as jest.MockedFunction).mockClear(); jest.spyOn(global.Math, 'random').mockRestore(); DdRum.unregisterResourceEventMapper(); + + setCachedSessionId(undefined as any); + setCachedUserId(undefined as any); + setCachedAccountId(undefined as any); }); describe('XHRProxy', () => { diff --git a/packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts b/packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts index 5f2be8dea..439c1493a 100644 --- a/packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts +++ b/packages/core/src/sdk/AccountInfoSingleton/AccountInfoSingleton.ts @@ -4,6 +4,8 @@ * Copyright 2016-Present Datadog, Inc. */ +import { setCachedAccountId } from '../../rum/helper'; + import type { AccountInfo } from './types'; class AccountInfoProvider { @@ -11,6 +13,7 @@ class AccountInfoProvider { setAccountInfo = (accountInfo: AccountInfo) => { this.accountInfo = accountInfo; + setCachedAccountId(this.accountInfo.id); }; addAccountExtraInfo = (extraInfo: AccountInfo['extraInfo']) => { diff --git a/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts b/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts index 3ce23614b..2408fdbf2 100644 --- a/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts +++ b/packages/core/src/sdk/UserInfoSingleton/UserInfoSingleton.ts @@ -4,6 +4,8 @@ * Copyright 2016-Present Datadog, Inc. */ +import { setCachedUserId } from '../../rum/helper'; + import type { UserInfo } from './types'; class UserInfoProvider { @@ -11,6 +13,7 @@ class UserInfoProvider { setUserInfo = (userInfo: UserInfo) => { this.userInfo = userInfo; + setCachedUserId(this.userInfo.id); }; getUserInfo = (): UserInfo | undefined => { From 20516670d86aea59bdd6040b2b5c6857bde3686d Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Fri, 21 Nov 2025 11:20:51 +0000 Subject: [PATCH 051/168] Integrate Feature Operations into `core` SDK --- packages/codepush/__mocks__/react-native.ts | 11 +++- packages/core/__mocks__/react-native.ts | 11 +++- .../reactnative/DdRumImplementation.kt | 54 ++++++++++++++++ .../kotlin/com/datadog/reactnative/DdRum.kt | 63 ++++++++++++++++++- .../kotlin/com/datadog/reactnative/DdRum.kt | 56 +++++++++++++++++ packages/core/ios/Sources/DdRum.mm | 43 +++++++++++++ .../ios/Sources/DdRumImplementation.swift | 51 +++++++++++++++ packages/core/jest/mock.js | 9 +++ packages/core/src/index.tsx | 3 +- packages/core/src/rum/DdRum.ts | 54 +++++++++++++++- packages/core/src/rum/types.ts | 42 ++++++++++++- packages/core/src/specs/NativeDdRum.ts | 40 ++++++++++++ packages/core/src/types.tsx | 6 ++ .../__mocks__/react-native.ts | 11 +++- 14 files changed, 447 insertions(+), 7 deletions(-) diff --git a/packages/codepush/__mocks__/react-native.ts b/packages/codepush/__mocks__/react-native.ts index 0c8189840..a87659170 100644 --- a/packages/codepush/__mocks__/react-native.ts +++ b/packages/codepush/__mocks__/react-native.ts @@ -119,7 +119,16 @@ actualRN.NativeModules.DdRum = { new Promise(resolve => resolve('test-session-id') ) - ) as jest.MockedFunction + ) as jest.MockedFunction, + startFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + succeedFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + failFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction }; module.exports = actualRN; diff --git a/packages/core/__mocks__/react-native.ts b/packages/core/__mocks__/react-native.ts index 73308f711..a4622b6b9 100644 --- a/packages/core/__mocks__/react-native.ts +++ b/packages/core/__mocks__/react-native.ts @@ -155,7 +155,16 @@ actualRN.NativeModules.DdRum = { new Promise(resolve => resolve('test-session-id') ) - ) as jest.MockedFunction + ) as jest.MockedFunction, + startFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + succeedFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + failFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction }; module.exports = actualRN; diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt index 4e3cd416f..67a299f36 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdRumImplementation.kt @@ -12,6 +12,7 @@ import com.datadog.android.rum.RumAttributes import com.datadog.android.rum.RumErrorSource import com.datadog.android.rum.RumResourceKind import com.datadog.android.rum.RumResourceMethod +import com.datadog.android.rum.featureoperations.FailureReason import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableMap @@ -333,6 +334,59 @@ class DdRumImplementation(private val datadog: DatadogWrapper = DatadogSDKWrappe } } + /** + * Starts a Feature Operation. + * + * @param name Human-readable operation name (e.g., "login_flow"). + * @param operationKey Optional key that uniquely identifies this operation instance. + * @param attributes Additional attributes to attach to the operation. + * @param promise Resolved with `null` when the call completes. + */ + fun startFeatureOperation(name: String, operationKey: String? = null, attributes: ReadableMap, promise: Promise) { + val attributesMap = attributes.toHashMap().toMutableMap() + datadog.getRumMonitor().startFeatureOperation(name, operationKey, attributesMap); + promise.resolve(null) + } + + /** + * Marks a Feature Operation as successfully completed. + * + * @param name The name of the feature operation (for example, `"login_flow"`). + * @param operationKey The key of the operation instance to complete, if one was provided when starting it. + * @param attributes A map of custom attributes to attach to this completion event. + */ + fun succeedFeatureOperation(name: String, operationKey: String? = null, attributes: ReadableMap, promise: Promise) { + val attributesMap = attributes.toHashMap().toMutableMap() + datadog.getRumMonitor().succeedFeatureOperation(name, operationKey, attributesMap) + promise.resolve(null) + } + + + /** + * Marks a Feature Operation as failed. + * + * @param name The name of the feature operation (for example, `"login_flow"`). + * @param operationKey The key of the operation instance to fail, if one was provided when starting it. + * @param failureReason The reason for the failure. Possible values are defined in [FailureReason] + * (e.g., `FailureReason.ERROR`, `FailureReason.ABANDONED`, `FailureReason.OTHER`). + * @param attributes A map of custom attributes to attach to this failure event. + */ + fun failFeatureOperation( + name: String, + operationKey: String? = null, + failureReason: String, + attributes: ReadableMap, + promise: Promise + ) { + val attributesMap = attributes.toHashMap().toMutableMap() + val reason = runCatching { + enumValueOf(failureReason.uppercase()) + }.getOrDefault(FailureReason.OTHER) + + datadog.getRumMonitor().failFeatureOperation(name, operationKey, reason, attributesMap) + promise.resolve(null) + } + // region Internal private fun String.asRumActionType(): RumActionType { diff --git a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt index 6cb2b385b..30788acff 100644 --- a/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/newarch/kotlin/com/datadog/reactnative/DdRum.kt @@ -6,6 +6,7 @@ package com.datadog.reactnative +import com.datadog.android.rum.featureoperations.FailureReason import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactMethod @@ -52,7 +53,12 @@ class DdRum( * If not provided, current timestamp will be used. */ @ReactMethod - override fun stopView(key: String, context: ReadableMap, timestampMs: Double, promise: Promise) { + override fun stopView( + key: String, + context: ReadableMap, + timestampMs: Double, + promise: Promise + ) { implementation.stopView(key, context, timestampMs, promise) } @@ -276,4 +282,59 @@ class DdRum( override fun getCurrentSessionId(promise: Promise) { implementation.getCurrentSessionId(promise) } + + /** + * Starts a RUM Feature Operation. + * + * @param name Human-readable operation name (e.g., "login_flow"). + * @param operationKey Optional key that uniquely identifies this operation instance. + * @param attributes Additional attributes to attach to the operation. + * @param promise Resolved with `null` when the call completes. + */ + @ReactMethod + override fun startFeatureOperation( + name: String, + operationKey: String?, + attributes: ReadableMap, + promise: Promise + ) { + implementation.startFeatureOperation(name, operationKey, attributes, promise) + } + + /** + * Marks a Feature Operation as successfully completed. + * + * @param name The name of the feature operation (for example, `"login_flow"`). + * @param operationKey The key of the operation instance to complete, if one was provided when starting it. + * @param attributes A map of custom attributes to attach to this completion event. + */ + @ReactMethod + override fun succeedFeatureOperation( + name: String, + operationKey: String?, + attributes: ReadableMap, + promise: Promise + ) { + implementation.succeedFeatureOperation(name, operationKey, attributes, promise) + } + + /** + * Marks a Feature Operation as failed. + * + * @param name The name of the feature operation (for example, `"login_flow"`). + * @param operationKey The key of the operation instance to fail, if one was provided when starting it. + * @param failureReason The reason for the failure. Possible values are defined in [FailureReason] + * (e.g., `FailureReason.ERROR`, `FailureReason.ABANDONED`, `FailureReason.OTHER`). + * @param attributes A map of custom attributes to attach to this failure event. + */ + @ReactMethod + override fun failFeatureOperation( + name: String, + operationKey: String?, + failureReason: String, + attributes: ReadableMap, + promise: Promise + ) { + implementation.failFeatureOperation(name, operationKey, failureReason, attributes, promise) + } } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt index a6c4965ea..4ef8409b2 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdRum.kt @@ -6,6 +6,7 @@ package com.datadog.reactnative +import com.datadog.android.rum.featureoperations.FailureReason import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule @@ -266,4 +267,59 @@ class DdRum( fun getCurrentSessionId(promise: Promise) { implementation.getCurrentSessionId(promise) } + + /** + * Starts a RUM Feature Operation. + * + * @param name Human-readable operation name (e.g., "login_flow"). + * @param operationKey Optional key that uniquely identifies this operation instance. + * @param attributes Additional attributes to attach to the operation. + * @param promise Resolved with `null` when the call completes. + */ + @ReactMethod + fun startFeatureOperation( + name: String, + operationKey: String? = null, + attributes: ReadableMap, + promise: Promise + ) { + implementation.startFeatureOperation(name, operationKey, attributes, promise) + } + + /** + * Marks a Feature Operation as successfully completed. + * + * @param name The name of the feature operation (for example, "login_flow"). + * @param operationKey The key of the operation instance to complete, if one was provided. + * @param attributes A map of custom attributes to attach to this completion event. + */ + @ReactMethod + fun succeedFeatureOperation( + name: String, + operationKey: String? = null, + attributes: ReadableMap, + promise: Promise + ) { + implementation.succeedFeatureOperation(name, operationKey, attributes, promise) + } + + /** + * Marks a Feature Operation as failed. + * + * @param name The name of the feature operation (for example, "login_flow"). + * @param operationKey The key of the operation instance to fail, if one was provided. + * @param failureReason The reason for the failure. Values are defined in [FailureReason] + * (e.g., `FailureReason.ERROR`, `FailureReason.ABANDONED`, `FailureReason.OTHER`). + * @param attributes A map of custom attributes to attach to this failure event. + */ + @ReactMethod + fun failFeatureOperation( + name: String, + operationKey: String? = null, + failureReason: String, + attributes: ReadableMap, + promise: Promise + ) { + implementation.failFeatureOperation(name, operationKey, failureReason, attributes, promise) + } } diff --git a/packages/core/ios/Sources/DdRum.mm b/packages/core/ios/Sources/DdRum.mm index f5c324ce8..c891537f3 100644 --- a/packages/core/ios/Sources/DdRum.mm +++ b/packages/core/ios/Sources/DdRum.mm @@ -164,6 +164,37 @@ @implementation DdRum [self getCurrentSessionId:resolve reject:reject]; } +RCT_REMAP_METHOD(startFeatureOperation, + startWithName:(NSString*)name + withOperationKey:(NSString*)operationKey + withAttributes:(NSDictionary*)attributes + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self startFeatureOperation:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject]; +} + +RCT_REMAP_METHOD(succeedFeatureOperation, + succeedWithName:(NSString*)name + withOperationKey:(NSString*)operationKey + withAttributes:(NSDictionary*)attributes + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self succeedFeatureOperation:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject]; +} + +RCT_REMAP_METHOD(failFeatureOperation, + failWithName:(NSString*)name + withOperationKey:(NSString*)operationKey + withReason:(NSString*)reason + withAttributes:(NSDictionary*)attributes + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) +{ + [self failFeatureOperation:name operationKey:operationKey reason:reason attributes:attributes resolve:resolve reject:reject]; +} + // Thanks to this guard, we won't compile this code when we build for the old architecture. #ifdef RCT_NEW_ARCH_ENABLED - (std::shared_ptr)getTurboModule: @@ -257,4 +288,16 @@ - (void)stopView:(NSString *)key context:(NSDictionary *)context timestampMs:(do [self.ddRumImplementation stopViewWithKey:key context:context timestampMs:timestampMs resolve:resolve reject:reject]; } +- (void) startFeatureOperation:(NSString *)name operationKey:(NSString *)operationKey attributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation startFeatureOperationWithName:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject]; +} + +- (void) succeedFeatureOperation:(NSString *)name operationKey:(NSString *)operationKey attributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation succeedFeatureOperationWithName:name operationKey:operationKey attributes:attributes resolve:resolve reject:reject]; +} + +- (void) failFeatureOperation:(NSString *)name operationKey:(NSString *)operationKey reason:(NSString *)reason attributes:(NSDictionary *)attributes resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject { + [self.ddRumImplementation failFeatureOperationWithName:name operationKey:operationKey reason:reason attributes:attributes resolve:resolve reject:reject]; +} + @end diff --git a/packages/core/ios/Sources/DdRumImplementation.swift b/packages/core/ios/Sources/DdRumImplementation.swift index 6fac21f82..0ac8a19bf 100644 --- a/packages/core/ios/Sources/DdRumImplementation.swift +++ b/packages/core/ios/Sources/DdRumImplementation.swift @@ -63,6 +63,16 @@ private extension RUMMethod { } } +internal extension RUMFeatureOperationFailureReason { + init(from string: String) { + switch string.lowercased() { + case "error": self = .error + case "abandoned": self = .abandoned + default: self = .other + } + } +} + @objc public class DdRumImplementation: NSObject { internal static let timestampKey = "_dd.timestamp" @@ -236,6 +246,47 @@ public class DdRumImplementation: NSObject { resolve(sessionId) } } + + @objc + public func startFeatureOperation( + name: String, + operationKey: String?, + attributes: NSDictionary, + resolve: @escaping (Any?) -> Void, + reject: RCTPromiseRejectBlock + ){ + let castedAttributes = castAttributesToSwift(attributes) + nativeRUM.startFeatureOperation(name: name, operationKey: operationKey, attributes: castedAttributes) + resolve(nil) + } + + @objc + public func succeedFeatureOperation( + name: String, + operationKey: String?, + attributes: NSDictionary, + resolve: @escaping (Any?) -> Void, + reject: RCTPromiseRejectBlock + ){ + let castedAttributes = castAttributesToSwift(attributes) + nativeRUM.succeedFeatureOperation(name: name, operationKey: operationKey, attributes: castedAttributes) + resolve(nil) + } + + @objc + public func failFeatureOperation( + name: String, + operationKey: String?, + reason: String, + attributes: NSDictionary, + resolve: @escaping (Any?) -> Void, + reject: RCTPromiseRejectBlock + ){ + let castedAttributes = castAttributesToSwift(attributes) + nativeRUM.failFeatureOperation(name: name, operationKey: operationKey, + reason: RUMFeatureOperationFailureReason(from: reason), attributes: castedAttributes) + resolve(nil) + } // MARK: - Private methods diff --git a/packages/core/jest/mock.js b/packages/core/jest/mock.js index 9f5ee2c41..aadc79d27 100644 --- a/packages/core/jest/mock.js +++ b/packages/core/jest/mock.js @@ -154,6 +154,15 @@ module.exports = { .mockImplementation( () => new Promise(resolve => resolve('test-session-id')) ), + startFeatureOperation: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + succeedFeatureOperation: jest + .fn() + .mockImplementation(() => new Promise(resolve => resolve())), + failFeatureOperation: jest + .fn() + .mockImplementation(() => new Promise() < (resolve => resolve())), setTimeProvider: jest.fn().mockImplementation(() => {}), timeProvider: jest.fn().mockReturnValue(undefined), getTracingContext: jest.fn().mockReturnValue(undefined), diff --git a/packages/core/src/index.tsx b/packages/core/src/index.tsx index 062fecc90..ce39c9dfa 100644 --- a/packages/core/src/index.tsx +++ b/packages/core/src/index.tsx @@ -42,7 +42,7 @@ import { DatadogProvider } from './sdk/DatadogProvider/DatadogProvider'; import { DdSdk } from './sdk/DdSdk'; import { FileBasedConfiguration } from './sdk/FileBasedConfiguration/FileBasedConfiguration'; import { DdTrace } from './trace/DdTrace'; -import { ErrorSource } from './types'; +import { ErrorSource, FeatureOperationFailure } from './types'; import { DefaultTimeProvider } from './utils/time-provider/DefaultTimeProvider'; import type { Timestamp } from './utils/time-provider/TimeProvider'; import { TimeProvider } from './utils/time-provider/TimeProvider'; @@ -57,6 +57,7 @@ export { DdRum, RumActionType, ErrorSource, + FeatureOperationFailure, DdSdkReactNativeConfiguration, DdSdkReactNative, DdSdk, diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index 1e231a197..6f4a63fc8 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -14,7 +14,7 @@ import type { Attributes } from '../sdk/AttributesSingleton/types'; import { bufferVoidNativeCall } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; import { DdSdk } from '../sdk/DdSdk'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; -import type { ErrorSource } from '../types'; +import type { ErrorSource, FeatureOperationFailure } from '../types'; import { validateContext } from '../utils/argsUtils'; import { getGlobalInstance } from '../utils/singletonUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; @@ -133,6 +133,58 @@ class DdRumWrapper implements DdRumType { return this.callNativeStopAction(...nativeCallArgs); }; + startFeatureOperation( + name: string, + operationKey: string | null, + attributes: object + ): Promise { + InternalLog.log( + `Starting feature operation “${name}” (${operationKey})`, + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.startFeatureOperation(name, operationKey, attributes) + ); + } + + succeedFeatureOperation( + name: string, + operationKey: string | null, + attributes: object + ): Promise { + InternalLog.log( + `Succeding feature operation “${name}” (${operationKey})`, + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.succeedFeatureOperation( + name, + operationKey, + attributes + ) + ); + } + + failFeatureOperation( + name: string, + operationKey: string | null, + reason: FeatureOperationFailure, + attributes: object + ): Promise { + InternalLog.log( + `Failing feature operation “${name}” (${operationKey})`, + SdkVerbosity.DEBUG + ); + return bufferVoidNativeCall(() => + this.nativeRum.failFeatureOperation( + name, + operationKey, + reason, + attributes + ) + ); + } + setTimeProvider = (timeProvider: TimeProvider): void => { this.timeProvider = timeProvider; }; diff --git a/packages/core/src/rum/types.ts b/packages/core/src/rum/types.ts index 3def7f0e6..b879010f7 100644 --- a/packages/core/src/rum/types.ts +++ b/packages/core/src/rum/types.ts @@ -5,7 +5,7 @@ */ import type { Attributes } from '../sdk/AttributesSingleton/types'; -import type { ErrorSource } from '../types'; +import type { ErrorSource, FeatureOperationFailure } from '../types'; import type { DatadogTracingContext } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingContext'; import type { DatadogTracingIdentifier } from './instrumentation/resourceTracking/distributedTracing/DatadogTracingIdentifier'; @@ -230,6 +230,46 @@ export type DdRumType = { * Generates a unique 128bit Span ID. */ generateSpanId(): DatadogTracingIdentifier; + + /** + * Starts a Feature Operation, representing a high-level logical flow within your application (e.g., `login_flow`). + * @param name - The name of the feature operation (for example, `"login_flow"`). + * @param operationKey - An optional key to uniquely identify a specific instance of this operation when multiple are running concurrently. + * @param attributes - Custom attributes to attach to this operation. + */ + startFeatureOperation( + name: string, + operationKey: string | null, + attributes: object + ): Promise; + + /** + * Marks a Feature Operation as successfully completed. + * Should be called when a previously started operation (via `startFeatureOperation`) finishes without error. + * @param name - The name of the feature operation (for example, `"login_flow"`). + * @param operationKey - The key for the operation instance to complete, if it was specified when starting it. + * @param attributes - Custom attributes to attach to this operation’s completion event. + */ + succeedFeatureOperation( + name: string, + operationKey: string | null, + attributes: object + ): Promise; + + /** + * Marks a Feature Operation as failed. + * Should be called when a previously started operation (via `startFeatureOperation`) ends with an error. + * @param name - The name of the feature operation (for example, `"login_flow"`). + * @param operationKey - The key for the operation instance to fail, if it was specified when starting it. + * @param reason - The reason for the failure. + * @param attributes - Custom attributes to attach to this operation’s failure event. + */ + failFeatureOperation( + name: string, + operationKey: string | null, + reason: FeatureOperationFailure, + attributes: object + ): Promise; }; /** diff --git a/packages/core/src/specs/NativeDdRum.ts b/packages/core/src/specs/NativeDdRum.ts index e31f5b925..9c0c459b3 100644 --- a/packages/core/src/specs/NativeDdRum.ts +++ b/packages/core/src/specs/NativeDdRum.ts @@ -185,6 +185,46 @@ export interface Spec extends TurboModule { * Get current Session ID, or `undefined` if not available. */ getCurrentSessionId(): Promise; + + /** + * Starts a Feature Operation, representing a high-level logical flow within your application (e.g., `login_flow`). + * @param name - The name of the feature operation (for example, `"login_flow"`). + * @param operationKey - An optional key to uniquely identify a specific instance of this operation when multiple are running concurrently. + * @param attributes - Custom attributes to attach to this operation. + */ + startFeatureOperation( + name: string, + operationKey: string | null, + attributes: Object + ): Promise; + + /** + * Marks a Feature Operation as successfully completed. + * Should be called when a previously started operation (via `startFeatureOperation`) finishes without error. + * @param name - The name of the feature operation (for example, `"login_flow"`). + * @param operationKey - The key for the operation instance to complete, if it was specified when starting it. + * @param attributes - Custom attributes to attach to this operation’s completion event. + */ + succeedFeatureOperation( + name: string, + operationKey: string | null, + attributes: Object + ): Promise; + + /** + * Marks a Feature Operation as failed. + * Should be called when a previously started operation (via `startFeatureOperation`) ends with an error. + * @param name - The name of the feature operation (for example, `"login_flow"`). + * @param operationKey - The key for the operation instance to fail, if it was specified when starting it. + * @param reason - The reason for the failure. + * @param attributes - Custom attributes to attach to this operation’s failure event. + */ + failFeatureOperation( + name: string, + operationKey: string | null, + reason: string, + attributes: Object + ): Promise; } // eslint-disable-next-line import/no-default-export diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index 7f97779ee..cd697c04b 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -232,3 +232,9 @@ export enum ErrorSource { WEBVIEW = 'WEBVIEW', CUSTOM = 'CUSTOM' } + +export enum FeatureOperationFailure { + ERROR = 'ERROR', + ABANDONED = 'ABANDONED', + OTHER = 'OTHER' +} diff --git a/packages/react-native-apollo-client/__mocks__/react-native.ts b/packages/react-native-apollo-client/__mocks__/react-native.ts index bbac607d3..ded32499f 100644 --- a/packages/react-native-apollo-client/__mocks__/react-native.ts +++ b/packages/react-native-apollo-client/__mocks__/react-native.ts @@ -110,7 +110,16 @@ actualRN.NativeModules.DdRum = { new Promise(resolve => resolve('test-session-id') ) - ) as jest.MockedFunction + ) as jest.MockedFunction, + startFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + succeedFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction, + failFeatureOperation: jest.fn().mockImplementation( + () => new Promise(resolve => resolve()) + ) as jest.MockedFunction }; module.exports = actualRN; From 815e14635cd2295c305ad4c4901cf765fff90311 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Tue, 18 Nov 2025 09:57:58 +0100 Subject: [PATCH 052/168] Remove defaultPrivacyLevel from Session Replay --- ...lemetryConfigurationEventForgeryFactory.kt | 1 - .../src/SessionReplay.ts | 41 +------------------ .../src/__tests__/SessionReplay.test.ts | 38 +---------------- 3 files changed, 2 insertions(+), 78 deletions(-) diff --git a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt index 10e894e6e..684d92c21 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/tools/unit/forge/TelemetryConfigurationEventForgeryFactory.kt @@ -86,7 +86,6 @@ internal class TelemetryConfigurationEventForgeryFactory : ) } }, - defaultPrivacyLevel = forge.aNullable { aString() }, enablePrivacyForActionName = forge.aNullable { aBool() }, useExcludedActivityUrls = forge.aNullable { aBool() }, useWorkerUrl = forge.aNullable { aBool() }, diff --git a/packages/react-native-session-replay/src/SessionReplay.ts b/packages/react-native-session-replay/src/SessionReplay.ts index 60d4e8ff8..516b8ffb4 100644 --- a/packages/react-native-session-replay/src/SessionReplay.ts +++ b/packages/react-native-session-replay/src/SessionReplay.ts @@ -95,15 +95,6 @@ export interface SessionReplayConfiguration { * Default: `true`. */ startRecordingImmediately?: boolean; - - /** - * Defines the way sensitive content (e.g. text) should be masked. - * - * Default `SessionReplayPrivacy.MASK`. - * @deprecated Use {@link imagePrivacyLevel}, {@link touchPrivacyLevel} and {@link textAndInputPrivacyLevel} instead. - * Note: setting this property (`defaultPrivacyLevel`) will override the individual privacy levels. - */ - defaultPrivacyLevel?: SessionReplayPrivacy; } type InternalBaseSessionReplayConfiguration = { @@ -121,11 +112,8 @@ type InternalPrivacySessionReplayConfiguration = { type InternalSessionReplayConfiguration = InternalBaseSessionReplayConfiguration & InternalPrivacySessionReplayConfiguration; -const DEFAULTS: InternalSessionReplayConfiguration & { - defaultPrivacyLevel: SessionReplayPrivacy; -} = { +const DEFAULTS: InternalSessionReplayConfiguration = { replaySampleRate: 100, - defaultPrivacyLevel: SessionReplayPrivacy.MASK, customEndpoint: '', imagePrivacyLevel: ImagePrivacyLevel.MASK_ALL, touchPrivacyLevel: TouchPrivacyLevel.HIDE, @@ -175,33 +163,6 @@ export class SessionReplayWrapper { DEFAULTS.textAndInputPrivacyLevel }; - // Legacy Default Privacy Level property handling - if (configuration.defaultPrivacyLevel) { - switch (configuration.defaultPrivacyLevel) { - case SessionReplayPrivacy.MASK: - privacyConfig.imagePrivacyLevel = - ImagePrivacyLevel.MASK_ALL; - privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.HIDE; - privacyConfig.textAndInputPrivacyLevel = - TextAndInputPrivacyLevel.MASK_ALL; - break; - case SessionReplayPrivacy.MASK_USER_INPUT: - privacyConfig.imagePrivacyLevel = - ImagePrivacyLevel.MASK_NONE; - privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.HIDE; - privacyConfig.textAndInputPrivacyLevel = - TextAndInputPrivacyLevel.MASK_ALL_INPUTS; - break; - case SessionReplayPrivacy.ALLOW: - privacyConfig.imagePrivacyLevel = - ImagePrivacyLevel.MASK_NONE; - privacyConfig.touchPrivacyLevel = TouchPrivacyLevel.SHOW; - privacyConfig.textAndInputPrivacyLevel = - TextAndInputPrivacyLevel.MASK_SENSITIVE_INPUTS; - break; - } - } - return { ...baseConfig, ...privacyConfig }; }; diff --git a/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts b/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts index c755fc6e2..43f449cf7 100644 --- a/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts +++ b/packages/react-native-session-replay/src/__tests__/SessionReplay.test.ts @@ -9,7 +9,6 @@ import { NativeModules } from 'react-native'; import { ImagePrivacyLevel, SessionReplay, - SessionReplayPrivacy, TextAndInputPrivacyLevel, TouchPrivacyLevel } from '../SessionReplay'; @@ -41,27 +40,9 @@ describe('SessionReplay', () => { ); }); - it('calls native session replay with provided configuration { w defaultPrivacyLevel = ALLOW }', () => { + it('calls native session replay with provided configuration { w custom endpoint }', () => { SessionReplay.enable({ replaySampleRate: 100, - defaultPrivacyLevel: SessionReplayPrivacy.ALLOW, - customEndpoint: 'https://session-replay.example.com' - }); - - expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith( - 100, - 'https://session-replay.example.com', - 'MASK_NONE', - 'SHOW', - 'MASK_SENSITIVE_INPUTS', - true - ); - }); - - it('calls native session replay with provided configuration { w defaultPrivacyLevel = MASK }', () => { - SessionReplay.enable({ - replaySampleRate: 100, - defaultPrivacyLevel: SessionReplayPrivacy.MASK, customEndpoint: 'https://session-replay.example.com' }); @@ -75,23 +56,6 @@ describe('SessionReplay', () => { ); }); - it('calls native session replay with provided configuration { w defaultPrivacyLevel = MASK_USER_INPUT }', () => { - SessionReplay.enable({ - replaySampleRate: 100, - defaultPrivacyLevel: SessionReplayPrivacy.MASK_USER_INPUT, - customEndpoint: 'https://session-replay.example.com' - }); - - expect(NativeModules.DdSessionReplay.enable).toHaveBeenCalledWith( - 100, - 'https://session-replay.example.com', - 'MASK_NONE', - 'HIDE', - 'MASK_ALL_INPUTS', - true - ); - }); - it('calls native session replay with provided configuration { w random privacy levels }', () => { const TIMES = 20; From 981f719288ff32f9084953f10d3176a741e19ea3 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 28 Nov 2025 11:49:29 +0100 Subject: [PATCH 053/168] Bump Native SDKs to 3.3.0 --- benchmarks/android/app/build.gradle | 2 +- benchmarks/ios/Podfile.lock | 74 +++++++------- example-new-architecture/ios/Podfile.lock | 70 +++++++------- example/ios/Podfile.lock | 96 +++++++++---------- packages/core/DatadogSDKReactNative.podspec | 12 +-- packages/core/android/build.gradle | 8 +- ...DatadogSDKReactNativeSessionReplay.podspec | 2 +- .../android/build.gradle | 4 +- .../DatadogSDKReactNativeWebView.podspec | 4 +- .../react-native-webview/android/build.gradle | 2 +- 10 files changed, 137 insertions(+), 137 deletions(-) diff --git a/benchmarks/android/app/build.gradle b/benchmarks/android/app/build.gradle index 04c240bd0..ba6ae7a53 100644 --- a/benchmarks/android/app/build.gradle +++ b/benchmarks/android/app/build.gradle @@ -129,5 +129,5 @@ dependencies { // Benchmark tools from dd-sdk-android are used for vitals recording // Remember to bump thid alongside the main dd-sdk-android dependencies - implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.1.0") + implementation("com.datadoghq:dd-sdk-android-benchmark-internal:3.3.0") } diff --git a/benchmarks/ios/Podfile.lock b/benchmarks/ios/Podfile.lock index 1d484ae0c..5ea481159 100644 --- a/benchmarks/ios/Podfile.lock +++ b/benchmarks/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogCrashReporting (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogCore (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogCrashReporting (3.3.0): + - DatadogInternal (= 3.3.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.1.0) - - DatadogLogs (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogRUM (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogInternal (3.3.0) + - DatadogLogs (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogRUM (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogSDKReactNative (2.13.2): + - DatadogCore (= 3.3.0) + - DatadogCrashReporting (= 3.3.0) + - DatadogLogs (= 3.3.0) + - DatadogRUM (= 3.3.0) + - DatadogTrace (= 3.3.0) + - DatadogWebViewTracking (= 3.3.0) - DoubleConversion - glog - hermes-engine @@ -39,7 +39,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.1.0) + - DatadogSessionReplay (= 3.3.0) - DoubleConversion - glog - hermes-engine @@ -60,10 +60,10 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.12.1): - - DatadogInternal (= 3.1.0) + - DatadogSDKReactNativeWebView (2.13.2): + - DatadogInternal (= 3.3.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.1.0) + - DatadogWebViewTracking (= 3.3.0) - DoubleConversion - glog - hermes-engine @@ -84,13 +84,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSessionReplay (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogTrace (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogSessionReplay (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogTrace (3.3.0): + - DatadogInternal (= 3.3.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogWebViewTracking (3.3.0): + - DatadogInternal (= 3.3.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.78.2) @@ -2070,17 +2070,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90 - DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d - DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d - DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc - DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 - DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: 8e0f39de38621d4d7ed961a74d8a216fd3a38321 - DatadogSDKReactNativeSessionReplay: f9288c8e981dcc65d1f727b01421ee9a7601e75f - DatadogSDKReactNativeWebView: 993527f6c5d38e0fcc4804a6a60c334dd199dc5b - DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 - DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 - DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 + DatadogCore: 9b1256ac9c27a07087d6214c8546acf756e40be7 + DatadogCrashReporting: 89a00886ef40808bffb8ccb4b6531e472f52e213 + DatadogInternal: 21dac5a7db548da6368a096d0714bdbec66deb6c + DatadogLogs: 355a4ac6bce3f0cb8231819e475c03dbbdd7957c + DatadogRUM: 1b3a47a9b9a5a25890f7fb3aa1f2bd86009d1086 + DatadogSDKReactNative: b364ddf133b4d774f3f7bfb5fc98232f960f5331 + DatadogSDKReactNativeSessionReplay: 73b5b7d46abe2ea8ffcaccb0c6232e49c0e27591 + DatadogSDKReactNativeWebView: 0310cc142fb39e185112e79f196f99f856a96c31 + DatadogSessionReplay: 85e63d3c5e5618c3029726d00595750a73c0920a + DatadogTrace: f13e8c09981787d6cb0a4b7fd1991351fab6d64b + DatadogWebViewTracking: 08fe084b5f57da05c1610fab49ce7bc84226141e DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: e32d34492c519a2194ec9d7f5e7a79d11b73f91c diff --git a/example-new-architecture/ios/Podfile.lock b/example-new-architecture/ios/Podfile.lock index c0cd90bf6..38e172b0e 100644 --- a/example-new-architecture/ios/Podfile.lock +++ b/example-new-architecture/ios/Podfile.lock @@ -1,22 +1,22 @@ PODS: - boost (1.84.0) - - DatadogCore (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogCrashReporting (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogCore (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogCrashReporting (3.3.0): + - DatadogInternal (= 3.3.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.1.0) - - DatadogLogs (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogRUM (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogInternal (3.3.0) + - DatadogLogs (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogRUM (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogSDKReactNative (2.13.2): + - DatadogCore (= 3.3.0) + - DatadogCrashReporting (= 3.3.0) + - DatadogLogs (= 3.3.0) + - DatadogRUM (= 3.3.0) + - DatadogTrace (= 3.3.0) + - DatadogWebViewTracking (= 3.3.0) - DoubleConversion - glog - hermes-engine @@ -37,13 +37,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNative/Tests (2.12.1): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogSDKReactNative/Tests (2.13.2): + - DatadogCore (= 3.3.0) + - DatadogCrashReporting (= 3.3.0) + - DatadogLogs (= 3.3.0) + - DatadogRUM (= 3.3.0) + - DatadogTrace (= 3.3.0) + - DatadogWebViewTracking (= 3.3.0) - DoubleConversion - glog - hermes-engine @@ -64,11 +64,11 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogTrace (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogTrace (3.3.0): + - DatadogInternal (= 3.3.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogWebViewTracking (3.3.0): + - DatadogInternal (= 3.3.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1850,14 +1850,14 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d - DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d - DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc - DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 - DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: 069ea9876220b2d09b0f4b180ce571b1b6ecbb35 - DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 - DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 + DatadogCore: 9b1256ac9c27a07087d6214c8546acf756e40be7 + DatadogCrashReporting: 89a00886ef40808bffb8ccb4b6531e472f52e213 + DatadogInternal: 21dac5a7db548da6368a096d0714bdbec66deb6c + DatadogLogs: 355a4ac6bce3f0cb8231819e475c03dbbdd7957c + DatadogRUM: 1b3a47a9b9a5a25890f7fb3aa1f2bd86009d1086 + DatadogSDKReactNative: 3c756b98ff379907842eb3769d44e6b8b570385f + DatadogTrace: f13e8c09981787d6cb0a4b7fd1991351fab6d64b + DatadogWebViewTracking: 08fe084b5f57da05c1610fab49ce7bc84226141e DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 3849c9558..8ae79d75b 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -1,34 +1,34 @@ PODS: - boost (1.84.0) - - DatadogCore (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogCrashReporting (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogCore (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogCrashReporting (3.3.0): + - DatadogInternal (= 3.3.0) - PLCrashReporter (~> 1.12.0) - - DatadogInternal (3.1.0) - - DatadogLogs (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogRUM (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogSDKReactNative (2.12.1): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogInternal (3.3.0) + - DatadogLogs (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogRUM (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogSDKReactNative (2.13.2): + - DatadogCore (= 3.3.0) + - DatadogCrashReporting (= 3.3.0) + - DatadogLogs (= 3.3.0) + - DatadogRUM (= 3.3.0) + - DatadogTrace (= 3.3.0) + - DatadogWebViewTracking (= 3.3.0) - React-Core - - DatadogSDKReactNative/Tests (2.12.1): - - DatadogCore (= 3.1.0) - - DatadogCrashReporting (= 3.1.0) - - DatadogLogs (= 3.1.0) - - DatadogRUM (= 3.1.0) - - DatadogTrace (= 3.1.0) - - DatadogWebViewTracking (= 3.1.0) + - DatadogSDKReactNative/Tests (2.13.2): + - DatadogCore (= 3.3.0) + - DatadogCrashReporting (= 3.3.0) + - DatadogLogs (= 3.3.0) + - DatadogRUM (= 3.3.0) + - DatadogTrace (= 3.3.0) + - DatadogWebViewTracking (= 3.3.0) - React-Core - DatadogSDKReactNativeSessionReplay (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.1.0) + - DatadogSessionReplay (= 3.3.0) - DoubleConversion - glog - hermes-engine @@ -51,7 +51,7 @@ PODS: - Yoga - DatadogSDKReactNativeSessionReplay/Tests (2.14.1): - DatadogSDKReactNative - - DatadogSessionReplay (= 3.1.0) + - DatadogSessionReplay (= 3.3.0) - DoubleConversion - glog - hermes-engine @@ -73,25 +73,25 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga - - DatadogSDKReactNativeWebView (2.12.1): - - DatadogInternal (= 3.1.0) + - DatadogSDKReactNativeWebView (2.13.2): + - DatadogInternal (= 3.3.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.1.0) + - DatadogWebViewTracking (= 3.3.0) - React-Core - - DatadogSDKReactNativeWebView/Tests (2.12.1): - - DatadogInternal (= 3.1.0) + - DatadogSDKReactNativeWebView/Tests (2.13.2): + - DatadogInternal (= 3.3.0) - DatadogSDKReactNative - - DatadogWebViewTracking (= 3.1.0) + - DatadogWebViewTracking (= 3.3.0) - React-Core - react-native-webview - React-RCTText - - DatadogSessionReplay (3.1.0): - - DatadogInternal (= 3.1.0) - - DatadogTrace (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogSessionReplay (3.3.0): + - DatadogInternal (= 3.3.0) + - DatadogTrace (3.3.0): + - DatadogInternal (= 3.3.0) - OpenTelemetrySwiftApi (= 1.13.1) - - DatadogWebViewTracking (3.1.0): - - DatadogInternal (= 3.1.0) + - DatadogWebViewTracking (3.3.0): + - DatadogInternal (= 3.3.0) - DoubleConversion (1.1.6) - fast_float (6.1.4) - FBLazyVector (0.76.9) @@ -1988,17 +1988,17 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 1dca942403ed9342f98334bf4c3621f011aa7946 - DatadogCore: d2f51c7fb4308cf3c25e55e2e7242e5d558ee71d - DatadogCrashReporting: f636f1d1c534572c0b0abcdc59df244c884d825d - DatadogInternal: 7837b2ce3d525d429682532eeda697b181299fdc - DatadogLogs: 250894b5a99da5b924a019049c0d0326823cdbd6 - DatadogRUM: 0d2a60e1abb8aacfb8827ef84f6d5deb4d5026c8 - DatadogSDKReactNative: af351a4e1ce08124c290c52de94b0062a166cc67 - DatadogSDKReactNativeSessionReplay: dcbd55d9d0f2b86026996a8b7ec9654922d5dfe1 - DatadogSDKReactNativeWebView: 096ac87eb753b6a217b93441983264b9837c3b7e - DatadogSessionReplay: 6bc71888e2b41dd0de3325f06f0c0b3cee0e6df4 - DatadogTrace: f59e933074cd285ad7e9f5af991f8fe04b095991 - DatadogWebViewTracking: 9bc92b4147aeed47eb1911451f651094aa6dd6c1 + DatadogCore: 9b1256ac9c27a07087d6214c8546acf756e40be7 + DatadogCrashReporting: 89a00886ef40808bffb8ccb4b6531e472f52e213 + DatadogInternal: 21dac5a7db548da6368a096d0714bdbec66deb6c + DatadogLogs: 355a4ac6bce3f0cb8231819e475c03dbbdd7957c + DatadogRUM: 1b3a47a9b9a5a25890f7fb3aa1f2bd86009d1086 + DatadogSDKReactNative: 96c64d4627096497594113ffb0c86ae72490b17c + DatadogSDKReactNativeSessionReplay: 02ea3eefd261341d2ae839882351be3d209376d0 + DatadogSDKReactNativeWebView: 83cd1a58da38a7a4bd554051d6742138e36e3589 + DatadogSessionReplay: 85e63d3c5e5618c3029726d00595750a73c0920a + DatadogTrace: f13e8c09981787d6cb0a4b7fd1991351fab6d64b + DatadogWebViewTracking: 08fe084b5f57da05c1610fab49ce7bc84226141e DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385 fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6 FBLazyVector: 7605ea4810e0e10ae4815292433c09bf4324ba45 diff --git a/packages/core/DatadogSDKReactNative.podspec b/packages/core/DatadogSDKReactNative.podspec index c0d235304..692dd23c8 100644 --- a/packages/core/DatadogSDKReactNative.podspec +++ b/packages/core/DatadogSDKReactNative.podspec @@ -19,14 +19,14 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the versions in sync with DatadogSDKReactNativeSessionReplay.podspec - s.dependency 'DatadogCore', '3.1.0' - s.dependency 'DatadogLogs', '3.1.0' - s.dependency 'DatadogTrace', '3.1.0' - s.dependency 'DatadogRUM', '3.1.0' - s.dependency 'DatadogCrashReporting', '3.1.0' + s.dependency 'DatadogCore', '3.3.0' + s.dependency 'DatadogLogs', '3.3.0' + s.dependency 'DatadogTrace', '3.3.0' + s.dependency 'DatadogRUM', '3.3.0' + s.dependency 'DatadogCrashReporting', '3.3.0' # DatadogWebViewTracking is not available for tvOS - s.ios.dependency 'DatadogWebViewTracking', '3.1.0' + s.ios.dependency 'DatadogWebViewTracking', '3.3.0' s.test_spec 'Tests' do |test_spec| test_spec.source_files = 'ios/Tests/**/*.{swift,json}' diff --git a/packages/core/android/build.gradle b/packages/core/android/build.gradle index 1344b2531..bf7a2ef9c 100644 --- a/packages/core/android/build.gradle +++ b/packages/core/android/build.gradle @@ -195,10 +195,10 @@ dependencies { } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" compileOnly "com.squareup.okhttp3:okhttp:3.12.13" - implementation "com.datadoghq:dd-sdk-android-rum:3.2.0" - implementation "com.datadoghq:dd-sdk-android-logs:3.2.0" - implementation "com.datadoghq:dd-sdk-android-trace:3.2.0" - implementation "com.datadoghq:dd-sdk-android-webview:3.2.0" + implementation "com.datadoghq:dd-sdk-android-rum:3.3.0" + implementation "com.datadoghq:dd-sdk-android-logs:3.3.0" + implementation "com.datadoghq:dd-sdk-android-trace:3.3.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.3.0" implementation "com.google.code.gson:gson:2.10.0" testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" testImplementation "org.junit.jupiter:junit-jupiter-api:5.6.2" diff --git a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec index 6a5d0b78f..e0b032a9e 100644 --- a/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec +++ b/packages/react-native-session-replay/DatadogSDKReactNativeSessionReplay.podspec @@ -23,7 +23,7 @@ Pod::Spec.new do |s| s.dependency "React-Core" # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogSessionReplay', '3.1.0' + s.dependency 'DatadogSessionReplay', '3.3.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-session-replay/android/build.gradle b/packages/react-native-session-replay/android/build.gradle index a0d77f2ff..1b16713db 100644 --- a/packages/react-native-session-replay/android/build.gradle +++ b/packages/react-native-session-replay/android/build.gradle @@ -216,8 +216,8 @@ dependencies { api "com.facebook.react:react-android:$reactNativeVersion" } implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.datadoghq:dd-sdk-android-session-replay:3.1.0" - implementation "com.datadoghq:dd-sdk-android-internal:3.1.0" + implementation "com.datadoghq:dd-sdk-android-session-replay:3.3.0" + implementation "com.datadoghq:dd-sdk-android-internal:3.3.0" implementation project(path: ':datadog_mobile-react-native') testImplementation "org.junit.platform:junit-platform-launcher:1.6.2" diff --git a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec index 080a853d8..000b35477 100644 --- a/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec +++ b/packages/react-native-webview/DatadogSDKReactNativeWebView.podspec @@ -23,8 +23,8 @@ Pod::Spec.new do |s| end # /!\ Remember to keep the version in sync with DatadogSDKReactNative.podspec - s.dependency 'DatadogWebViewTracking', '3.1.0' - s.dependency 'DatadogInternal', '3.1.0' + s.dependency 'DatadogWebViewTracking', '3.3.0' + s.dependency 'DatadogInternal', '3.3.0' s.dependency 'DatadogSDKReactNative' s.test_spec 'Tests' do |test_spec| diff --git a/packages/react-native-webview/android/build.gradle b/packages/react-native-webview/android/build.gradle index e1fb081f0..50fa7fce3 100644 --- a/packages/react-native-webview/android/build.gradle +++ b/packages/react-native-webview/android/build.gradle @@ -196,7 +196,7 @@ dependencies { implementation "com.facebook.react:react-android:$reactNativeVersion" } - implementation "com.datadoghq:dd-sdk-android-webview:3.1.0" + implementation "com.datadoghq:dd-sdk-android-webview:3.3.0" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation project(path: ':datadog_mobile-react-native') From 48690c3a5049f4e83be539456ba7e4ff9bc486d3 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 28 Nov 2025 11:49:57 +0100 Subject: [PATCH 054/168] Solve merge issues and fix failing native tests --- .../reactnative/DdSdkImplementation.kt | 34 +------- .../Sources/DdSdkNativeInitialization.swift | 6 -- packages/core/ios/Tests/DdSdkTests.swift | 81 ++++++------------- .../Sources/RCTDatadogWebViewTracking.swift | 4 +- 4 files changed, 31 insertions(+), 94 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 02123ee8f..8cb165b5f 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -351,6 +351,7 @@ class DdSdkImplementation( } } + /** * Normalizes frameTime values so when are turned into FPS metrics they are normalized on a range of zero to 60fps. * @param frameTimeSeconds: the frame time to normalize. In seconds. @@ -368,43 +369,16 @@ class DdSdkImplementation( val frameTimeMs = frameTimeSeconds * 1000.0 val frameBudgetHz = fpsBudget ?: DEFAULT_REFRESH_HZ val maxDeviceDisplayHz = deviceDisplayFps ?: getMaxDisplayRefreshRate(context) - ?: 60.0 - - val maxDeviceFrameTimeMs = 1000.0 / maxDeviceDisplayHz - val budgetFrameTimeMs = 1000.0 / frameBudgetHz - - if (listOf( - maxDeviceDisplayHz, frameTimeMs, frameBudgetHz, budgetFrameTimeMs, maxDeviceFrameTimeMs - ).any { !it.isFinite() || it <= 0.0 } - ) return 1.0 / DEFAULT_REFRESH_HZ - - - var normalizedFrameTimeMs = frameTimeMs / (maxDeviceFrameTimeMs / budgetFrameTimeMs) - - normalizedFrameTimeMs = max(normalizedFrameTimeMs, maxDeviceFrameTimeMs) - - return normalizedFrameTimeMs / 1000.0 // in seconds - } - - @Suppress("CyclomaticComplexMethod") - private fun getMaxDisplayRefreshRate(context: Context?): Double { - val dm = context?.getSystemService(Context.DISPLAY_SERVICE) as? DisplayManager ?: return 60.0 - val display: Display = dm.getDisplay(Display.DEFAULT_DISPLAY) ?: return DEFAULT_REFRESH_HZ - - return display.supportedModes.maxOf { it.refreshRate.toDouble() } - } - - // endregion + ?: 60.0 val maxDeviceFrameTimeMs = 1000.0 / maxDeviceDisplayHz val budgetFrameTimeMs = 1000.0 / frameBudgetHz if (listOf( - maxDeviceDisplayHz, frameTimeMs, frameBudgetHz, budgetFrameTimeMs, maxDeviceFrameTimeMs - ).any { !it.isFinite() || it <= 0.0 } + maxDeviceDisplayHz, frameTimeMs, frameBudgetHz, budgetFrameTimeMs, maxDeviceFrameTimeMs + ).any { !it.isFinite() || it <= 0.0 } ) return 1.0 / DEFAULT_REFRESH_HZ - var normalizedFrameTimeMs = frameTimeMs / (maxDeviceFrameTimeMs / budgetFrameTimeMs) normalizedFrameTimeMs = max(normalizedFrameTimeMs, maxDeviceFrameTimeMs) diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index 969f72abf..fc11cd80d 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -93,16 +93,10 @@ public class DdSdkNativeInitialization: NSObject { if sdkConfiguration.nativeCrashReportEnabled ?? false { CrashReporting.enable() } -<<<<<<< HEAD -<<<<<<< HEAD #if os(iOS) DatadogSDKWrapper.shared.enableWebviewTracking() #endif -======= ->>>>>>> 0443e0ff (iOS: Always use SDK default core instance) -======= ->>>>>>> 93aa6125 (iOS: Always use SDK default core instance) } func buildSDKConfiguration(configuration: DdSdkConfiguration, defaultAppVersion: String = getDefaultAppVersion()) -> Datadog.Configuration { diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index cba49f84f..3f5c0e960 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -82,7 +82,7 @@ class DdSdkTests: XCTestCase { func testResolvesPromiseAfterInitializationIsDone() throws { let bridge = DispatchQueueMock() let mockJSRefreshRateMonitor = MockJSRefreshRateMonitor() - let mockListener = MockOnCoreInitializedListener() + let mockListener = MockOnSdkInitializedListener() DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: mockListener.listener) let expectation = self.expectation(description: "Listener is called when promise resolves") @@ -275,7 +275,9 @@ class DdSdkTests: XCTestCase { func testSDKInitializationWithOnInitializedCallback() { var isInitialized = false + var coreFromCallback: DatadogCoreProtocol? = nil DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: { + core in coreFromCallback = core isInitialized = Datadog.isInitialized() }) @@ -718,13 +720,14 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) } - - func testClearUserInfo() throws { + + func testAddingAttribute() { + let rumMonitorMock = MockRUMMonitor() let bridge = DdSdkImplementation( mainDispatchQueue: DispatchQueueMock(), jsDispatchQueue: DispatchQueueMock(), jsRefreshRateMonitor: JSRefreshRateMonitor(), - RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorProvider: { rumMonitorMock }, RUMMonitorInternalProvider: { nil } ) bridge.initialize( @@ -733,57 +736,19 @@ class DdSdkTests: XCTestCase { reject: mockReject ) - bridge.setUserInfo( - userInfo: NSDictionary( - dictionary: [ - "id": "id_123", - "name": "John Doe", - "email": "john@doe.com", - "extraInfo": [ - "extra-info-1": 123, - "extra-info-2": "abc", - "extra-info-3": true, - "extra-info-4": [ - "nested-extra-info-1": 456 - ], - ], - ] - ), - resolve: mockResolve, - reject: mockReject - ) - - var ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - var userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, "id_123") - XCTAssertEqual(userInfo.name, "John Doe") - XCTAssertEqual(userInfo.email, "john@doe.com") - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) - - if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] - as? DatadogSDKReactNative.AnyEncodable, - let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] - { - XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) - } else { - XCTFail("extra-info-4 is not of expected type or value") - } - - bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) + bridge.addAttribute(key: "attribute-1", value: NSDictionary(dictionary: ["value": 123]), resolve: mockResolve, reject: mockReject) + bridge.addAttribute(key: "attribute-2", value: NSDictionary(dictionary: ["value": "abc"]), resolve: mockResolve, reject: mockReject) + bridge.addAttribute(key: "attribute-3", value: NSDictionary(dictionary: ["value": true]), resolve: mockResolve, reject: mockReject) + + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, "abc") + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-3"] as? Bool, true) - ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - userInfo = try XCTUnwrap(ddContext.userInfo) + XCTAssertEqual(GlobalState.globalAttributes["attribute-1"] as? Int64, 123) + XCTAssertEqual(GlobalState.globalAttributes["attribute-2"] as? String, "abc") + XCTAssertEqual(GlobalState.globalAttributes["attribute-3"] as? Bool, true) - XCTAssertEqual(userInfo.id, nil) - XCTAssertEqual(userInfo.name, nil) - XCTAssertEqual(userInfo.email, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) + GlobalState.globalAttributes.removeAll() } func testRemovingAttribute() { @@ -1542,7 +1507,7 @@ class DdSdkTests: XCTestCase { func testCallsOnSdkInitializedListeners() throws { let bridge = DispatchQueueMock() let mockJSRefreshRateMonitor = MockJSRefreshRateMonitor() - let mockListener = MockOnCoreInitializedListener() + let mockListener = MockOnSdkInitializedListener() DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: mockListener.listener) @@ -1811,9 +1776,13 @@ extension DdSdkImplementation { } } -class MockOnCoreInitializedListener { +class MockOnSdkInitializedListener { var called = false - func listener() { + var receivedCore: DatadogCoreProtocol? + + lazy var listener: OnSdkInitializedListener = { core in self.called = true + self.receivedCore = core } } + diff --git a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift index 45f11e452..6d3bc3f8d 100644 --- a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift +++ b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift @@ -17,14 +17,14 @@ import DatadogInternal public override init() { super.init() - self.onSdkInitializedListener = { [weak self] in + self.onSdkInitializedListener = { [weak self] (core: DatadogCoreProtocol) in guard let strongSelf = self, let webView = strongSelf.webView else { return } strongSelf.enableWebViewTracking( webView: webView, allowedHosts: strongSelf.allowedHosts, - core: CoreRegistry.default + core: core ) } } From bf55f23a8de7967073fec545323ea9dd5c63104f Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 9 Sep 2025 12:08:35 +0200 Subject: [PATCH 055/168] iOS: Always use SDK default core instance --- .../core/ios/Sources/DatadogSDKWrapper.swift | 40 +++++++++---------- .../Sources/DdSdkNativeInitialization.swift | 2 +- .../Sources/RCTDatadogWebViewTracking.swift | 2 +- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/packages/core/ios/Sources/DatadogSDKWrapper.swift b/packages/core/ios/Sources/DatadogSDKWrapper.swift index 894f0a09f..3c23b3b0b 100644 --- a/packages/core/ios/Sources/DatadogSDKWrapper.swift +++ b/packages/core/ios/Sources/DatadogSDKWrapper.swift @@ -4,18 +4,16 @@ * Copyright 2016-Present Datadog, Inc. */ - import DatadogCore -import DatadogRUM -import DatadogLogs -import DatadogTrace import DatadogCrashReporting import DatadogInternal +import DatadogLogs +import DatadogRUM +import DatadogTrace import Foundation - #if os(iOS) -import DatadogWebViewTracking + import DatadogWebViewTracking #endif public typealias OnSdkInitializedListener = (DatadogCoreProtocol) -> Void @@ -30,9 +28,9 @@ public class DatadogSDKWrapper { internal private(set) var loggerConfiguration = DatadogLogs.Logger.Configuration() - private init() { } + private init() {} - public func addOnSdkInitializedListener(listener:@escaping OnSdkInitializedListener) { + public func addOnSdkInitializedListener(listener: @escaping OnSdkInitializedListener) { onSdkInitializedListeners.append(listener) } @@ -41,7 +39,7 @@ public class DatadogSDKWrapper { coreConfiguration: Datadog.Configuration, loggerConfiguration: DatadogLogs.Logger.Configuration, trackingConsent: TrackingConsent - ) -> Void { + ) { let core = Datadog.initialize(with: coreConfiguration, trackingConsent: trackingConsent) for listener in onSdkInitializedListeners { @@ -51,18 +49,18 @@ public class DatadogSDKWrapper { self.loggerConfiguration = loggerConfiguration } -#if os(iOS) - // Webview - private var webviewMessageEmitter: InternalExtension.AbstractMessageEmitter? + #if os(iOS) + // Webview + private var webviewMessageEmitter: + InternalExtension.AbstractMessageEmitter? - internal func enableWebviewTracking() { - webviewMessageEmitter = WebViewTracking._internal.messageEmitter(in: CoreRegistry.default) - } + internal func enableWebviewTracking() { + webviewMessageEmitter = WebViewTracking._internal.messageEmitter( + in: CoreRegistry.default) + } - internal func sendWebviewMessage(body: NSString) throws { - try self.webviewMessageEmitter?.send(body: body) - } -#endif + internal func sendWebviewMessage(body: NSString) throws { + try self.webviewMessageEmitter?.send(body: body) + } + #endif } - - diff --git a/packages/core/ios/Sources/DdSdkNativeInitialization.swift b/packages/core/ios/Sources/DdSdkNativeInitialization.swift index fc11cd80d..6e773282d 100644 --- a/packages/core/ios/Sources/DdSdkNativeInitialization.swift +++ b/packages/core/ios/Sources/DdSdkNativeInitialization.swift @@ -93,7 +93,7 @@ public class DdSdkNativeInitialization: NSObject { if sdkConfiguration.nativeCrashReportEnabled ?? false { CrashReporting.enable() } - + #if os(iOS) DatadogSDKWrapper.shared.enableWebviewTracking() #endif diff --git a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift index 6d3bc3f8d..b2189037b 100644 --- a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift +++ b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift @@ -24,7 +24,7 @@ import DatadogInternal strongSelf.enableWebViewTracking( webView: webView, allowedHosts: strongSelf.allowedHosts, - core: core + core: CoreRegistry.default ) } } From 50dfd73a80b85631cea0d2ba77a303f1842b3817 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 12 Sep 2025 14:43:25 +0200 Subject: [PATCH 056/168] Remove setUser --- packages/core/ios/Tests/MockRUMMonitor.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index 4a1d0cd92..c692f6e7c 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,6 +10,22 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { + func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { + // not implemented + } + + func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { + // not implemented + } + + func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { + // not implemented + } + + func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { + // not implemented + } + func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } From b134e5363b0c9e8cb087d1f25caf7fe9e42cec55 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 5 Sep 2025 17:47:29 +0200 Subject: [PATCH 057/168] Use native sdk's core instance instead of the one inside RN SDK wrapper --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 ++ .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +----- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 +------ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index 4388ad5f6..ee55d08fe 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,7 +65,9 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) + Logs.enable(logsConfiguration, Datadog.getInstance()) + Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index ef91ca549..27e4c155b 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -22,11 +22,7 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation( - reactContext, - datadog = datadogWrapper, - ddTelemetry - ) + private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 327d8ffc0..f034b292c 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -164,12 +164,7 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation( - mockReactContext, - mockDatadog, - mockDdTelemetry, - TestUiThreadExecutor() - ) + testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From 78ed8958f4752901674b91f0472a4d8295256a43 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Mon, 22 Sep 2025 16:20:27 +0200 Subject: [PATCH 058/168] Fixed internal testing tools and unit tests --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 -- .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +++++- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 ++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index ee55d08fe..4388ad5f6 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,9 +65,7 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) - Logs.enable(logsConfiguration, Datadog.getInstance()) - Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 27e4c155b..ef91ca549 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -22,7 +22,11 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) + private val implementation = DdSdkImplementation( + reactContext, + datadog = datadogWrapper, + ddTelemetry + ) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index f034b292c..327d8ffc0 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -164,7 +164,12 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) + testedBridgeSdk = DdSdkImplementation( + mockReactContext, + mockDatadog, + mockDdTelemetry, + TestUiThreadExecutor() + ) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From 9bc7d648cd9e9b1c76dfb0d29f031eac500a5777 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 19 Sep 2025 17:37:31 +0200 Subject: [PATCH 059/168] Expose clearUserInfo API --- .../ios/Sources/DdSdkImplementation.swift | 2 +- packages/core/ios/Tests/DdSdkTests.swift | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index db8c4e938..b894a7998 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -143,7 +143,7 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - + @objc public func addUserExtraInfo( extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 3f5c0e960..b2c6e8134 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -653,6 +653,73 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } + + func testClearUserInfo() throws { + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ) + bridge.initialize( + configuration: .mockAny(), + resolve: mockResolve, + reject: mockReject + ) + + bridge.setUserInfo( + userInfo: NSDictionary( + dictionary: [ + "id": "id_123", + "name": "John Doe", + "email": "john@doe.com", + "extraInfo": [ + "extra-info-1": 123, + "extra-info-2": "abc", + "extra-info-3": true, + "extra-info-4": [ + "nested-extra-info-1": 456 + ], + ], + ] + ), + resolve: mockResolve, + reject: mockReject + ) + + var ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + var userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, "id_123") + XCTAssertEqual(userInfo.name, "John Doe") + XCTAssertEqual(userInfo.email, "john@doe.com") + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) + + if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] + as? DatadogSDKReactNative.AnyEncodable, + let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] + { + XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) + } else { + XCTFail("extra-info-4 is not of expected type or value") + } + + bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) + + ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, nil) + XCTAssertEqual(userInfo.name, nil) + XCTAssertEqual(userInfo.email, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) + } func testClearUserInfo() throws { let bridge = DdSdkImplementation( From a4d0217c184d3bd5bf5c076f0127d7dbfa2f2ee0 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 29 Sep 2025 12:20:44 +0200 Subject: [PATCH 060/168] Update attribute API --- .../datadog/reactnative/DatadogSDKWrapper.kt | 8 +++ .../com/datadog/reactnative/DatadogWrapper.kt | 15 +++++ packages/core/ios/Tests/DdSdkTests.swift | 67 ------------------- 3 files changed, 23 insertions(+), 67 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index 56a15373c..46efaf37c 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -89,6 +89,14 @@ internal class DatadogSDKWrapper : DatadogWrapper { override fun clearUserInfo() { Datadog.clearUserInfo() } + + override fun addRumGlobalAttribute(key: String, value: Any?) { + this.getRumMonitor().addAttribute(key, value) + } + + override fun removeRumGlobalAttribute(key: String) { + this.getRumMonitor().removeAttribute(key) + } override fun setAccountInfo( id: String, diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index c72f2faef..ab48601b2 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -91,6 +91,21 @@ interface DatadogWrapper { */ fun clearUserInfo() + + /** Adds a global attribute. + * + * @param key: Key that identifies the attribute. + * @param value: Value linked to the attribute. + */ + fun addRumGlobalAttribute(key: String, value: Any?) + + /** + * Removes a global attribute. + * + * @param key: Key that identifies the attribute. + */ + fun removeRumGlobalAttribute(key: String) + /** * Sets the account information. * diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index b2c6e8134..3f5c0e960 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -653,73 +653,6 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } - - func testClearUserInfo() throws { - let bridge = DdSdkImplementation( - mainDispatchQueue: DispatchQueueMock(), - jsDispatchQueue: DispatchQueueMock(), - jsRefreshRateMonitor: JSRefreshRateMonitor(), - RUMMonitorProvider: { MockRUMMonitor() }, - RUMMonitorInternalProvider: { nil } - ) - bridge.initialize( - configuration: .mockAny(), - resolve: mockResolve, - reject: mockReject - ) - - bridge.setUserInfo( - userInfo: NSDictionary( - dictionary: [ - "id": "id_123", - "name": "John Doe", - "email": "john@doe.com", - "extraInfo": [ - "extra-info-1": 123, - "extra-info-2": "abc", - "extra-info-3": true, - "extra-info-4": [ - "nested-extra-info-1": 456 - ], - ], - ] - ), - resolve: mockResolve, - reject: mockReject - ) - - var ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - var userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, "id_123") - XCTAssertEqual(userInfo.name, "John Doe") - XCTAssertEqual(userInfo.email, "john@doe.com") - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) - - if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] - as? DatadogSDKReactNative.AnyEncodable, - let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] - { - XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) - } else { - XCTFail("extra-info-4 is not of expected type or value") - } - - bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) - - ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, nil) - XCTAssertEqual(userInfo.name, nil) - XCTAssertEqual(userInfo.email, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) - } func testClearUserInfo() throws { let bridge = DdSdkImplementation( From 53324dbafa9cd26ecac6dff59f2f7721e1f6883c Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 16 Oct 2025 11:01:21 +0200 Subject: [PATCH 061/168] JS refresh rate normalization --- .../main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt index 8cb165b5f..a926e4ac2 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkImplementation.kt @@ -351,7 +351,6 @@ class DdSdkImplementation( } } - /** * Normalizes frameTime values so when are turned into FPS metrics they are normalized on a range of zero to 60fps. * @param frameTimeSeconds: the frame time to normalize. In seconds. From 1e3bc3de9b9b49a889aa167bdf7586ec635c9b5a Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 10 Oct 2025 14:52:21 +0200 Subject: [PATCH 062/168] Expose view Attributes API --- .../core/ios/Sources/DdSdkImplementation.swift | 2 +- packages/core/ios/Tests/MockRUMMonitor.swift | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index b894a7998..db8c4e938 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -143,7 +143,7 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - + @objc public func addUserExtraInfo( extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index c692f6e7c..4a1d0cd92 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,22 +10,6 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { - func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { - // not implemented - } - - func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { - // not implemented - } - - func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { - // not implemented - } - - func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { - // not implemented - } - func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } From 40d6932495ef7bf4a02d7b72825e78332673cefc Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 8 Sep 2025 15:27:17 +0200 Subject: [PATCH 063/168] Bump native SDK dependencies to 3.0.0 --- .../Sources/RCTDatadogWebViewTracking.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift index b2189037b..af1791022 100644 --- a/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift +++ b/packages/react-native-webview/ios/Sources/RCTDatadogWebViewTracking.swift @@ -4,17 +4,17 @@ * Copyright 2016-Present Datadog, Inc. */ -import WebKit -import DatadogWebViewTracking -import DatadogSDKReactNative import DatadogCore import DatadogInternal +import DatadogSDKReactNative +import DatadogWebViewTracking +import WebKit @objc public class RCTDatadogWebViewTracking: NSObject { var webView: RCTDatadogWebView? = nil var allowedHosts: Set = Set() var onSdkInitializedListener: OnSdkInitializedListener? - + public override init() { super.init() self.onSdkInitializedListener = { [weak self] (core: DatadogCoreProtocol) in @@ -28,10 +28,10 @@ import DatadogInternal ) } } - + /** Enables tracking on the given WebView. - + - Parameter webView: The WebView to enable tracking on. - Parameter allowedHosts: The allowed hosts. - Note: If the SDK core is not available immediately, this method will register a listener and @@ -44,14 +44,14 @@ import DatadogInternal guard !webView.isTrackingEnabled else { return } if CoreRegistry.isRegistered(instanceName: CoreRegistry.defaultInstanceName) { - enableWebViewTracking(webView: webView, allowedHosts: allowedHosts, core: CoreRegistry.default) + enableWebViewTracking( + webView: webView, allowedHosts: allowedHosts, core: CoreRegistry.default) } else if let onSdkInitializedListener = self.onSdkInitializedListener { DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: onSdkInitializedListener) } else { // TODO: Report initialization problem } } - private func enableWebViewTracking( webView: RCTDatadogWebView, @@ -63,7 +63,7 @@ import DatadogInternal } DispatchQueue.main.async { WebViewTracking.enable(webView: wkWebView, hosts: allowedHosts, in: core) - self.webView?.isTrackingEnabled = true; + self.webView?.isTrackingEnabled = true } } } From 32e38e13762621cc9556cc81e6ca126c04efecc0 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 12 Sep 2025 14:43:25 +0200 Subject: [PATCH 064/168] Remove setUser --- packages/core/ios/Tests/MockRUMMonitor.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index 4a1d0cd92..c692f6e7c 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,6 +10,22 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { + func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { + // not implemented + } + + func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { + // not implemented + } + + func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { + // not implemented + } + + func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { + // not implemented + } + func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } From 4373160762918b593983cda073956e041c7d4d79 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 5 Sep 2025 17:47:29 +0200 Subject: [PATCH 065/168] Use native sdk's core instance instead of the one inside RN SDK wrapper --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 ++ .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +----- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 +------ 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index 4388ad5f6..ee55d08fe 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,7 +65,9 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) + Logs.enable(logsConfiguration, Datadog.getInstance()) + Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index ef91ca549..27e4c155b 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -22,11 +22,7 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation( - reactContext, - datadog = datadogWrapper, - ddTelemetry - ) + private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index 327d8ffc0..f034b292c 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -164,12 +164,7 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation( - mockReactContext, - mockDatadog, - mockDdTelemetry, - TestUiThreadExecutor() - ) + testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From 1703b3391bb72690b399164fca5b96f81302d35a Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Mon, 22 Sep 2025 16:20:27 +0200 Subject: [PATCH 066/168] Fixed internal testing tools and unit tests --- .../com/datadog/reactnative/DdSdkNativeInitialization.kt | 2 -- .../src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt | 6 +++++- .../src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt | 7 ++++++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt index ee55d08fe..4388ad5f6 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DdSdkNativeInitialization.kt @@ -65,9 +65,7 @@ class DdSdkNativeInitialization internal constructor( datadog.initialize(appContext, sdkConfiguration, trackingConsent) Rum.enable(rumConfiguration, Datadog.getInstance()) - Logs.enable(logsConfiguration, Datadog.getInstance()) - Trace.enable(traceConfiguration, Datadog.getInstance()) } diff --git a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt index 27e4c155b..ef91ca549 100644 --- a/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt +++ b/packages/core/android/src/oldarch/kotlin/com/datadog/reactnative/DdSdk.kt @@ -22,7 +22,11 @@ class DdSdk( ddTelemetry: DdTelemetry = DdTelemetry() ) : ReactContextBaseJavaModule(reactContext) { - private val implementation = DdSdkImplementation(reactContext, datadog = datadogWrapper, ddTelemetry) + private val implementation = DdSdkImplementation( + reactContext, + datadog = datadogWrapper, + ddTelemetry + ) private var lifecycleEventListener: LifecycleEventListener? = null override fun getName(): String = DdSdkImplementation.NAME diff --git a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt index f034b292c..327d8ffc0 100644 --- a/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt +++ b/packages/core/android/src/test/kotlin/com/datadog/reactnative/DdSdkTest.kt @@ -164,7 +164,12 @@ internal class DdSdkTest { answer.getArgument(0).run() true } - testedBridgeSdk = DdSdkImplementation(mockReactContext, mockDatadog, mockDdTelemetry, TestUiThreadExecutor()) + testedBridgeSdk = DdSdkImplementation( + mockReactContext, + mockDatadog, + mockDdTelemetry, + TestUiThreadExecutor() + ) DatadogSDKWrapperStorage.onInitializedListeners.clear() } From ff07572fb91af34b8c15296739d3df3d9004d1ff Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 19 Sep 2025 17:37:31 +0200 Subject: [PATCH 067/168] Expose clearUserInfo API --- .../ios/Sources/DdSdkImplementation.swift | 2 +- packages/core/ios/Tests/DdSdkTests.swift | 67 +++++++++++++++++++ 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index db8c4e938..b894a7998 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -143,7 +143,7 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - + @objc public func addUserExtraInfo( extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index 3f5c0e960..b2c6e8134 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -653,6 +653,73 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } + + func testClearUserInfo() throws { + let bridge = DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: JSRefreshRateMonitor(), + RUMMonitorProvider: { MockRUMMonitor() }, + RUMMonitorInternalProvider: { nil } + ) + bridge.initialize( + configuration: .mockAny(), + resolve: mockResolve, + reject: mockReject + ) + + bridge.setUserInfo( + userInfo: NSDictionary( + dictionary: [ + "id": "id_123", + "name": "John Doe", + "email": "john@doe.com", + "extraInfo": [ + "extra-info-1": 123, + "extra-info-2": "abc", + "extra-info-3": true, + "extra-info-4": [ + "nested-extra-info-1": 456 + ], + ], + ] + ), + resolve: mockResolve, + reject: mockReject + ) + + var ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + var userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, "id_123") + XCTAssertEqual(userInfo.name, "John Doe") + XCTAssertEqual(userInfo.email, "john@doe.com") + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) + + if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] + as? DatadogSDKReactNative.AnyEncodable, + let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] + { + XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) + } else { + XCTFail("extra-info-4 is not of expected type or value") + } + + bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) + + ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() + userInfo = try XCTUnwrap(ddContext.userInfo) + + XCTAssertEqual(userInfo.id, nil) + XCTAssertEqual(userInfo.name, nil) + XCTAssertEqual(userInfo.email, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) + XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) + } func testClearUserInfo() throws { let bridge = DdSdkImplementation( From 9e0d7e9897a0d2037a52544ca993039d0b4f2eda Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Mon, 29 Sep 2025 12:20:44 +0200 Subject: [PATCH 068/168] Update attribute API --- packages/core/ios/Tests/DdSdkTests.swift | 185 +++++++++-------------- 1 file changed, 72 insertions(+), 113 deletions(-) diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index b2c6e8134..d1f51f010 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -277,7 +277,8 @@ class DdSdkTests: XCTestCase { var isInitialized = false var coreFromCallback: DatadogCoreProtocol? = nil DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: { - core in coreFromCallback = core + core in + coreFromCallback = core isInitialized = Datadog.isInitialized() }) @@ -653,73 +654,6 @@ class DdSdkTests: XCTestCase { XCTFail("extra-info-4 is not of expected type or value") } } - - func testClearUserInfo() throws { - let bridge = DdSdkImplementation( - mainDispatchQueue: DispatchQueueMock(), - jsDispatchQueue: DispatchQueueMock(), - jsRefreshRateMonitor: JSRefreshRateMonitor(), - RUMMonitorProvider: { MockRUMMonitor() }, - RUMMonitorInternalProvider: { nil } - ) - bridge.initialize( - configuration: .mockAny(), - resolve: mockResolve, - reject: mockReject - ) - - bridge.setUserInfo( - userInfo: NSDictionary( - dictionary: [ - "id": "id_123", - "name": "John Doe", - "email": "john@doe.com", - "extraInfo": [ - "extra-info-1": 123, - "extra-info-2": "abc", - "extra-info-3": true, - "extra-info-4": [ - "nested-extra-info-1": 456 - ], - ], - ] - ), - resolve: mockResolve, - reject: mockReject - ) - - var ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - var userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, "id_123") - XCTAssertEqual(userInfo.name, "John Doe") - XCTAssertEqual(userInfo.email, "john@doe.com") - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, 123) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, "abc") - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true) - - if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"] - as? DatadogSDKReactNative.AnyEncodable, - let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int] - { - XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456]) - } else { - XCTFail("extra-info-4 is not of expected type or value") - } - - bridge.clearUserInfo(resolve: mockResolve, reject: mockReject) - - ddContext = try XCTUnwrap(CoreRegistry.default as? DatadogCore).contextProvider.read() - userInfo = try XCTUnwrap(ddContext.userInfo) - - XCTAssertEqual(userInfo.id, nil) - XCTAssertEqual(userInfo.name, nil) - XCTAssertEqual(userInfo.email, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-1"] as? Int64, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-2"] as? String, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) - XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) - } func testClearUserInfo() throws { let bridge = DdSdkImplementation( @@ -787,7 +721,7 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, nil) XCTAssertEqual(userInfo.extraInfo["extra-info-4"] as? [String: Int], nil) } - + func testAddingAttribute() { let rumMonitorMock = MockRUMMonitor() let bridge = DdSdkImplementation( @@ -803,10 +737,16 @@ class DdSdkTests: XCTestCase { reject: mockReject ) - bridge.addAttribute(key: "attribute-1", value: NSDictionary(dictionary: ["value": 123]), resolve: mockResolve, reject: mockReject) - bridge.addAttribute(key: "attribute-2", value: NSDictionary(dictionary: ["value": "abc"]), resolve: mockResolve, reject: mockReject) - bridge.addAttribute(key: "attribute-3", value: NSDictionary(dictionary: ["value": true]), resolve: mockResolve, reject: mockReject) - + bridge.addAttribute( + key: "attribute-1", value: NSDictionary(dictionary: ["value": 123]), + resolve: mockResolve, reject: mockReject) + bridge.addAttribute( + key: "attribute-2", value: NSDictionary(dictionary: ["value": "abc"]), + resolve: mockResolve, reject: mockReject) + bridge.addAttribute( + key: "attribute-3", value: NSDictionary(dictionary: ["value": true]), + resolve: mockResolve, reject: mockReject) + XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-1"] as? Int64, 123) XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-2"] as? String, "abc") XCTAssertEqual(rumMonitorMock.addedAttributes["attribute-3"] as? Bool, true) @@ -1280,7 +1220,7 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(rumMonitorMock.receivedLongTasks.first?.value, 0.25) XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.25) } - + func testFrameTimeNormalizationFromCallback() { let mockRefreshRateMonitor = MockJSRefreshRateMonitor() let rumMonitorMock = MockRUMMonitor() @@ -1299,93 +1239,113 @@ class DdSdkTests: XCTestCase { resolve: mockResolve, reject: mockReject ) - + XCTAssertTrue(mockRefreshRateMonitor.isStarted) - + // 10 fps mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.1) sharedQueue.sync {} XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.1) - + // 30 fps mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.03) sharedQueue.sync {} XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.03) - + // 45 fps mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.02) sharedQueue.sync {} XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.02) - + // 60 fps mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.016) sharedQueue.sync {} - XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) - + XCTAssertEqual( + rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, + accuracy: 0.001) + // 90 fps mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.011) sharedQueue.sync {} - XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) - + XCTAssertEqual( + rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, + accuracy: 0.001) + // 120 fps mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.008) sharedQueue.sync {} - XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) + XCTAssertEqual( + rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, + accuracy: 0.001) } - + func testFrameTimeNormalizationUtilityFunction() { // 10 fps, 60fps capable device, 60 fps budget -> Normalized to 10fps - var frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.1, fpsBudget: 60.0, deviceDisplayFps: 60.0) + var frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.1, fpsBudget: 60.0, deviceDisplayFps: 60.0) XCTAssertEqual(frameTimeSeconds, 0.1, accuracy: 0.01) - + // 30 fps, 60fps capable device, 60 fps budget -> Normalized to 30fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.03, fpsBudget: 60.0, deviceDisplayFps: 60.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.03, fpsBudget: 60.0, deviceDisplayFps: 60.0) XCTAssertEqual(frameTimeSeconds, 0.03, accuracy: 0.01) - + // 60 fps, 60fps capable device, 60 fps budget-> Normalized to 60fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 60.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.016, fpsBudget: 60.0, deviceDisplayFps: 60.0) XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.01) - + // 60 fps, 120fps capable device, 60 fps budget -> Normalized to 30fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 120.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.016, fpsBudget: 60.0, deviceDisplayFps: 120.0) XCTAssertEqual(frameTimeSeconds, 0.03, accuracy: 0.01) - + // 120 fps, 120fps capable device, 60 fps budget -> Normalized to 60fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0083, fpsBudget: 60.0, deviceDisplayFps: 120.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.0083, fpsBudget: 60.0, deviceDisplayFps: 120.0) XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) - + // 90 fps, 120fps capable device, 60 fps budget -> Normalized to 45fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0111, fpsBudget: 60.0, deviceDisplayFps: 120.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.0111, fpsBudget: 60.0, deviceDisplayFps: 120.0) XCTAssertEqual(frameTimeSeconds, 0.0222, accuracy: 0.001) - + // 100 fps, 120fps capable device, 60 fps budget -> Normalized to 50fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.01, fpsBudget: 60.0, deviceDisplayFps: 120.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.01, fpsBudget: 60.0, deviceDisplayFps: 120.0) XCTAssertEqual(frameTimeSeconds, 0.02, accuracy: 0.001) - + // 120 fps, 120fps capable device, 120 fps budget -> Normalized to 120fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0083, fpsBudget: 120.0, deviceDisplayFps: 120.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.0083, fpsBudget: 120.0, deviceDisplayFps: 120.0) XCTAssertEqual(frameTimeSeconds, 0.0083, accuracy: 0.001) - + // 80 fps, 160fps capable device, 60 fps budget -> Normalized to 30fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0125, fpsBudget: 60.0, deviceDisplayFps: 160.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.0125, fpsBudget: 60.0, deviceDisplayFps: 160.0) XCTAssertEqual(frameTimeSeconds, 0.033, accuracy: 0.001) - + // 160 fps, 160fps capable device, 60 fps budget -> Normalized to 60fps - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.00625, fpsBudget: 60.0, deviceDisplayFps: 160.0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.00625, fpsBudget: 60.0, deviceDisplayFps: 160.0) XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) - + // Edge cases - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0, fpsBudget: 0, deviceDisplayFps: 0) + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0, fpsBudget: 0, deviceDisplayFps: 0) XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) - - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 0, deviceDisplayFps: 0) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.016, fpsBudget: 0, deviceDisplayFps: 0) XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) - - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 0) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.016, fpsBudget: 60.0, deviceDisplayFps: 0) XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) - - frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 0, deviceDisplayFps: 60.0) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate( + 0.016, fpsBudget: 0, deviceDisplayFps: 60.0) XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) } @@ -1852,4 +1812,3 @@ class MockOnSdkInitializedListener { self.receivedCore = core } } - From 4167d1d54009ce374b30961b62e3d67449985c55 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 16 Oct 2025 11:01:21 +0200 Subject: [PATCH 069/168] JS refresh rate normalization --- packages/core/ios/Tests/DdSdkTests.swift | 108 +++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/packages/core/ios/Tests/DdSdkTests.swift b/packages/core/ios/Tests/DdSdkTests.swift index d1f51f010..f0b3b2551 100644 --- a/packages/core/ios/Tests/DdSdkTests.swift +++ b/packages/core/ios/Tests/DdSdkTests.swift @@ -1220,6 +1220,114 @@ class DdSdkTests: XCTestCase { XCTAssertEqual(rumMonitorMock.receivedLongTasks.first?.value, 0.25) XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.25) } + + func testFrameTimeNormalizationFromCallback() { + let mockRefreshRateMonitor = MockJSRefreshRateMonitor() + let rumMonitorMock = MockRUMMonitor() + + DdSdkImplementation( + mainDispatchQueue: DispatchQueueMock(), + jsDispatchQueue: DispatchQueueMock(), + jsRefreshRateMonitor: mockRefreshRateMonitor, + RUMMonitorProvider: { rumMonitorMock }, + RUMMonitorInternalProvider: { rumMonitorMock._internalMock } + ).initialize( + configuration: .mockAny( + longTaskThresholdMs: 200, + vitalsUpdateFrequency: "average" + ), + resolve: mockResolve, + reject: mockReject + ) + + XCTAssertTrue(mockRefreshRateMonitor.isStarted) + + // 10 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.1) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.1) + + // 30 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.03) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.03) + + // 45 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.02) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds], 0.02) + + // 60 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.016) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) + + // 90 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.011) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) + + // 120 fps + mockRefreshRateMonitor.executeFrameCallback(frameTime: 0.008) + sharedQueue.sync {} + XCTAssertEqual(rumMonitorMock.lastReceivedPerformanceMetrics[.jsFrameTimeSeconds]!, 0.016, accuracy: 0.001) + } + + func testFrameTimeNormalizationUtilityFunction() { + + // 10 fps, 60fps capable device, 60 fps budget -> Normalized to 10fps + var frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.1, fpsBudget: 60.0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.1, accuracy: 0.01) + + // 30 fps, 60fps capable device, 60 fps budget -> Normalized to 30fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.03, fpsBudget: 60.0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.03, accuracy: 0.01) + + // 60 fps, 60fps capable device, 60 fps budget-> Normalized to 60fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.01) + + // 60 fps, 120fps capable device, 60 fps budget -> Normalized to 30fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.03, accuracy: 0.01) + + // 120 fps, 120fps capable device, 60 fps budget -> Normalized to 60fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0083, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + // 90 fps, 120fps capable device, 60 fps budget -> Normalized to 45fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0111, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.0222, accuracy: 0.001) + + // 100 fps, 120fps capable device, 60 fps budget -> Normalized to 50fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.01, fpsBudget: 60.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.02, accuracy: 0.001) + + // 120 fps, 120fps capable device, 120 fps budget -> Normalized to 120fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0083, fpsBudget: 120.0, deviceDisplayFps: 120.0) + XCTAssertEqual(frameTimeSeconds, 0.0083, accuracy: 0.001) + + // 80 fps, 160fps capable device, 60 fps budget -> Normalized to 30fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.0125, fpsBudget: 60.0, deviceDisplayFps: 160.0) + XCTAssertEqual(frameTimeSeconds, 0.033, accuracy: 0.001) + + // 160 fps, 160fps capable device, 60 fps budget -> Normalized to 60fps + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.00625, fpsBudget: 60.0, deviceDisplayFps: 160.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + // Edge cases + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0, fpsBudget: 0, deviceDisplayFps: 0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 0, deviceDisplayFps: 0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 60.0, deviceDisplayFps: 0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + + frameTimeSeconds = DdSdkImplementation.normalizeFrameTimeForDeviceRefreshRate(0.016, fpsBudget: 0, deviceDisplayFps: 60.0) + XCTAssertEqual(frameTimeSeconds, 0.016, accuracy: 0.001) + } func testFrameTimeNormalizationFromCallback() { let mockRefreshRateMonitor = MockJSRefreshRateMonitor() From 45519c81c0a1baeb868e49d1c9c7372d0a973286 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 10 Oct 2025 14:52:21 +0200 Subject: [PATCH 070/168] Expose view Attributes API --- .../core/ios/Sources/DdSdkImplementation.swift | 2 +- packages/core/ios/Tests/MockRUMMonitor.swift | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/packages/core/ios/Sources/DdSdkImplementation.swift b/packages/core/ios/Sources/DdSdkImplementation.swift index b894a7998..db8c4e938 100644 --- a/packages/core/ios/Sources/DdSdkImplementation.swift +++ b/packages/core/ios/Sources/DdSdkImplementation.swift @@ -143,7 +143,7 @@ public class DdSdkImplementation: NSObject { resolve(nil) } - + @objc public func addUserExtraInfo( extraInfo: NSDictionary, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock diff --git a/packages/core/ios/Tests/MockRUMMonitor.swift b/packages/core/ios/Tests/MockRUMMonitor.swift index c692f6e7c..4a1d0cd92 100644 --- a/packages/core/ios/Tests/MockRUMMonitor.swift +++ b/packages/core/ios/Tests/MockRUMMonitor.swift @@ -10,22 +10,6 @@ @testable import DatadogSDKReactNative internal class MockRUMMonitor: RUMMonitorProtocol { - func addViewAttribute(forKey key: DatadogInternal.AttributeKey, value: any DatadogInternal.AttributeValue) { - // not implemented - } - - func addViewAttributes(_ attributes: [DatadogInternal.AttributeKey : any DatadogInternal.AttributeValue]) { - // not implemented - } - - func removeViewAttribute(forKey key: DatadogInternal.AttributeKey) { - // not implemented - } - - func removeViewAttributes(forKeys keys: [DatadogInternal.AttributeKey]) { - // not implemented - } - func currentSessionID(completion: @escaping (String?) -> Void) { // not implemented } From 59f0ee95433edd796929ad69740774ebd24c994e Mon Sep 17 00:00:00 2001 From: Carlos Nogueira Date: Mon, 3 Nov 2025 18:05:04 +0000 Subject: [PATCH 071/168] Expose `setAccountInfo` API to JS layer add account info to `applyEventMapper` --- .../datadog/reactnative/DatadogSDKWrapper.kt | 20 +++++++++++++- .../com/datadog/reactnative/DatadogWrapper.kt | 27 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt index 46efaf37c..ffbcbe62a 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogSDKWrapper.kt @@ -89,7 +89,25 @@ internal class DatadogSDKWrapper : DatadogWrapper { override fun clearUserInfo() { Datadog.clearUserInfo() } - + + override fun setAccountInfo( + id: String, + name: String?, + extraInfo: Map + ) { + Datadog.setAccountInfo(id, name, extraInfo) + } + + override fun addAccountExtraInfo( + extraInfo: Map + ) { + Datadog.addAccountExtraInfo(extraInfo) + } + + override fun clearAccountInfo() { + Datadog.clearAccountInfo() + } + override fun addRumGlobalAttribute(key: String, value: Any?) { this.getRumMonitor().addAttribute(key, value) } diff --git a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt index ab48601b2..26a17f5cf 100644 --- a/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt +++ b/packages/core/android/src/main/kotlin/com/datadog/reactnative/DatadogWrapper.kt @@ -91,6 +91,33 @@ interface DatadogWrapper { */ fun clearUserInfo() + /** + * Sets the account information. + * + * @param id a unique account identifier (relevant to your business domain) + * @param name (nullable) the account name + * @param extraInfo additional information. An extra information can be + * nested up to 8 levels deep. Keys using more than 8 levels will be sanitized by SDK. + */ + fun setAccountInfo( + id: String, + name: String?, + extraInfo: Map + ) + + /** + * Sets the account information. + * @param extraInfo: The additional information. (To set the id or name please use setAccountInfo). + */ + fun addAccountExtraInfo( + extraInfo: Map + ) + + /** + * Clears the account information. + */ + fun clearAccountInfo() + /** Adds a global attribute. * From e857e8c2fecc228fa205058f758a861f3e6d880a Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Wed, 3 Dec 2025 17:38:46 +0100 Subject: [PATCH 072/168] Adapt internal testing tools package to changes done for v3 --- .../ios/Sources/DatadogCoreProxy.swift | 2 +- .../Sources/DdInternalTestingImplementation.swift | 12 ++---------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/packages/internal-testing-tools/ios/Sources/DatadogCoreProxy.swift b/packages/internal-testing-tools/ios/Sources/DatadogCoreProxy.swift index 9f7c3db2c..0d7e1dec1 100644 --- a/packages/internal-testing-tools/ios/Sources/DatadogCoreProxy.swift +++ b/packages/internal-testing-tools/ios/Sources/DatadogCoreProxy.swift @@ -96,7 +96,7 @@ private final class FeatureScopeInterceptor: @unchecked Sendable { let actualWriter: Writer unowned var interception: FeatureScopeInterceptor? - func write(value: T, metadata: M) { + func write(value: T, metadata: M?, completion: @escaping DatadogInternal.CompletionHandler) where T : Encodable, M : Encodable { group.enter() defer { group.leave() } diff --git a/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift b/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift index 7b1c2862d..b460b86af 100644 --- a/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift +++ b/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift @@ -14,7 +14,7 @@ import DatadogInternal public class DdInternalTestingImplementation: NSObject { @objc public func clearData(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - let coreProxy = (DatadogSDKWrapper.shared.getCoreInstance() as! DatadogCoreProxy) + let coreProxy = CoreRegistry.default as! DatadogCoreProxy coreProxy.waitAndDeleteEvents(ofFeature: "rum") coreProxy.waitAndDeleteEvents(ofFeature: "logging") coreProxy.waitAndDeleteEvents(ofFeature: "tracing") @@ -26,7 +26,7 @@ public class DdInternalTestingImplementation: NSObject { @objc public func getAllEvents(feature: String, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { do { - let coreProxy = (DatadogSDKWrapper.shared.getCoreInstance() as! DatadogCoreProxy) + let coreProxy = CoreRegistry.default as! DatadogCoreProxy let events = coreProxy.waitAndReturnEventsData(ofFeature: feature) let data = try JSONSerialization.data(withJSONObject: events, options: .prettyPrinted) resolve(String(data: data, encoding: String.Encoding.utf8) ?? "") @@ -39,10 +39,6 @@ public class DdInternalTestingImplementation: NSObject { @objc public func enable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - DatadogSDKWrapper.shared.addOnCoreInitializedListener(listener: {core in - let proxiedCore = DatadogCoreProxy(core: core) - DatadogSDKWrapper.shared.setCoreInstance(core: proxiedCore) - }) resolve(nil) } } @@ -51,9 +47,5 @@ public class DdInternalTestingImplementation: NSObject { public class DdInternalTestingNativeInitialization: NSObject { @objc public func enableFromNative() -> Void { - DatadogSDKWrapper.shared.addOnCoreInitializedListener(listener: {core in - let proxiedCore = DatadogCoreProxy(core: core) - DatadogSDKWrapper.shared.setCoreInstance(core: proxiedCore) - }) } } From 305c7cc0b10d9e38b9b1d6abcaab20da1e7ed180 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 4 Dec 2025 12:18:45 +0100 Subject: [PATCH 073/168] Fixed ProxiedCore implementation --- .../ios/Sources/DdInternalTestingImplementation.swift | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift b/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift index b460b86af..41a213efd 100644 --- a/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift +++ b/packages/internal-testing-tools/ios/Sources/DdInternalTestingImplementation.swift @@ -39,6 +39,11 @@ public class DdInternalTestingImplementation: NSObject { @objc public func enable(resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { + DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: {core in + let proxiedCore = DatadogCoreProxy(core: core) + CoreRegistry.unregisterDefault() + CoreRegistry.register(default: proxiedCore) + }) resolve(nil) } } @@ -47,5 +52,10 @@ public class DdInternalTestingImplementation: NSObject { public class DdInternalTestingNativeInitialization: NSObject { @objc public func enableFromNative() -> Void { + DatadogSDKWrapper.shared.addOnSdkInitializedListener(listener: {core in + let proxiedCore = DatadogCoreProxy(core: core) + CoreRegistry.unregisterDefault() + CoreRegistry.register(default: proxiedCore) + }) } } From f7d9af07e9fde31bc0dcb364d1c269ba0b4683ba Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 4 Dec 2025 16:49:11 +0100 Subject: [PATCH 074/168] Remove unnecessary isInitialized check on DdLogsImplementation --- packages/core/ios/Sources/Attributes.swift | 7 ---- .../ios/Sources/DdLogsImplementation.swift | 39 +------------------ 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/packages/core/ios/Sources/Attributes.swift b/packages/core/ios/Sources/Attributes.swift index adf9638f5..25dd8df20 100644 --- a/packages/core/ios/Sources/Attributes.swift +++ b/packages/core/ios/Sources/Attributes.swift @@ -109,10 +109,3 @@ internal struct InternalConfigurationAttributes { /// Expects `Bool` value. static let dropAction = "_dd.action.drop_action" } - -/// Error messages that can be thrown to the JS SDK -internal struct Errors { - /// Error thrown when a log was sent before the SDK was initialized. - /// Not sending the log prevent the logger to be set to a Noop logger. - static let logSentBeforeSDKInit = "DD_INTERNAL_LOG_SENT_BEFORE_SDK_INIT" -} diff --git a/packages/core/ios/Sources/DdLogsImplementation.swift b/packages/core/ios/Sources/DdLogsImplementation.swift index fe3fde092..7c428af45 100644 --- a/packages/core/ios/Sources/DdLogsImplementation.swift +++ b/packages/core/ios/Sources/DdLogsImplementation.swift @@ -13,27 +13,20 @@ import DatadogCore public class DdLogsImplementation: NSObject { private lazy var logger: LoggerProtocol = loggerProvider() private let loggerProvider: () -> LoggerProtocol - private let isSDKInitialized: () -> Bool - internal init(_ loggerProvider: @escaping () -> LoggerProtocol, _ isSDKInitialized: @escaping () -> Bool) { + internal init(_ loggerProvider: @escaping () -> LoggerProtocol) { self.loggerProvider = loggerProvider - self.isSDKInitialized = isSDKInitialized } @objc public override convenience init() { self.init( - { DatadogLogs.Logger.create(with: DatadogSDKWrapper.shared.loggerConfiguration) }, - { Datadog.isInitialized() } + { DatadogLogs.Logger.create(with: DatadogSDKWrapper.shared.loggerConfiguration) } ) } @objc public func debug(message: String, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger.debug(message, error: nil, attributes: attributes) resolve(nil) @@ -41,10 +34,6 @@ public class DdLogsImplementation: NSObject { @objc public func info(message: String, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger.info(message, error: nil, attributes: attributes) resolve(nil) @@ -52,10 +41,6 @@ public class DdLogsImplementation: NSObject { @objc public func warn(message: String, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger.warn(message, error: nil, attributes: attributes) resolve(nil) @@ -63,10 +48,6 @@ public class DdLogsImplementation: NSObject { @objc public func error(message: String, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger.error(message, error: nil, attributes: attributes) resolve(nil) @@ -74,10 +55,6 @@ public class DdLogsImplementation: NSObject { @objc public func debugWithError(message: String, errorKind: String?, errorMessage: String?, stacktrace: String?, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger._internal.log(level: .debug, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) @@ -85,10 +62,6 @@ public class DdLogsImplementation: NSObject { @objc public func infoWithError(message: String, errorKind: String?, errorMessage: String?, stacktrace: String?, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger._internal.log(level: .info, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) @@ -96,10 +69,6 @@ public class DdLogsImplementation: NSObject { @objc public func warnWithError(message: String, errorKind: String?, errorMessage: String?, stacktrace: String?, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger._internal.log(level: .warn, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) @@ -107,10 +76,6 @@ public class DdLogsImplementation: NSObject { @objc public func errorWithError(message: String, errorKind: String?, errorMessage: String?, stacktrace: String?, context: NSDictionary, resolve:RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock) -> Void { - if (!self.isSDKInitialized()) { - reject(nil, Errors.logSentBeforeSDKInit, nil) - return - } let attributes = castAttributesToSwift(context).mergeWithGlobalAttributes() logger._internal.log(level: .error, message: message, errorKind: errorKind, errorMessage: errorMessage, stackTrace: stacktrace, attributes: attributes) resolve(nil) From bf94a8b166afcf4dd04a9d4c23faa0b5e690be2b Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Thu, 4 Dec 2025 17:42:17 +0100 Subject: [PATCH 075/168] Fix iOS tests --- packages/core/ios/Tests/DdLogsTests.swift | 33 ++--------------------- 1 file changed, 2 insertions(+), 31 deletions(-) diff --git a/packages/core/ios/Tests/DdLogsTests.swift b/packages/core/ios/Tests/DdLogsTests.swift index 60640e807..d3d31a95c 100644 --- a/packages/core/ios/Tests/DdLogsTests.swift +++ b/packages/core/ios/Tests/DdLogsTests.swift @@ -14,7 +14,7 @@ func mockReject(args: String?, arg: String?, err: Error?) {} internal class DdLogsTests: XCTestCase { private let mockNativeLogger = MockNativeLogger() - private lazy var logger = DdLogsImplementation({ self.mockNativeLogger }, { true }) + private lazy var logger = DdLogsImplementation({ self.mockNativeLogger }) private let testMessage_swift: String = "message" private let testMessage_objc: NSString = "message" @@ -80,7 +80,7 @@ internal class DdLogsTests: XCTestCase { let logger = DdLogsImplementation({ [unowned self] in expectation.fulfill() return self.mockNativeLogger - }, { true }) + }) // When (0..<10).forEach { _ in logger.debug(message: "foo", context: [:], resolve: mockResolve, reject: mockReject)} @@ -372,35 +372,6 @@ internal class DdLogsTests: XCTestCase { GlobalState.globalAttributes.keys ) } - - func testDoesNotInitializeLoggerBeforeSdkIsInitialized() throws { - var isInitialized = false - let newLogger = DdLogsImplementation({ self.mockNativeLogger }, { isInitialized }) - - newLogger.debug(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.info(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.warn(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.error(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.debugWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - newLogger.infoWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - newLogger.warnWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - newLogger.errorWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - - XCTAssertEqual(mockNativeLogger.receivedMethodCalls.count, 0) - - isInitialized = true - - newLogger.debug(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.info(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.warn(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.error(message: testMessage_objc as String, context: validTestAttributes_objc, resolve: mockResolve, reject: mockReject) - newLogger.debugWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - newLogger.infoWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - newLogger.warnWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - newLogger.errorWithError(message: testMessage_objc as String, errorKind: testErrorKind_objc as String, errorMessage: testErrorMessage_objc as String, stacktrace: testErrorStacktrace_objc as String, context: invalidTestAttributes, resolve: mockResolve, reject: mockReject) - - XCTAssertEqual(mockNativeLogger.receivedMethodCalls.count, 8) - } } private class MockNativeLogger: LoggerProtocol { From d2b179381256580044fc062ec054223d200fb9ba Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Fri, 10 Oct 2025 11:31:03 +0200 Subject: [PATCH 076/168] Attributes Safe Encoding --- packages/core/src/DdSdkReactNative.tsx | 33 +-- .../src/DdSdkReactNativeConfiguration.tsx | 17 ++ packages/core/src/logs/DdLogs.ts | 16 +- packages/core/src/nativeModulesTypes.ts | 4 +- packages/core/src/rum/DdRum.ts | 35 +-- .../instrumentation/DdRumErrorTracking.tsx | 25 +- .../DdRumUserInteractionTracking.tsx | 8 +- .../__tests__/attributesEncoding.test.ts | 266 ++++++++++++++++++ .../__tests__/defaultEncoders.test.ts | 198 +++++++++++++ .../AttributesEncoding/attributesEncoding.tsx | 41 +++ .../AttributesEncoding/defaultEncoders.tsx | 185 ++++++++++++ .../AttributesEncoding/errorUtils.tsx} | 72 +++-- .../src/sdk/AttributesEncoding/helpers.tsx | 134 +++++++++ .../core/src/sdk/AttributesEncoding/types.tsx | 33 +++ .../core/src/sdk/AttributesEncoding/utils.tsx | 31 ++ .../DatadogProvider/Buffer/BoundedBuffer.ts | 6 +- packages/core/src/sdk/DdSdk.ts | 12 - packages/core/src/sdk/DdSdk.tsx | 15 + packages/core/src/sdk/DdSdkInternal.tsx | 105 +++++++ .../core/src/sdk/EventMappers/EventMapper.ts | 4 +- packages/core/src/trace/DdTrace.ts | 6 +- packages/core/src/types.tsx | 5 +- .../react-native-apollo-client/src/helpers.ts | 4 +- 23 files changed, 1148 insertions(+), 107 deletions(-) create mode 100644 packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts create mode 100644 packages/core/src/sdk/AttributesEncoding/__tests__/defaultEncoders.test.ts create mode 100644 packages/core/src/sdk/AttributesEncoding/attributesEncoding.tsx create mode 100644 packages/core/src/sdk/AttributesEncoding/defaultEncoders.tsx rename packages/core/src/{utils/errorUtils.ts => sdk/AttributesEncoding/errorUtils.tsx} (55%) create mode 100644 packages/core/src/sdk/AttributesEncoding/helpers.tsx create mode 100644 packages/core/src/sdk/AttributesEncoding/types.tsx create mode 100644 packages/core/src/sdk/AttributesEncoding/utils.tsx delete mode 100644 packages/core/src/sdk/DdSdk.ts create mode 100644 packages/core/src/sdk/DdSdk.tsx create mode 100644 packages/core/src/sdk/DdSdkInternal.tsx diff --git a/packages/core/src/DdSdkReactNative.tsx b/packages/core/src/DdSdkReactNative.tsx index e07ba4e34..243b11491 100644 --- a/packages/core/src/DdSdkReactNative.tsx +++ b/packages/core/src/DdSdkReactNative.tsx @@ -35,7 +35,7 @@ import { AttributesSingleton } from './sdk/AttributesSingleton/AttributesSinglet import type { Attributes } from './sdk/AttributesSingleton/types'; import { registerNativeBridge } from './sdk/DatadogInternalBridge/DdSdkInternalNativeBridge'; import { BufferSingleton } from './sdk/DatadogProvider/Buffer/BufferSingleton'; -import { DdSdk } from './sdk/DdSdk'; +import { NativeDdSdk } from './sdk/DdSdkInternal'; import { FileBasedConfiguration } from './sdk/FileBasedConfiguration/FileBasedConfiguration'; import { GlobalState } from './sdk/GlobalState/GlobalState'; import { UserInfoSingleton } from './sdk/UserInfoSingleton/UserInfoSingleton'; @@ -84,7 +84,7 @@ export class DdSdkReactNative { SdkVerbosity.WARN ); if (!__DEV__) { - DdSdk.telemetryDebug( + NativeDdSdk.telemetryDebug( 'RN SDK was already initialized in javascript' ); } @@ -95,7 +95,7 @@ export class DdSdkReactNative { registerNativeBridge(); - await DdSdk.initialize( + await NativeDdSdk.initialize( DdSdkReactNative.buildConfiguration(configuration, params) ); @@ -189,7 +189,7 @@ export class DdSdkReactNative { `Adding attribute ${JSON.stringify(value)} for key ${key}`, SdkVerbosity.DEBUG ); - await DdSdk.addAttribute(key, { value }); + await NativeDdSdk.addAttribute(key, { value }); AttributesSingleton.getInstance().addAttribute(key, value); }; @@ -202,7 +202,7 @@ export class DdSdkReactNative { `Removing attribute for key ${key}`, SdkVerbosity.DEBUG ); - await DdSdk.removeAttribute(key); + await NativeDdSdk.removeAttribute(key); AttributesSingleton.getInstance().removeAttribute(key); }; @@ -216,7 +216,7 @@ export class DdSdkReactNative { `Adding attributes ${JSON.stringify(attributes)}`, SdkVerbosity.DEBUG ); - await DdSdk.addAttributes(attributes); + await NativeDdSdk.addAttributes(attributes); AttributesSingleton.getInstance().addAttributes(attributes); }; @@ -229,7 +229,7 @@ export class DdSdkReactNative { `Removing attributes for keys ${JSON.stringify(keys)}`, SdkVerbosity.DEBUG ); - await DdSdk.removeAttributes(keys); + await NativeDdSdk.removeAttributes(keys); AttributesSingleton.getInstance().removeAttributes(keys); }; @@ -252,7 +252,7 @@ export class DdSdkReactNative { SdkVerbosity.DEBUG ); - await DdSdk.setUserInfo(userInfo); + await NativeDdSdk.setUserInfo(userInfo); UserInfoSingleton.getInstance().setUserInfo(userInfo); }; @@ -262,7 +262,7 @@ export class DdSdkReactNative { */ static clearUserInfo = async (): Promise => { InternalLog.log('Clearing user info', SdkVerbosity.DEBUG); - await DdSdk.clearUserInfo(); + await NativeDdSdk.clearUserInfo(); UserInfoSingleton.getInstance().clearUserInfo(); }; @@ -296,7 +296,7 @@ export class DdSdkReactNative { } }; - await DdSdk.addUserExtraInfo(extraUserInfo); + await NativeDdSdk.addUserExtraInfo(extraUserInfo); UserInfoSingleton.getInstance().setUserInfo(updatedUserInfo); }; @@ -317,7 +317,7 @@ export class DdSdkReactNative { SdkVerbosity.DEBUG ); - await DdSdk.setAccountInfo(accountInfo); + await NativeDdSdk.setAccountInfo(accountInfo); AccountInfoSingleton.getInstance().setAccountInfo(accountInfo); }; @@ -327,7 +327,7 @@ export class DdSdkReactNative { */ static clearAccountInfo = async (): Promise => { InternalLog.log('Clearing account info', SdkVerbosity.DEBUG); - await DdSdk.clearAccountInfo(); + await NativeDdSdk.clearAccountInfo(); AccountInfoSingleton.getInstance().clearAccountInfo(); }; @@ -359,7 +359,7 @@ export class DdSdkReactNative { ...extraAccountInfo }; - await DdSdk.addAccountExtraInfo(extraInfo); + await NativeDdSdk.addAccountExtraInfo(extraInfo); AccountInfoSingleton.getInstance().addAccountExtraInfo( extraAccountInfo ); @@ -372,7 +372,7 @@ export class DdSdkReactNative { */ static setTrackingConsent = (consent: TrackingConsent): Promise => { InternalLog.log(`Setting consent ${consent}`, SdkVerbosity.DEBUG); - return DdSdk.setTrackingConsent(consent); + return NativeDdSdk.setTrackingConsent(consent); }; /** @@ -381,7 +381,7 @@ export class DdSdkReactNative { */ static clearAllData = (): Promise => { InternalLog.log('Clearing all data', SdkVerbosity.DEBUG); - return DdSdk.clearAllData(); + return NativeDdSdk.clearAllData(); }; private static buildConfiguration = ( @@ -464,7 +464,8 @@ export class DdSdkReactNative { configuration.trackWatchdogTerminations, configuration.batchProcessingLevel, configuration.initialResourceThreshold, - configuration.trackMemoryWarnings + configuration.trackMemoryWarnings, + configuration.attributeEncoders ); }; diff --git a/packages/core/src/DdSdkReactNativeConfiguration.tsx b/packages/core/src/DdSdkReactNativeConfiguration.tsx index 4ec1ef883..bcdf8e877 100644 --- a/packages/core/src/DdSdkReactNativeConfiguration.tsx +++ b/packages/core/src/DdSdkReactNativeConfiguration.tsx @@ -12,6 +12,7 @@ import type { ErrorEventMapper } from './rum/eventMappers/errorEventMapper'; import type { ResourceEventMapper } from './rum/eventMappers/resourceEventMapper'; import type { FirstPartyHost } from './rum/types'; import { PropagatorType } from './rum/types'; +import type { AttributeEncoder } from './sdk/AttributesEncoding/types'; import type { LogEventMapper } from './types'; export enum VitalsUpdateFrequency { @@ -323,6 +324,22 @@ export class DdSdkReactNativeConfiguration { */ public initialResourceThreshold?: number; + /** + * Optional list of custom encoders for attributes. + * + * Each encoder defines how to detect (`check`) and transform (`encode`) + * values of a specific type that is not handled by the built-in encoders + * (e.g., domain-specific objects, custom classes). + * + * These encoders are applied before the built-in ones. If an encoder + * successfully `check` a value, its `encode` result will be used. + * + * Example use cases: + * - Serializing a custom `UUID` class into a string + * - Handling third-party library objects that are not JSON-serializable + */ + public attributeEncoders: AttributeEncoder[] = []; + /** * Determines whether the SDK should track application termination by the watchdog on iOS. Default: `false`. */ diff --git a/packages/core/src/logs/DdLogs.ts b/packages/core/src/logs/DdLogs.ts index 32d3dac59..f00cbfdfa 100644 --- a/packages/core/src/logs/DdLogs.ts +++ b/packages/core/src/logs/DdLogs.ts @@ -9,8 +9,8 @@ import { DATADOG_MESSAGE_PREFIX, InternalLog } from '../InternalLog'; import { SdkVerbosity } from '../SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; import type { DdNativeLogsType } from '../nativeModulesTypes'; +import { encodeAttributes } from '../sdk/AttributesEncoding/attributesEncoding'; import type { ErrorSource, LogEventMapper } from '../types'; -import { validateContext } from '../utils/argsUtils'; import { getGlobalInstance } from '../utils/singletonUtils'; import { generateEventMapper } from './eventMapper'; @@ -38,7 +38,7 @@ const isLogWithError = ( typeof args[1] === 'string' || typeof args[2] === 'string' || typeof args[3] === 'string' || - typeof args[4] === 'object' || + (args[4] !== undefined && args[4] !== null) || typeof args[5] === 'string' ); }; @@ -160,13 +160,11 @@ class DdLogsWrapper implements DdLogsType { return generateEmptyPromise(); } - const validatedContext = validateContext(event.context); - this.printLogTracked(event.message, status); try { return await this.nativeLogs[status]( event.message, - validatedContext + encodeAttributes(event.context) ); } catch (error) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -210,9 +208,11 @@ class DdLogsWrapper implements DdLogsType { this.printLogTracked(mappedEvent.message, status); try { - const updatedContext = validateContext(mappedEvent.context); - - updatedContext[DdAttributes.errorSourceType] = 'react-native'; + const encodedContext = encodeAttributes(mappedEvent.context); + const updatedContext = { + ...encodedContext, + [DdAttributes.errorSourceType]: 'react-native' + }; if (fingerprint && fingerprint !== '') { updatedContext[DdAttributes.errorFingerprint] = fingerprint; diff --git a/packages/core/src/nativeModulesTypes.ts b/packages/core/src/nativeModulesTypes.ts index b05fb6e95..9b6d71e06 100644 --- a/packages/core/src/nativeModulesTypes.ts +++ b/packages/core/src/nativeModulesTypes.ts @@ -4,6 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ +import type { AttributeEncoder } from './sdk/AttributesEncoding/types'; import type { Spec as NativeDdLogs } from './specs/NativeDdLogs'; import type { Spec as NativeDdRum } from './specs/NativeDdRum'; import type { Spec as NativeDdSdk } from './specs/NativeDdSdk'; @@ -36,7 +37,8 @@ export class DdNativeSdkConfiguration { readonly sampleRate: number, readonly site: string, readonly trackingConsent: string, - readonly additionalConfiguration: object // eslint-disable-next-line no-empty-function + readonly additionalConfiguration: object, + readonly attributeEncoders: AttributeEncoder[] // eslint-disable-next-line no-empty-function ) {} } diff --git a/packages/core/src/rum/DdRum.ts b/packages/core/src/rum/DdRum.ts index 6f4a63fc8..6c3e7af2b 100644 --- a/packages/core/src/rum/DdRum.ts +++ b/packages/core/src/rum/DdRum.ts @@ -10,12 +10,12 @@ import { InternalLog } from '../InternalLog'; import { SdkVerbosity } from '../SdkVerbosity'; import { debugId } from '../metro/debugIdResolver'; import type { DdNativeRumType } from '../nativeModulesTypes'; +import { encodeAttributes } from '../sdk/AttributesEncoding/attributesEncoding'; import type { Attributes } from '../sdk/AttributesSingleton/types'; import { bufferVoidNativeCall } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; -import { DdSdk } from '../sdk/DdSdk'; +import { NativeDdSdk } from '../sdk/DdSdkInternal'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; import type { ErrorSource, FeatureOperationFailure } from '../types'; -import { validateContext } from '../utils/argsUtils'; import { getGlobalInstance } from '../utils/singletonUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; import type { TimeProvider } from '../utils/time-provider/TimeProvider'; @@ -76,7 +76,7 @@ class DdRumWrapper implements DdRumType { this.nativeRum.startView( key, name, - validateContext(context), + encodeAttributes(context), timestampMs ) ); @@ -89,7 +89,7 @@ class DdRumWrapper implements DdRumType { ): Promise => { InternalLog.log(`Stopping RUM View #${key}`, SdkVerbosity.DEBUG); return bufferVoidNativeCall(() => - this.nativeRum.stopView(key, validateContext(context), timestampMs) + this.nativeRum.stopView(key, encodeAttributes(context), timestampMs) ); }; @@ -108,7 +108,7 @@ class DdRumWrapper implements DdRumType { this.nativeRum.startAction( type, name, - validateContext(context), + encodeAttributes(context), timestampMs ) ); @@ -217,7 +217,7 @@ class DdRumWrapper implements DdRumType { this.nativeRum.addAction( mappedEvent.type, mappedEvent.name, - validatedContext, + encodeAttributes(mappedEvent.context), mappedEvent.timestampMs ) ); @@ -240,7 +240,7 @@ class DdRumWrapper implements DdRumType { key, method, url, - validateContext(context), + encodeAttributes(context), timestampMs ) ); @@ -296,7 +296,7 @@ class DdRumWrapper implements DdRumType { mappedEvent.statusCode, mappedEvent.kind, mappedEvent.size, - validatedContext, + encodeAttributes(mappedEvent.context), mappedEvent.timestampMs ) ); @@ -323,14 +323,11 @@ class DdRumWrapper implements DdRumType { return generateEmptyPromise(); } InternalLog.log(`Adding RUM Error “${message}”`, SdkVerbosity.DEBUG); - - const updatedContext: any = validateContext(mappedEvent.context); - + const updatedContext = encodeAttributes(mappedEvent.context); updatedContext[DdAttributes.errorSourceType] = 'react-native'; - const _debugId = debugId; - if (_debugId) { - updatedContext[DdAttributes.debugId] = _debugId; + if (debugId) { + updatedContext[DdAttributes.debugId] = debugId; } return bufferVoidNativeCall(() => @@ -528,13 +525,11 @@ class DdRumWrapper implements DdRumType { ); } - const validatedContext = validateContext(mappedEvent.context); - return bufferVoidNativeCall(() => this.nativeRum.stopAction( mappedEvent.type, mappedEvent.name, - validatedContext, + encodeAttributes(mappedEvent.context), mappedEvent.timestampMs ) ); @@ -561,20 +556,20 @@ class DdRumWrapper implements DdRumType { return [ args[0], args[1], - args[2] || {}, + args[2] ?? {}, args[3] || this.timeProvider.now() ]; } if (isOldStopActionAPI(args)) { if (this.lastActionData) { - DdSdk.telemetryDebug( + NativeDdSdk.telemetryDebug( 'DDdRum.stopAction called with the old signature' ); const { type, name } = this.lastActionData; return [ type, name, - args[0] || {}, + args[0] ?? {}, args[1] || this.timeProvider.now() ]; } diff --git a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx index 09cd447fb..b80309858 100644 --- a/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx +++ b/packages/core/src/rum/instrumentation/DdRumErrorTracking.tsx @@ -8,14 +8,15 @@ import type { ErrorHandlerCallback } from 'react-native'; import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; -import { ErrorSource } from '../../types'; +import { errorEncoder } from '../../sdk/AttributesEncoding/defaultEncoders'; import { + ERROR_DEFAULT_NAME, + ERROR_EMPTY_STACKTRACE, getErrorMessage, - getErrorStackTrace, - EMPTY_STACK_TRACE, getErrorName, - DEFAULT_ERROR_NAME -} from '../../utils/errorUtils'; + getErrorStackTrace +} from '../../sdk/AttributesEncoding/errorUtils'; +import { ErrorSource } from '../../types'; import { executeWithDelay } from '../../utils/jsUtils'; import { DdRum } from '../DdRum'; @@ -71,7 +72,7 @@ export class DdRumErrorTracking { const stacktrace = getErrorStackTrace(error); this.reportError(message, ErrorSource.SOURCE, stacktrace, { '_dd.error.is_crash': isFatal, - '_dd.error.raw': error + '_dd.error.raw': errorEncoder.encode(error) }).then(async () => { DdRumErrorTracking.isInDefaultErrorHandler = true; try { @@ -95,24 +96,24 @@ export class DdRumErrorTracking { return; } - let stack: string = EMPTY_STACK_TRACE; - let errorName: string = DEFAULT_ERROR_NAME; + let stack: string = ERROR_EMPTY_STACKTRACE; + let errorName: string = ERROR_DEFAULT_NAME; for (let i = 0; i < params.length; i += 1) { const param = params[i]; const paramStack = getErrorStackTrace(param); - if (paramStack !== EMPTY_STACK_TRACE) { + if (paramStack !== ERROR_EMPTY_STACKTRACE) { stack = paramStack; } const paramErrorName = getErrorName(param); - if (paramErrorName !== DEFAULT_ERROR_NAME) { + if (paramErrorName !== ERROR_DEFAULT_NAME) { errorName = paramErrorName; } if ( - errorName !== DEFAULT_ERROR_NAME && - stack !== EMPTY_STACK_TRACE + errorName !== ERROR_DEFAULT_NAME && + stack !== ERROR_EMPTY_STACKTRACE ) { break; } diff --git a/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx b/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx index 03fe98262..0e273435c 100644 --- a/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx +++ b/packages/core/src/rum/instrumentation/interactionTracking/DdRumUserInteractionTracking.tsx @@ -8,8 +8,8 @@ import React from 'react'; import { InternalLog } from '../../../InternalLog'; import { SdkVerbosity } from '../../../SdkVerbosity'; -import { DdSdk } from '../../../sdk/DdSdk'; -import { getErrorMessage } from '../../../utils/errorUtils'; +import { getErrorMessage } from '../../../sdk/AttributesEncoding/errorUtils'; +import { NativeDdSdk } from '../../../sdk/DdSdkInternal'; import { BABEL_PLUGIN_TELEMETRY } from '../../constants'; import { DdBabelInteractionTracking } from './DdBabelInteractionTracking'; @@ -72,7 +72,7 @@ export class DdRumUserInteractionTracking { return; } - DdSdk?.sendTelemetryLog( + NativeDdSdk?.sendTelemetryLog( BABEL_PLUGIN_TELEMETRY, DdBabelInteractionTracking.getTelemetryConfig(), { onlyOnce: true } @@ -116,7 +116,7 @@ export class DdRumUserInteractionTracking { }; } } catch (e) { - DdSdk.telemetryDebug(getErrorMessage(e)); + NativeDdSdk.telemetryDebug(getErrorMessage(e)); } const originalMemo = React.memo; diff --git a/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts b/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts new file mode 100644 index 000000000..9cd2718ff --- /dev/null +++ b/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts @@ -0,0 +1,266 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import { DdSdk } from '../../DdSdk'; +import { encodeAttributes } from '../attributesEncoding'; +import { warn } from '../utils'; + +jest.mock('../utils', () => ({ + ...jest.requireActual('../utils'), + warn: jest.fn() +})); + +const setEncoders = (encoders: any[]) => { + (DdSdk as any)?._setAttributeEncodersForTesting(encoders); +}; + +describe('encodeAttributes', () => { + beforeEach(() => { + (warn as jest.Mock).mockClear(); + setEncoders([]); + }); + + it('wraps root string under context', () => { + const result = encodeAttributes('foo'); + expect(result).toEqual({ context: 'foo' }); + expect(warn).toHaveBeenCalled(); + }); + + it('wraps root number under context', () => { + const result = encodeAttributes(123); + expect(result).toEqual({ context: 123 }); + expect(warn).toHaveBeenCalled(); + }); + + it('wraps root array under context', () => { + const result = encodeAttributes([1, 2, 3]); + expect(result).toEqual({ context: [1, 2, 3] }); + expect(warn).toHaveBeenCalled(); + }); + + it('drops unsupported root function', () => { + const result = encodeAttributes(() => {}); + expect(result).toEqual({}); + expect(warn).toHaveBeenCalled(); + }); + + it('drops unsupported root symbol', () => { + const result = encodeAttributes(Symbol('x')); + expect(result).toEqual({}); + expect(warn).toHaveBeenCalled(); + }); + + it('flattens nested objects using dot syntax', () => { + const input = { user: { profile: { name: 'Alice' } } }; + const result = encodeAttributes(input); + expect(result).toEqual({ 'user.profile.name': 'Alice' }); + expect(warn).not.toHaveBeenCalled(); + }); + + it('keeps arrays as arrays inside objects', () => { + const input = { tags: ['a', 'b'] }; + const result = encodeAttributes(input); + expect(result).toEqual({ tags: ['a', 'b'] }); + }); + + it('flattens nested arrays of objects', () => { + const input = { arr: [{ x: 1 }, { y: 2 }] }; + const result = encodeAttributes(input); + expect(result).toEqual({ + arr: [{ x: 1 }, { y: 2 }] + }); + }); + + it('applies custom attribute encoders before built-in ones', () => { + setEncoders([ + { + check: (v: any): v is Date => v instanceof Date, + encode: (d: Date) => 'CUSTOM_DATE' + } + ]); + + const result = encodeAttributes({ now: new Date() }); + expect(result).toEqual({ now: 'CUSTOM_DATE' }); + }); + + it('applies built-in Date encoder if no custom encoder is provided', () => { + const date = new Date('2020-01-01T12:00:00Z'); + const result = encodeAttributes({ now: date }); + expect(typeof result.now).toBe('string'); + expect(result.now).toContain('2020'); + }); + + it('applies built-in Error encoder', () => { + const error = new Error('boom'); + const result = encodeAttributes({ err: error }); + expect(result['err.name']).toBe('Error'); + expect(result['err.message']).toBe('boom'); + expect(result['err.stack']).toContain('Error: boom'); + }); + + it('applies built-in Map encoder', () => { + const map = new Map([ + ['k1', 1], + ['k2', { nested: 'yes' }] + ]); + const result = encodeAttributes({ data: map }); + expect(Array.isArray(result.data)).toBe(true); + expect(result.data).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: 'k1', + keyType: 'string', + value: 1 + }), + expect.objectContaining({ + key: 'k2', + keyType: 'string', + 'value.nested': 'yes' + }) + ]) + ); + }); + + it('drops unsupported nested values', () => { + const input = { valid: 'ok', bad: () => {} }; + const result = encodeAttributes(input); + expect(result).toEqual({ valid: 'ok' }); + }); + + it('handles deeply nested objects', () => { + const deep = { level1: { level2: { level3: { value: 42 } } } }; + const result = encodeAttributes(deep); + expect(result).toEqual({ 'level1.level2.level3.value': 42 }); + }); + + it('handles object with manual dot keys', () => { + const input = { 'user.profile.name': 'Bob' }; + const result = encodeAttributes(input); + expect(result).toEqual({ 'user.profile.name': 'Bob' }); + }); + + it('handles array with mixed values', () => { + const input = [1, 'two', { nested: true }]; + const result = encodeAttributes(input); + expect(result).toEqual({ context: [1, 'two', { nested: true }] }); + }); + + it('handles empty object gracefully', () => { + const result = encodeAttributes({}); + expect(result).toEqual({}); + expect(warn).not.toHaveBeenCalled(); + }); + + it('handles empty array gracefully at root', () => { + const result = encodeAttributes([]); + expect(result).toEqual({ context: [] }); + expect(warn).toHaveBeenCalled(); + }); + + it('handles NaN and Infinity by dropping them', () => { + const result = encodeAttributes({ + bad1: NaN, + bad2: Infinity, + good: 42 + }); + expect(result).toEqual({ good: 42 }); + }); + + it('flattens object nested inside array', () => { + const input = { arr: [{ foo: 'bar' }] }; + const result = encodeAttributes(input); + expect(result).toEqual({ arr: [{ foo: 'bar' }] }); + }); + + it('handles array of arrays correctly', () => { + const input = { + matrix: [ + [1, 2], + [3, 4] + ] + }; + const result = encodeAttributes(input); + expect(result).toEqual({ + matrix: [ + [1, 2], + [3, 4] + ] + }); + }); + + it('drops functions inside arrays', () => { + const input = { arr: [1, () => {}, 3] }; + const result = encodeAttributes(input); + expect(result.arr).toEqual([1, 3]); + }); + + it('encodes nested Maps inside objects', () => { + const map = new Map([['nested', new Map([['k', 'v']])]]); + const result = encodeAttributes({ outer: map }); + expect(result.outer).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + key: 'nested', + value: expect.arrayContaining([ + expect.objectContaining({ key: 'k', value: 'v' }) + ]) + }) + ]) + ); + }); + + it('handles deeply nested array of objects', () => { + const input = { items: [[{ foo: 'bar' }]] }; + const result = encodeAttributes(input); + expect(result.items).toEqual([[{ foo: 'bar' }]]); + }); + + it('handles objects with undefined values by dropping them', () => { + const input = { a: 1, b: undefined, c: 'ok' }; + const result = encodeAttributes(input); + expect(result).toEqual({ a: 1, c: 'ok' }); + }); + + it('custom encoder can override primitive handling', () => { + setEncoders([ + { + check: (v: any): v is number => typeof v === 'number', + encode: (n: number) => `num:${n}` + } + ]); + const result = encodeAttributes({ a: 5 }); + expect(result).toEqual({ a: 'num:5' }); + }); + + it('handles object with both dot syntax and nested keys without collisions', () => { + const input = { + 'user.profile.name': 'Alice', + user: { profile: { age: 30 } } + }; + const result = encodeAttributes(input); + expect(result).toEqual({ + 'user.profile.name': 'Alice', + 'user.profile.age': 30 + }); + }); + + it('handles null and undefined keys in Map', () => { + const map = new Map([ + [null, 'nullKey'], + [undefined, 'undefinedKey'] + ]); + const result = encodeAttributes({ myMap: map }); + expect(result.myMap).toEqual( + expect.arrayContaining([ + expect.objectContaining({ key: 'null', value: 'nullKey' }), + expect.objectContaining({ + key: 'undefined', + value: 'undefinedKey' + }) + ]) + ); + }); +}); diff --git a/packages/core/src/sdk/AttributesEncoding/__tests__/defaultEncoders.test.ts b/packages/core/src/sdk/AttributesEncoding/__tests__/defaultEncoders.test.ts new file mode 100644 index 000000000..88c0c0242 --- /dev/null +++ b/packages/core/src/sdk/AttributesEncoding/__tests__/defaultEncoders.test.ts @@ -0,0 +1,198 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import { + stringEncoder, + numberEncoder, + booleanEncoder, + nullishEncoder, + arrayEncoder, + dateEncoder, + errorEncoder, + mapEncoder +} from '../defaultEncoders'; +import { warn } from '../utils'; + +jest.mock('../utils', () => ({ + ...jest.requireActual('../utils'), + warn: jest.fn() +})); + +describe('default encoders', () => { + beforeEach(() => { + (warn as jest.Mock).mockClear(); + }); + + describe('stringEncoder', () => { + it('encodes a string directly', () => { + expect(stringEncoder.check('foo')).toBe(true); + expect(stringEncoder.encode('foo')).toBe('foo'); + }); + it('rejects non-strings', () => { + expect(stringEncoder.check(123)).toBe(false); + }); + }); + + describe('numberEncoder', () => { + it('encodes finite numbers', () => { + expect(numberEncoder.check(42)).toBe(true); + expect(numberEncoder.encode(42)).toBe(42); + }); + it('drops NaN and Infinity', () => { + expect(numberEncoder.encode(NaN)).toBeUndefined(); + expect(numberEncoder.encode(Infinity)).toBeUndefined(); + }); + }); + + describe('booleanEncoder', () => { + it('encodes booleans directly', () => { + expect(booleanEncoder.check(true)).toBe(true); + expect(booleanEncoder.encode(true)).toBe(true); + expect(booleanEncoder.encode(false)).toBe(false); + }); + }); + + describe('nullishEncoder', () => { + it('encodes null and undefined directly', () => { + expect(nullishEncoder.check(null)).toBe(true); + expect(nullishEncoder.check(undefined)).toBe(true); + expect(nullishEncoder.encode(null)).toBeNull(); + expect(nullishEncoder.encode(undefined)).toBeUndefined(); + }); + it('rejects non-nullish values', () => { + expect(nullishEncoder.check('')).toBe(false); + }); + }); + + describe('arrayEncoder', () => { + it('encodes array of primitives', () => { + const result = arrayEncoder.encode([1, 'a', true]); + expect(result).toEqual([1, 'a', true]); + }); + it('encodes nested objects inside array', () => { + const result = arrayEncoder.encode([{ foo: 'bar' }]); + expect((result as Record[])[0]).toHaveProperty( + 'foo', + 'bar' + ); + }); + it('encodes nested arrays recursively', () => { + const result = arrayEncoder.encode([[1, 2], ['a']]); + expect(result).toEqual([[1, 2], ['a']]); + }); + }); + + describe('dateEncoder', () => { + it('encodes Date to string', () => { + const date = new Date('2020-01-01T00:00:00Z'); + expect(dateEncoder.check(date)).toBe(true); + expect(dateEncoder.encode(date)).toEqual(String(date)); + }); + it('rejects non-Date values', () => { + expect(dateEncoder.check('2020-01-01')).toBe(false); + }); + }); + + describe('errorEncoder', () => { + it('encodes Error with name, message, and stack', () => { + const error = new Error('boom'); + const result = errorEncoder.encode(error) as Record; + expect(result.name).toBe('Error'); + expect(result.message).toBe('boom'); + expect(result.stack).toContain('Error: boom'); + }); + + it('removes duplicate fields like stack', () => { + const err = { + message: 'fail' + } as Record; + + err.name = 'CustomError'; + err.stacktrace = 'custom-stack'; + err.stack = 'error-stacktrace'; + err.componentStack = 'component-stack'; + + const result = errorEncoder.encode(err) as Record; + expect(result.name).toBe('CustomError'); + expect(result.message).toBe('fail'); + expect(result.stack).toBe('custom-stack'); + expect(result).not.toHaveProperty('stacktrace'); + expect(result).toHaveProperty('componentStack'); + }); + + it('encodes error with cause', () => { + const cause = new Error('inner'); + const err: any = new Error('outer'); + err.cause = cause; + const result = errorEncoder.encode(err) as Record; + expect(result.cause).toBe(cause); + }); + }); + + describe('mapEncoder', () => { + it('encodes map with string keys', () => { + const map = new Map([ + ['a', 1], + ['b', 'str'] + ]); + const result = mapEncoder.encode(map); + expect(result).toEqual( + expect.arrayContaining([ + { key: 'a', keyType: 'string', value: 1 }, + { key: 'b', keyType: 'string', value: 'str' } + ]) + ); + }); + + it('encodes map with object key', () => { + const keyObj = { toString: () => 'objKey' }; + const map = new Map([[keyObj, 123]]); + const result = mapEncoder.encode(map); + expect((result as Record[])[0]).toHaveProperty( + 'key', + 'objKey' + ); + expect((result as Record[])[0]).toHaveProperty( + 'keyType', + 'object' + ); + }); + + it('encodes map with symbol key', () => { + const map = new Map([[Symbol('s'), 'val']]); + const result = mapEncoder.encode(map); + expect((result as Record[])[0].key).toContain( + 'Symbol(s)' + ); + expect((result as Record[])[0].keyType).toBe('symbol'); + }); + + it('encodes map with null and undefined keys', () => { + const map = new Map([ + [null, 'nullVal'], + [undefined, 'undefVal'] + ]); + const result = mapEncoder.encode(map); + expect(result).toEqual( + expect.arrayContaining([ + { key: 'null', keyType: 'object', value: 'nullVal' }, + { + key: 'undefined', + keyType: 'undefined', + value: 'undefVal' + } + ]) + ); + }); + + it('warns and drops unsupported key types', () => { + const map = new Map([[BigInt(1), 'big']]); + const result = mapEncoder.encode(map); + expect((result as Record[])[0].key).toBe('1'); // bigint stringified + expect(warn).not.toHaveBeenCalled(); // bigint is allowed + }); + }); +}); diff --git a/packages/core/src/sdk/AttributesEncoding/attributesEncoding.tsx b/packages/core/src/sdk/AttributesEncoding/attributesEncoding.tsx new file mode 100644 index 000000000..0f05d7e11 --- /dev/null +++ b/packages/core/src/sdk/AttributesEncoding/attributesEncoding.tsx @@ -0,0 +1,41 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import { DdSdk } from '../DdSdk'; + +import { builtInEncoders } from './defaultEncoders'; +import { encodeAttributesInPlace } from './helpers'; +import type { Encodable } from './types'; +import { isPlainObject, warn } from './utils'; + +/** + * Encodes arbitrary input into a flat dictionary of attributes. + * - Objects are flattened using dot syntax. Max depth handling is done on the native layer by the + * Android and iOS SDKs. + * - We assume the input does not always conform to Record and we: + * - Fallback to { context: givenValue } if a primitive is passed + * - Apply built-in and consumer encoders to all values + * - Drop values of unsupported types + */ +export function encodeAttributes(input: unknown): Record { + const result: Record = {}; + const allEncoders = [...DdSdk.attributeEncoders, ...builtInEncoders]; + + if (isPlainObject(input)) { + for (const [k, v] of Object.entries(input)) { + encodeAttributesInPlace(v, result, [k], allEncoders); + } + } else { + // Fallback for primitive values passed as root + encodeAttributesInPlace(input, result, ['context'], allEncoders); + warn( + 'Warning: attributes root should be an object.\n' + + 'Received a primitive/array instead, which will be wrapped under the "context" key.' + ); + } + + return result; +} diff --git a/packages/core/src/sdk/AttributesEncoding/defaultEncoders.tsx b/packages/core/src/sdk/AttributesEncoding/defaultEncoders.tsx new file mode 100644 index 000000000..fdee006f1 --- /dev/null +++ b/packages/core/src/sdk/AttributesEncoding/defaultEncoders.tsx @@ -0,0 +1,185 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +/* ---------------------------------------- + * Built-in encoders + * -------------------------------------- */ +import { DdSdk } from '../DdSdk'; + +import { + getErrorMessage, + getErrorName, + getErrorStackTrace +} from './errorUtils'; +import { encodeAttributesInPlace, sanitizeForJson } from './helpers'; +import type { AttributeEncoder, Encodable } from './types'; +import { warn } from './utils'; + +/** Primitives: keep them explicit so the full pipeline is used uniformly. */ +export const stringEncoder: AttributeEncoder = { + check: (v): v is string => typeof v === 'string', + encode: v => v +}; + +export const numberEncoder: AttributeEncoder = { + check: (v): v is number => typeof v === 'number', + encode: v => (Number.isFinite(v) ? v : undefined) // drop non-finite +}; + +export const booleanEncoder: AttributeEncoder = { + check: (v): v is boolean => typeof v === 'boolean', + encode: v => v +}; + +export const nullishEncoder: AttributeEncoder = { + check: (v): v is null | undefined => v === null || v === undefined, + encode: v => v +}; + +/** + * Array encoder: + * - Sanitizes each item through the encoder pipeline. + * - Returns the sanitized array (it may later be flattened by the visitor if it still contains objects). + */ +export const arrayEncoder: AttributeEncoder = { + check: Array.isArray, + encode: (arr: unknown[]) => + arr.map(x => + sanitizeForJson(x, [...DdSdk.attributeEncoders, ...builtInEncoders]) + ) +}; + +/** + * Default Datadog Date Encoder. + * This does not make assumptions on format; uses String(date). + */ +export const dateEncoder: AttributeEncoder = { + check: (v: unknown): v is Date => v instanceof Date, + encode: (d: Date) => String(d) +}; +/* + } else if ('componentStack' in error) { + stack = String(error.componentStack); + } else if ( + 'sourceURL' in error && + 'line' in error && + 'column' in error + ) { + + +*/ +/** + * Extended Error Encoder. + * Serializes name, message, stack, and cause (ES2022+) for Error objects. + * If the error has other enumerable properties, they are included and sanitized. + */ +export const errorEncoder: AttributeEncoder = { + check: (v: unknown): v is Error => v instanceof Error, + encode: (e: any) => { + const extraAttributes: Record = {}; + + // In React Native, some errors have extra fields we want to capture + if (e && typeof e === 'object') { + const allEncoders = [ + ...DdSdk.attributeEncoders, + ...builtInEncoders + ]; + encodeAttributesInPlace(e, extraAttributes, [], allEncoders); + } + + // Remove fields that are duplicated in the dedicated fields below + if ('stacktrace' in e) { + delete extraAttributes['stacktrace']; + } else if ('stack' in e) { + delete extraAttributes['stack']; + } else if ('componentStack' in e) { + delete extraAttributes['componentStack']; + } + + return { + ...extraAttributes, + name: getErrorName(e), + message: getErrorMessage(e), + stack: getErrorStackTrace(e), + cause: (e as any).cause + }; + } +}; + +/** + * Map encoder: + * - Converts Map into an array of entries. + * - Each entry is { key: string, keyType: string, value: Encodable }. + * - Keys are stringified with type info to reduce collision risk. + * - Entries with un-stringifiable keys are dropped (with a warning). + */ +export const mapEncoder: AttributeEncoder> = { + check: (v: unknown): v is Map => v instanceof Map, + encode: (map: Map) => { + const entries: Encodable[] = []; + + for (const [k, v] of map.entries()) { + try { + const keyType = typeof k; + let keyStr: string; + + if (k === null) { + keyStr = 'null'; + } else if (k === undefined) { + keyStr = 'undefined'; + } else if ( + keyType === 'string' || + keyType === 'number' || + keyType === 'boolean' || + keyType === 'bigint' + ) { + keyStr = String(k); + } else if (typeof k === 'symbol') { + keyStr = k.description + ? `Symbol(${k.description})` + : 'Symbol'; + } else if (typeof k === 'object' || typeof k === 'function') { + // Try to get a descriptive form + if (typeof (k as any).toString === 'function') { + keyStr = (k as any).toString(); + } else { + keyStr = Object.prototype.toString.call(k); // e.g. "[object Object]" + } + } else { + warn( + `Dropping Map entry: unsupported key type "${keyType}".` + ); + continue; + } + + const allEncoders = [ + ...DdSdk.attributeEncoders, + ...builtInEncoders + ]; + entries.push({ + key: keyStr, + keyType, + value: sanitizeForJson(v, allEncoders) + }); + } catch (err) { + warn(`Failed to encode Map key: ${k}. ERROR: ${String(err)}`); + } + } + + return entries; + } +}; + +export const builtInEncoders = [ + stringEncoder, + numberEncoder, + booleanEncoder, + nullishEncoder, + arrayEncoder, + dateEncoder, + errorEncoder, + mapEncoder +]; diff --git a/packages/core/src/utils/errorUtils.ts b/packages/core/src/sdk/AttributesEncoding/errorUtils.tsx similarity index 55% rename from packages/core/src/utils/errorUtils.ts rename to packages/core/src/sdk/AttributesEncoding/errorUtils.tsx index ca7b3a715..b8692959b 100644 --- a/packages/core/src/utils/errorUtils.ts +++ b/packages/core/src/sdk/AttributesEncoding/errorUtils.tsx @@ -3,23 +3,9 @@ * This product includes software developed at Datadog (https://www.datadoghq.com/). * Copyright 2016-Present Datadog, Inc. */ - -export const EMPTY_MESSAGE = 'Unknown Error'; -export const EMPTY_STACK_TRACE = ''; -export const DEFAULT_ERROR_NAME = 'Error'; - -export const getErrorMessage = (error: any | undefined): string => { - let message = EMPTY_MESSAGE; - if (error === undefined || error === null) { - message = EMPTY_MESSAGE; - } else if (typeof error === 'object' && 'message' in error) { - message = String(error.message); - } else { - message = String(error); - } - - return message; -}; +export const ERROR_EMPTY_STACKTRACE = ''; +export const ERROR_EMPTY_MESSAGE = 'Unknown Error'; +export const ERROR_DEFAULT_NAME = 'Error'; /** * Will extract the stack from the error, taking the first key found among: @@ -30,13 +16,13 @@ export const getErrorMessage = (error: any | undefined): string => { * generate a stack from this information. */ export const getErrorStackTrace = (error: any | undefined): string => { - let stack = EMPTY_STACK_TRACE; + let stack = ERROR_EMPTY_STACKTRACE; try { if (error === undefined || error === null) { - stack = EMPTY_STACK_TRACE; + stack = ERROR_EMPTY_STACKTRACE; } else if (typeof error === 'string') { - stack = EMPTY_STACK_TRACE; + stack = ERROR_EMPTY_STACKTRACE; } else if (typeof error === 'object') { if ('stacktrace' in error) { stack = String(error.stacktrace); @@ -58,10 +44,52 @@ export const getErrorStackTrace = (error: any | undefined): string => { return stack; }; +export const getErrorMessage = (error: any | undefined): string => { + if (error == null) { + return ERROR_EMPTY_MESSAGE; + } + + // If it's an actual Error (or subclass) + if (error instanceof Error) { + // Prefer .message if defined, otherwise fallback to .toString() + return error.message || error.toString() || ERROR_EMPTY_MESSAGE; + } + + // If it's an object with a message property (not necessarily Error) + if ( + typeof error === 'object' && + 'message' in error && + typeof (error as any).message === 'string' + ) { + return (error as any).message || ERROR_EMPTY_MESSAGE; + } + + // If it’s a primitive (string, number, boolean, symbol) + if ( + typeof error === 'string' || + typeof error === 'number' || + typeof error === 'boolean' || + typeof error === 'symbol' + ) { + return String(error); + } + + // If it has its own toString (not the default Object one) + if ( + typeof error?.toString === 'function' && + error.toString !== Object.prototype.toString + ) { + return error.toString(); + } + + // Fallback + return ERROR_EMPTY_MESSAGE; +}; + export const getErrorName = (error: unknown): string => { try { if (typeof error !== 'object' || error === null) { - return DEFAULT_ERROR_NAME; + return ERROR_DEFAULT_NAME; } if (typeof (error as any).name === 'string') { return (error as any).name; @@ -69,5 +97,5 @@ export const getErrorName = (error: unknown): string => { } catch (e) { // Do nothing } - return DEFAULT_ERROR_NAME; + return ERROR_DEFAULT_NAME; }; diff --git a/packages/core/src/sdk/AttributesEncoding/helpers.tsx b/packages/core/src/sdk/AttributesEncoding/helpers.tsx new file mode 100644 index 000000000..3636be9e1 --- /dev/null +++ b/packages/core/src/sdk/AttributesEncoding/helpers.tsx @@ -0,0 +1,134 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import type { AttributeEncoder, Encodable } from './types'; +import { formatPathForLog, isPlainObject, warn } from './utils'; + +/** + * Recursive in-place encoder: flattens values into `out` dictionary. + * Never applies "context", that's only for the root. + */ +export function encodeAttributesInPlace( + input: unknown, + out: Record, + path: string[], + encoders: AttributeEncoder[] +): void { + const value = applyEncoders(input, encoders); + + // Nullish / primitive + if ( + value === null || + value === undefined || + typeof value === 'string' || + typeof value === 'number' || + typeof value === 'boolean' + ) { + out[path.join('.')] = value; + return; + } + + // Arrays + if (Array.isArray(value)) { + const normalize = (x: unknown): Encodable => { + const v = applyEncoders(x, encoders); + + // Primitive / nullish are fine + if ( + v === null || + v === undefined || + typeof v === 'string' || + typeof v === 'number' || + typeof v === 'boolean' + ) { + return v; + } + + if (isPlainObject(v)) { + const nested: Record = {}; + encodeAttributesInPlace(v, nested, [], encoders); + return nested; + } + + if (Array.isArray(v)) { + return v.map(normalize); + } + + // Unsupported + warn( + `Dropped unsupported value in array at '${formatPathForLog( + path + )}': ${String(v)}` + ); + return undefined; + }; + + out[path.join('.')] = value + .map(normalize) + .filter(item => item !== undefined); // drop unsupported + return; + } + + // Plain object + if (isPlainObject(value)) { + for (const [k, v] of Object.entries(value)) { + encodeAttributesInPlace(v, out, [...path, k], encoders); + } + return; + } + + // Unsupported + warn( + `Dropped unsupported value at '${formatPathForLog(path)}': ${String( + value + )}` + ); +} + +/** + * Sanitize unknown input to be JSON-serializable using encoders. + * This does not flatten—it's a pure sanitization pass (used by some encoders). + */ +export function sanitizeForJson( + value: unknown, + encoders: AttributeEncoder[] +): Encodable { + const v = applyEncoders(value, encoders); + + // If still a plain object, sanitize shallowly + if (isPlainObject(v)) { + const out: Record = {}; + for (const [k, val] of Object.entries(v)) { + encodeAttributesInPlace(val, out, [k], encoders); + } + return out; + } + + // If array, sanitize items + if (Array.isArray(v)) { + return v.map(item => sanitizeForJson(item, encoders)); + } + + return v; +} + +export function applyEncoders( + value: unknown, + encoders: AttributeEncoder[] +): Encodable { + for (const enc of encoders) { + try { + if (enc.check(value)) { + return enc.encode(value as never); + } + } catch (err) { + warn(`Encoder error: ${String(err)}`); + return undefined; + } + } + // Not matched by any encoder; leave as-is for the visitor to decide + return value as Encodable; +} diff --git a/packages/core/src/sdk/AttributesEncoding/types.tsx b/packages/core/src/sdk/AttributesEncoding/types.tsx new file mode 100644 index 000000000..1f136d576 --- /dev/null +++ b/packages/core/src/sdk/AttributesEncoding/types.tsx @@ -0,0 +1,33 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +/** + * A value that can safely be encoded as a Datadog Attribute. + */ +export type Encodable = + | string + | number + | boolean + | null + | undefined + | Encodable[] + | { [key: string]: Encodable }; + +export type AttributeKey = string; +export type AttributeValue = Encodable; +export type EncodableAttributes = Record; +export type Attributes = Record; + +/** + * Encoders define how to handle special object types (Date, Error, Map, etc.). + * Each encoder is: + * - check: decides if the value can be handled by this encoder. + * - encode: converts the value into an Encodable. + */ +export interface AttributeEncoder { + check: (value: unknown) => boolean; + encode: (value: T) => Encodable; +} diff --git a/packages/core/src/sdk/AttributesEncoding/utils.tsx b/packages/core/src/sdk/AttributesEncoding/utils.tsx new file mode 100644 index 000000000..11e3adfdf --- /dev/null +++ b/packages/core/src/sdk/AttributesEncoding/utils.tsx @@ -0,0 +1,31 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import { InternalLog } from '../../InternalLog'; +import { SdkVerbosity } from '../../SdkVerbosity'; + +export function warn(text: string) { + InternalLog.log(`[ATTRIBUTES] ${text}`, SdkVerbosity.WARN); +} + +export function isPlainObject(v: unknown): v is Record { + return !!v && typeof v === 'object' && (v as any).constructor === Object; +} + +/** + * Utility: format a path array into dot/bracket notation (for logs). + */ +export function formatPathForLog(path: (string | number)[]): string { + return path + .map((segment, index) => { + const s = String(segment); + if (/^\d/.test(s)) { + return `[${s}]`; + } + return index === 0 ? s : `.${s}`; + }) + .join(''); +} diff --git a/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts b/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts index dc91a475f..93f9389b0 100644 --- a/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts +++ b/packages/core/src/sdk/DatadogProvider/Buffer/BoundedBuffer.ts @@ -6,8 +6,8 @@ import { InternalLog } from '../../../InternalLog'; import { SdkVerbosity } from '../../../SdkVerbosity'; -import { DdSdk } from '../../../sdk/DdSdk'; -import { getErrorStackTrace } from '../../../utils/errorUtils'; +import { getErrorStackTrace } from '../../AttributesEncoding/errorUtils'; +import { NativeDdSdk } from '../../DdSdkInternal'; import { DatadogBuffer } from './DatadogBuffer'; @@ -206,7 +206,7 @@ export class BoundedBuffer extends DatadogBuffer { private drainTelemetry = () => { Object.values(this.telemetryBuffer).forEach( ({ message, stack, kind, occurrences }) => { - DdSdk.telemetryError( + NativeDdSdk.telemetryError( `${message} happened ${occurrences} times.`, stack, kind diff --git a/packages/core/src/sdk/DdSdk.ts b/packages/core/src/sdk/DdSdk.ts deleted file mode 100644 index b086d96bd..000000000 --- a/packages/core/src/sdk/DdSdk.ts +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -import type { DdNativeSdkType } from '../nativeModulesTypes'; - -// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires -const DdSdk: DdNativeSdkType = require('../specs/NativeDdSdk').default; - -export { DdSdk }; diff --git a/packages/core/src/sdk/DdSdk.tsx b/packages/core/src/sdk/DdSdk.tsx new file mode 100644 index 000000000..a829707e3 --- /dev/null +++ b/packages/core/src/sdk/DdSdk.tsx @@ -0,0 +1,15 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ +import { getGlobalInstance } from '../utils/singletonUtils'; + +import { DdSdkWrapper } from './DdSdkInternal'; +import type { DdSdkType } from './DdSdkInternal'; + +const CORE_MODULE = 'com.datadog.reactnative.core'; +export const DdSdk = getGlobalInstance( + CORE_MODULE, + () => new DdSdkWrapper() +) as DdSdkType; diff --git a/packages/core/src/sdk/DdSdkInternal.tsx b/packages/core/src/sdk/DdSdkInternal.tsx new file mode 100644 index 000000000..7af33d2e3 --- /dev/null +++ b/packages/core/src/sdk/DdSdkInternal.tsx @@ -0,0 +1,105 @@ +/* + * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. + * This product includes software developed at Datadog (https://www.datadoghq.com/). + * Copyright 2016-Present Datadog, Inc. + */ + +import type { + DdNativeSdkConfiguration, + DdNativeSdkType +} from '../nativeModulesTypes'; + +import type { AttributeEncoder } from './AttributesEncoding/types'; + +// eslint-disable-next-line global-require, @typescript-eslint/no-var-requires +const NativeDdSdk: DdNativeSdkType = require('../specs/NativeDdSdk').default; + +export type DdSdkType = { + readonly attributeEncoders: AttributeEncoder[]; + + /** + * Initializes Datadog's features. + * @param configuration: The configuration to use. + */ + initialize(configuration: DdNativeSdkConfiguration): Promise; +}; + +export class DdSdkWrapper implements DdNativeSdkType { + get attributeEncoders(): AttributeEncoder[] { + return this._attributeEncoders; + } + private _attributeEncoders: AttributeEncoder[] = []; + + initialize(configuration: DdNativeSdkConfiguration): Promise { + this._attributeEncoders = [...configuration.attributeEncoders]; + return NativeDdSdk.initialize(configuration); + } + + getConstants() { + return NativeDdSdk.getConstants(); + } + + setAttributes(attributes: object): Promise { + return NativeDdSdk.setAttributes(attributes); + } + + setUserInfo(user: object): Promise { + return NativeDdSdk.setUserInfo(user); + } + + clearUserInfo(): Promise { + return NativeDdSdk.clearUserInfo(); + } + + addUserExtraInfo(extraInfo: object): Promise { + return NativeDdSdk.addUserExtraInfo(extraInfo); + } + + setTrackingConsent(trackingConsent: string): Promise { + return NativeDdSdk.setTrackingConsent(trackingConsent); + } + + sendTelemetryLog( + message: string, + attributes: object, + config: object + ): Promise { + return NativeDdSdk.sendTelemetryLog(message, attributes, config); + } + + telemetryDebug(message: string): Promise { + return NativeDdSdk.telemetryDebug(message); + } + + telemetryError( + message: string, + stack: string, + kind: string + ): Promise { + return NativeDdSdk.telemetryError(message, stack, kind); + } + + consumeWebviewEvent(message: string): Promise { + return NativeDdSdk.consumeWebviewEvent(message); + } + + clearAllData(): Promise { + return NativeDdSdk.clearAllData(); + } + + addListener(eventType: string): void { + return NativeDdSdk.addListener(eventType); + } + + removeListeners(count: number): void { + return NativeDdSdk.removeListeners(count); + } + + _setAttributeEncodersForTesting( + attributeEncoders: AttributeEncoder[] + ) { + this._attributeEncoders = [...attributeEncoders]; + } +} + +export { NativeDdSdk }; diff --git a/packages/core/src/sdk/EventMappers/EventMapper.ts b/packages/core/src/sdk/EventMappers/EventMapper.ts index e1cbaae19..0f3878fe1 100644 --- a/packages/core/src/sdk/EventMappers/EventMapper.ts +++ b/packages/core/src/sdk/EventMappers/EventMapper.ts @@ -6,11 +6,11 @@ import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; -import { DdSdk } from '../../sdk/DdSdk'; import { AccountInfoSingleton } from '../AccountInfoSingleton/AccountInfoSingleton'; import type { AccountInfo } from '../AccountInfoSingleton/types'; import { AttributesSingleton } from '../AttributesSingleton/AttributesSingleton'; import type { Attributes } from '../AttributesSingleton/types'; +import { NativeDdSdk } from '../DdSdkInternal'; import { UserInfoSingleton } from '../UserInfoSingleton/UserInfoSingleton'; import type { UserInfo } from '../UserInfoSingleton/types'; @@ -92,7 +92,7 @@ export class EventMapper { )}: ${error}`, SdkVerbosity.WARN ); - DdSdk.telemetryDebug('Error while running the event mapper'); + NativeDdSdk.telemetryDebug('Error while running the event mapper'); return this.formatMapperEventForNative(backupEvent, backupEvent); } }; diff --git a/packages/core/src/trace/DdTrace.ts b/packages/core/src/trace/DdTrace.ts index 119716914..8c57a6ed2 100644 --- a/packages/core/src/trace/DdTrace.ts +++ b/packages/core/src/trace/DdTrace.ts @@ -7,12 +7,12 @@ import { InternalLog } from '../InternalLog'; import { SdkVerbosity } from '../SdkVerbosity'; import type { DdNativeTraceType } from '../nativeModulesTypes'; +import { encodeAttributes } from '../sdk/AttributesEncoding/attributesEncoding'; import { bufferNativeCallReturningId, bufferNativeCallWithId } from '../sdk/DatadogProvider/Buffer/bufferNativeCall'; import type { DdTraceType } from '../types'; -import { validateContext } from '../utils/argsUtils'; import { getGlobalInstance } from '../utils/singletonUtils'; import { DefaultTimeProvider } from '../utils/time-provider/DefaultTimeProvider'; @@ -33,7 +33,7 @@ class DdTraceWrapper implements DdTraceType { const spanId = bufferNativeCallReturningId(() => this.nativeTrace.startSpan( operation, - validateContext(context), + encodeAttributes(context), timestampMs ) ); @@ -54,7 +54,7 @@ class DdTraceWrapper implements DdTraceType { id => this.nativeTrace.finishSpan( id, - validateContext(context), + encodeAttributes(context), timestampMs ), spanId diff --git a/packages/core/src/types.tsx b/packages/core/src/types.tsx index cd697c04b..6865e2a41 100644 --- a/packages/core/src/types.tsx +++ b/packages/core/src/types.tsx @@ -5,6 +5,7 @@ */ import type { BatchProcessingLevel } from './DdSdkReactNativeConfiguration'; +import type { AttributeEncoder } from './sdk/AttributesEncoding/types'; declare global { // eslint-disable-next-line no-var, vars-on-top @@ -70,7 +71,8 @@ export class DdSdkConfiguration { readonly trackWatchdogTerminations: boolean | undefined, readonly batchProcessingLevel: BatchProcessingLevel, // eslint-disable-next-line no-empty-function readonly initialResourceThreshold: number | undefined, - readonly trackMemoryWarnings: boolean + readonly trackMemoryWarnings: boolean, + readonly attributeEncoders: AttributeEncoder[] ) {} } @@ -224,7 +226,6 @@ export type LogEvent = { export type LogEventMapper = (logEvent: LogEvent) => LogEvent | null; // DdRum - export enum ErrorSource { NETWORK = 'NETWORK', SOURCE = 'SOURCE', diff --git a/packages/react-native-apollo-client/src/helpers.ts b/packages/react-native-apollo-client/src/helpers.ts index c7f9a435a..9812bd547 100644 --- a/packages/react-native-apollo-client/src/helpers.ts +++ b/packages/react-native-apollo-client/src/helpers.ts @@ -22,7 +22,7 @@ export const getVariables = (operation: Operation): string | null => { try { return JSON.stringify(operation.variables); } catch (e) { - DdSdk?.telemetryError( + (DdSdk as any)?.telemetryError( _getErrorMessage( ErrorCode.GQL_VARIABLE_RETRIEVAL_ERROR, apolloVersion @@ -61,7 +61,7 @@ export const getOperationType = ( })[0] || null ); } catch (e) { - DdSdk?.telemetryError( + (DdSdk as any)?.telemetryError( _getErrorMessage(ErrorCode.GQL_OPERATION_TYPE_ERROR, apolloVersion), _getErrorStack(e), ErrorCode.GQL_OPERATION_TYPE_ERROR From 6d4a687be9f19705671917bb1bea8de2f5abb1ce Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Fri, 10 Oct 2025 11:32:05 +0200 Subject: [PATCH 077/168] Fixed existing tests --- .../src/__tests__/DdSdkReactNative.test.tsx | 65 ++-- .../DdSdkReactNativeConfiguration.test.ts | 3 + .../DdRumErrorTracking.test.tsx | 132 ++++++++- .../DdRumUserInteractionTracking.test.tsx | 17 +- .../core/src/logs/__tests__/DdLogs.test.ts | 124 +++----- packages/core/src/rum/__tests__/DdRum.test.ts | 68 ++--- .../__tests__/__utils__/XMLHttpRequestMock.ts | 67 +++-- .../XHRProxy/__tests__/XHRProxy.test.ts | 277 +++++++++++------- .../__tests__/DdSdkNativeBridge.test.tsx | 2 +- .../Buffer/__tests__/BoundedBuffer.test.ts | 10 +- .../__tests__/initialization.test.tsx | 1 + .../__tests__/EventMapper.test.ts | 6 +- .../__tests__/FileBasedConfiguration.test.ts | 2 + .../core/src/trace/__tests__/DdTrace.test.ts | 13 +- .../src/utils/__tests__/argsUtils.test.ts | 66 ----- .../src/utils/__tests__/errorUtils.test.ts | 2 +- 16 files changed, 468 insertions(+), 387 deletions(-) delete mode 100644 packages/core/src/utils/__tests__/argsUtils.test.ts diff --git a/packages/core/src/__tests__/DdSdkReactNative.test.tsx b/packages/core/src/__tests__/DdSdkReactNative.test.tsx index 5e6f8c447..b6ecfdf7a 100644 --- a/packages/core/src/__tests__/DdSdkReactNative.test.tsx +++ b/packages/core/src/__tests__/DdSdkReactNative.test.tsx @@ -19,7 +19,7 @@ import { DdRumUserInteractionTracking } from '../rum/instrumentation/interaction import { DdRumResourceTracking } from '../rum/instrumentation/resourceTracking/DdRumResourceTracking'; import { PropagatorType, RumActionType } from '../rum/types'; import { AttributesSingleton } from '../sdk/AttributesSingleton/AttributesSingleton'; -import { DdSdk } from '../sdk/DdSdk'; +import { NativeDdSdk } from '../sdk/DdSdkInternal'; import { GlobalState } from '../sdk/GlobalState/GlobalState'; import { UserInfoSingleton } from '../sdk/UserInfoSingleton/UserInfoSingleton'; import { ErrorSource } from '../types'; @@ -428,7 +428,9 @@ describe('DdSdkReactNative', () => { const ddSdkConfiguration = NativeModules.DdSdk.initialize.mock .calls[0][0] as DdSdkConfiguration; expect( - ddSdkConfiguration.additionalConfiguration['_dd.version'] + (ddSdkConfiguration.additionalConfiguration as { + '_dd.version': string; + })['_dd.version'] ).toBe('2.0.0'); }); @@ -451,10 +453,14 @@ describe('DdSdkReactNative', () => { const ddSdkConfiguration = NativeModules.DdSdk.initialize.mock .calls[0][0] as DdSdkConfiguration; expect( - ddSdkConfiguration.additionalConfiguration['_dd.version'] + (ddSdkConfiguration.additionalConfiguration as { + '_dd.version': string; + })['_dd.version'] ).toBeUndefined(); expect( - ddSdkConfiguration.additionalConfiguration['_dd.version_suffix'] + (ddSdkConfiguration.additionalConfiguration as { + '_dd.version_suffix': string; + })['_dd.version_suffix'] ).toBe('-codepush-3'); }); @@ -478,10 +484,14 @@ describe('DdSdkReactNative', () => { const ddSdkConfiguration = NativeModules.DdSdk.initialize.mock .calls[0][0] as DdSdkConfiguration; expect( - ddSdkConfiguration.additionalConfiguration['_dd.version'] + (ddSdkConfiguration.additionalConfiguration as { + '_dd.version': string; + })['_dd.version'] ).toBe('2.0.0-codepush-3'); expect( - ddSdkConfiguration.additionalConfiguration['_dd.version_suffix'] + (ddSdkConfiguration.additionalConfiguration as { + '_dd.version_suffix': string; + })['_dd.version_suffix'] ).toBeUndefined(); }); @@ -1056,8 +1066,10 @@ describe('DdSdkReactNative', () => { await DdSdkReactNative.addAttribute(key, value); // THEN - expect(DdSdk.addAttribute).toHaveBeenCalledTimes(1); - expect(DdSdk.addAttribute).toHaveBeenCalledWith(key, { value }); + expect(NativeDdSdk.addAttribute).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.addAttribute).toHaveBeenCalledWith(key, { + value + }); expect(AttributesSingleton.getInstance().getAttribute(key)).toEqual( value ); @@ -1075,8 +1087,8 @@ describe('DdSdkReactNative', () => { await DdSdkReactNative.removeAttribute(key); // THEN - expect(DdSdk.removeAttribute).toHaveBeenCalledTimes(1); - expect(DdSdk.removeAttribute).toHaveBeenCalledWith(key); + expect(NativeDdSdk.removeAttribute).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.removeAttribute).toHaveBeenCalledWith(key); expect(AttributesSingleton.getInstance().getAttribute(key)).toEqual( undefined ); @@ -1093,8 +1105,8 @@ describe('DdSdkReactNative', () => { await DdSdkReactNative.addAttributes(attributes); // THEN - expect(DdSdk.addAttributes).toHaveBeenCalledTimes(1); - expect(DdSdk.addAttributes).toHaveBeenCalledWith(attributes); + expect(NativeDdSdk.addAttributes).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.addAttributes).toHaveBeenCalledWith(attributes); expect(AttributesSingleton.getInstance().getAttributes()).toEqual({ foo: 'bar' }); @@ -1111,8 +1123,11 @@ describe('DdSdkReactNative', () => { await DdSdkReactNative.removeAttributes(['foo', 'baz']); // THEN - expect(DdSdk.removeAttributes).toHaveBeenCalledTimes(1); - expect(DdSdk.removeAttributes).toHaveBeenCalledWith(['foo', 'baz']); + expect(NativeDdSdk.removeAttributes).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.removeAttributes).toHaveBeenCalledWith([ + 'foo', + 'baz' + ]); expect(AttributesSingleton.getInstance().getAttributes()).toEqual( {} ); @@ -1135,8 +1150,8 @@ describe('DdSdkReactNative', () => { await DdSdkReactNative.setUserInfo(userInfo); // THEN - expect(DdSdk.setUserInfo).toHaveBeenCalledTimes(1); - expect(DdSdk.setUserInfo).toHaveBeenCalledWith(userInfo); + expect(NativeDdSdk.setUserInfo).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.setUserInfo).toHaveBeenCalledWith(userInfo); expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual( userInfo ); @@ -1156,8 +1171,10 @@ describe('DdSdkReactNative', () => { await DdSdkReactNative.addUserExtraInfo(extraInfo); // THEN - expect(DdSdk.addUserExtraInfo).toHaveBeenCalledTimes(1); - expect(DdSdk.addUserExtraInfo).toHaveBeenCalledWith(extraInfo); + expect(NativeDdSdk.addUserExtraInfo).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.addUserExtraInfo).toHaveBeenCalledWith( + extraInfo + ); expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual({ id: 'id', extraInfo: { @@ -1186,8 +1203,8 @@ describe('DdSdkReactNative', () => { await DdSdkReactNative.clearUserInfo(); // THEN - expect(DdSdk.clearUserInfo).toHaveBeenCalledTimes(1); - expect(DdSdk.setUserInfo).toHaveBeenCalled(); + expect(NativeDdSdk.clearUserInfo).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.setUserInfo).toHaveBeenCalled(); expect(UserInfoSingleton.getInstance().getUserInfo()).toEqual( undefined ); @@ -1204,8 +1221,10 @@ describe('DdSdkReactNative', () => { DdSdkReactNative.setTrackingConsent(consent); // THEN - expect(DdSdk.setTrackingConsent).toHaveBeenCalledTimes(1); - expect(DdSdk.setTrackingConsent).toHaveBeenCalledWith(consent); + expect(NativeDdSdk.setTrackingConsent).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.setTrackingConsent).toHaveBeenCalledWith( + consent + ); }); }); @@ -1215,7 +1234,7 @@ describe('DdSdkReactNative', () => { DdSdkReactNative.clearAllData(); // THEN - expect(DdSdk.clearAllData).toHaveBeenCalledTimes(1); + expect(NativeDdSdk.clearAllData).toHaveBeenCalledTimes(1); }); }); diff --git a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts index 60a9d13ff..d13b00df3 100644 --- a/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts +++ b/packages/core/src/__tests__/DdSdkReactNativeConfiguration.test.ts @@ -34,6 +34,7 @@ describe('DdSdkReactNativeConfiguration', () => { "actionEventMapper": null, "additionalConfiguration": {}, "applicationId": "fake-app-id", + "attributeEncoders": [], "batchProcessingLevel": "MEDIUM", "batchSize": "MEDIUM", "bundleLogsWithRum": true, @@ -134,6 +135,7 @@ describe('DdSdkReactNativeConfiguration', () => { "additionalField": "fake-value", }, "applicationId": "fake-app-id", + "attributeEncoders": [], "batchProcessingLevel": "MEDIUM", "batchSize": "LARGE", "bundleLogsWithRum": true, @@ -222,6 +224,7 @@ describe('DdSdkReactNativeConfiguration', () => { "actionEventMapper": null, "additionalConfiguration": {}, "applicationId": "", + "attributeEncoders": [], "batchProcessingLevel": "MEDIUM", "batchSize": "MEDIUM", "bundleLogsWithRum": false, diff --git a/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx b/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx index fc34de5b0..cfa6b9014 100644 --- a/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx +++ b/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx @@ -18,13 +18,13 @@ let baseErrorHandlerCalled = false; const baseErrorHandler = (error: any, isFatal?: boolean) => { baseErrorHandlerCalled = true; }; -let originalErrorHandler; +let originalErrorHandler: any; let baseConsoleErrorCalled = false; -const baseConsoleError = (...params: unknown) => { +const baseConsoleError = (...params: unknown[]) => { baseConsoleErrorCalled = true; }; -let originalConsoleError; +let originalConsoleError: any; const flushPromises = () => new Promise(jest.requireActual('timers').setImmediate); @@ -61,11 +61,14 @@ it('M intercept and send a RUM event W onGlobalError() {no message}', async () = // THEN expect(DdRum.addError).toHaveBeenCalledTimes(1); expect(DdRum.addError).toHaveBeenCalledWith( - '[object Object]', + 'Unknown Error', 'SOURCE', 'doSomething() at ./path/to/file.js:67:3', { - '_dd.error.raw': error, + '_dd.error.raw.name': 'Error', + '_dd.error.raw.message': 'Unknown Error', + '_dd.error.raw.cause': undefined, + '_dd.error.raw.stack': 'doSomething() at ./path/to/file.js:67:3', '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -94,7 +97,10 @@ it('M intercept and send a RUM event W onGlobalError() {empty stack trace}', asy 'SOURCE', '', { - '_dd.error.raw': error, + '_dd.error.raw.name': 'Error', + '_dd.error.raw.message': 'Something bad happened', + '_dd.error.raw.cause': undefined, + '_dd.error.raw.stack': '', '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -121,7 +127,10 @@ it('M intercept and send a RUM event W onGlobalError() {Error object}', async () 'SOURCE', expect.stringContaining('Error: Something bad happened'), { - '_dd.error.raw': error, + '_dd.error.raw.name': error.name, + '_dd.error.raw.message': error.message, + '_dd.error.raw.stack': error.stack, + '_dd.error.raw.cause': undefined, '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -155,7 +164,10 @@ it('M intercept and send a RUM event W onGlobalError() {CustomError object}', as 'SOURCE', expect.stringContaining('Error: Something bad happened'), { - '_dd.error.raw': error, + '_dd.error.raw.name': error.name, + '_dd.error.raw.message': error.message, + '_dd.error.raw.stack': error.stack, + '_dd.error.raw.cause': undefined, '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -190,9 +202,15 @@ it('M intercept and send a RUM event W onGlobalError() {with source file info}', 'SOURCE', 'at ./path/to/file.js:1038:57', { - '_dd.error.raw': error, '_dd.error.is_crash': is_fatal, - '_dd.error.source_type': 'react-native' + '_dd.error.source_type': 'react-native', + '_dd.error.raw.sourceURL': './path/to/file.js', + '_dd.error.raw.line': 1038, + '_dd.error.raw.column': 57, + '_dd.error.raw.message': 'Something bad happened', + '_dd.error.raw.name': 'Error', + '_dd.error.raw.cause': undefined, + '_dd.error.raw.stack': 'at ./path/to/file.js:1038:57' }, expect.any(Number), '' @@ -224,7 +242,63 @@ it('M intercept and send a RUM event W onGlobalError() {with component stack}', 'SOURCE', 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', { - '_dd.error.raw': error, + '_dd.error.raw.message': 'Something bad happened', + '_dd.error.raw.name': 'Error', + '_dd.error.raw.stack': [ + 'doSomething() at ./path/to/file.js:67:3', + 'nestedCall() at ./path/to/file.js:1064:9', + 'root() at ./path/to/index.js:10:1' + ].join(','), + '_dd.error.raw.cause': undefined, + '_dd.error.is_crash': is_fatal, + '_dd.error.source_type': 'react-native' + }, + expect.any(Number), + '' + ); + expect(baseErrorHandlerCalled).toStrictEqual(true); +}); + +it('M intercept and send a RUM event W onGlobalError() {with stack and component stack}', async () => { + // GIVEN + DdRumErrorTracking.startTracking(); + const is_fatal = Math.random() < 0.5; + const error = { + stack: [ + 'example() at ./path/to/file.js:77:2', + 'test() at ./path/to/index.js:22:3' + ], + componentStack: [ + 'doSomething() at ./path/to/file.js:67:3', + 'nestedCall() at ./path/to/file.js:1064:9', + 'root() at ./path/to/index.js:10:1' + ], + message: 'Something bad happened' + }; + + // WHEN + DdRumErrorTracking.onGlobalError(error, is_fatal); + await flushPromises(); + + // THEN + expect(DdRum.addError).toHaveBeenCalledTimes(1); + expect(DdRum.addError).toHaveBeenCalledWith( + 'Something bad happened', + 'SOURCE', + 'example() at ./path/to/file.js:77:2,test() at ./path/to/index.js:22:3', + { + '_dd.error.raw.message': 'Something bad happened', + '_dd.error.raw.name': 'Error', + '_dd.error.raw.stack': [ + 'example() at ./path/to/file.js:77:2', + 'test() at ./path/to/index.js:22:3' + ].join(','), + '_dd.error.raw.componentStack': [ + 'doSomething() at ./path/to/file.js:67:3', + 'nestedCall() at ./path/to/file.js:1064:9', + 'root() at ./path/to/index.js:10:1' + ], + '_dd.error.raw.cause': undefined, '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -258,7 +332,14 @@ it('M intercept and send a RUM event W onGlobalError() {with stack}', async () = 'SOURCE', 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', { - '_dd.error.raw': error, + '_dd.error.raw.name': 'Error', + '_dd.error.raw.message': 'Something bad happened', + '_dd.error.raw.cause': undefined, + '_dd.error.raw.stack': [ + 'doSomething() at ./path/to/file.js:67:3', + 'nestedCall() at ./path/to/file.js:1064:9', + 'root() at ./path/to/index.js:10:1' + ].join(','), '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -292,7 +373,14 @@ it('M intercept and send a RUM event W onGlobalError() {with stacktrace}', async 'SOURCE', 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', { - '_dd.error.raw': error, + '_dd.error.raw.name': 'Error', + '_dd.error.raw.message': 'Something bad happened', + '_dd.error.raw.stack': [ + 'doSomething() at ./path/to/file.js:67:3', + 'nestedCall() at ./path/to/file.js:1064:9', + 'root() at ./path/to/index.js:10:1' + ].join(','), + '_dd.error.raw.cause': undefined, '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -332,7 +420,14 @@ it('M not report error in console handler W onGlobalError() {with console report 'SOURCE', 'doSomething() at ./path/to/file.js:67:3,nestedCall() at ./path/to/file.js:1064:9,root() at ./path/to/index.js:10:1', { - '_dd.error.raw': error, + '_dd.error.raw.name': 'Error', + '_dd.error.raw.cause': undefined, + '_dd.error.raw.message': 'Something bad happened', + '_dd.error.raw.stack': [ + 'doSomething() at ./path/to/file.js:67:3', + 'nestedCall() at ./path/to/file.js:1064:9', + 'root() at ./path/to/index.js:10:1' + ].join(','), '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, @@ -483,7 +578,10 @@ describe.each([ const errorMessage = message === undefined || message === null ? 'Unknown Error' - : String(message); + : typeof message?.toString === 'function' && + message.toString !== Object.prototype.toString + ? String(message) + : 'Unknown Error'; expect(DdRum.addError).toHaveBeenCalledTimes(1); expect(DdRum.addError).toHaveBeenCalledWith( errorMessage, @@ -517,7 +615,9 @@ it('M intercept and send a RUM event W on error() {called from RNErrorHandler}', 'SOURCE', expect.stringContaining('Error: Something bad happened'), { - '_dd.error.raw': error, + '_dd.error.raw.name': error.name, + '_dd.error.raw.message': error.message, + '_dd.error.raw.stack': error.stack, '_dd.error.is_crash': is_fatal, '_dd.error.source_type': 'react-native' }, diff --git a/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx b/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx index d94d9bb82..6ee9e9c71 100644 --- a/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx +++ b/packages/core/src/__tests__/rum/instrumentation/DdRumUserInteractionTracking.test.tsx @@ -21,7 +21,7 @@ import React from 'react'; import type { DdNativeRumType } from '../../../nativeModulesTypes'; import { DdRumUserInteractionTracking } from '../../../rum/instrumentation/interactionTracking/DdRumUserInteractionTracking'; import { BufferSingleton } from '../../../sdk/DatadogProvider/Buffer/BufferSingleton'; -import { DdSdk } from '../../../sdk/DdSdk'; +import { NativeDdSdk } from '../../../sdk/DdSdkInternal'; const styles = StyleSheet.create({ button: { @@ -311,7 +311,7 @@ describe('startTracking memoization', () => { // GIVEN DdRumUserInteractionTracking.startTracking({}); let rendersCount = 0; - const DummyComponent = props => { + const DummyComponent = (props: { onPress: () => void }) => { rendersCount++; return ( @@ -343,7 +343,7 @@ describe('startTracking memoization', () => { // GIVEN DdRumUserInteractionTracking.startTracking({}); let rendersCount = 0; - const DummyComponent = props => { + const DummyComponent = (props: { title: string }) => { rendersCount++; return ( @@ -370,7 +370,10 @@ describe('startTracking memoization', () => { // GIVEN DdRumUserInteractionTracking.startTracking({}); let rendersCount = 0; - const DummyComponent = props => { + const DummyComponent = (props: { + onPress: () => void; + title: string; + }) => { rendersCount++; return ( @@ -410,7 +413,7 @@ describe('startTracking memoization', () => { // GIVEN DdRumUserInteractionTracking.startTracking({}); let rendersCount = 0; - const DummyComponent = props => { + const DummyComponent = (props: { onPress: () => void }) => { rendersCount++; return ( @@ -456,7 +459,7 @@ describe('startTracking', () => { jest.setMock('react/jsx-runtime', {}); DdRumUserInteractionTracking.startTracking({}); expect(DdRumUserInteractionTracking['isTracking']).toBe(true); - expect(DdSdk.telemetryDebug).toBeCalledWith( + expect(NativeDdSdk.telemetryDebug).toBeCalledWith( 'React jsx runtime does not export new jsx transform' ); }); @@ -466,7 +469,7 @@ describe('startTracking', () => { DdRumUserInteractionTracking.startTracking({}); expect(DdRumUserInteractionTracking['isTracking']).toBe(true); - expect(DdSdk.telemetryDebug).toBeCalledWith( + expect(NativeDdSdk.telemetryDebug).toBeCalledWith( 'React version does not support new jsx transform' ); }); diff --git a/packages/core/src/logs/__tests__/DdLogs.test.ts b/packages/core/src/logs/__tests__/DdLogs.test.ts index 2dd15f46a..bfd252d82 100644 --- a/packages/core/src/logs/__tests__/DdLogs.test.ts +++ b/packages/core/src/logs/__tests__/DdLogs.test.ts @@ -12,7 +12,7 @@ import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; import type { DdNativeLogsType } from '../../nativeModulesTypes'; import { ErrorSource } from '../../types'; -import type { LogEventMapper } from '../../types'; +import type { LogEventMapper, LogEvent } from '../../types'; import { DdLogs } from '../DdLogs'; jest.mock('../../InternalLog', () => { @@ -38,7 +38,7 @@ describe('DdLogs', () => { context: { newContext: 'context' }, status: 'info', userInfo: {} - }; + } as LogEvent; }; DdLogs.registerLogEventMapper(logEventMapper); @@ -506,7 +506,7 @@ describe('DdLogs', () => { it('native context is an object with nested property W context is an array', async () => { await DdLogs.debug('message', [1, 2, 3]); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), SdkVerbosity.WARN ); @@ -516,12 +516,12 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.debug('message', obj); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdLogs.debug).toHaveBeenCalledWith( 'message', @@ -549,7 +549,7 @@ describe('DdLogs', () => { it('native context is an object with nested property W context is an array', async () => { await DdLogs.warn('message', [1, 2, 3]); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), SdkVerbosity.WARN ); @@ -559,12 +559,12 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.warn('message', obj); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdLogs.warn).toHaveBeenCalledWith( 'message', @@ -592,7 +592,7 @@ describe('DdLogs', () => { it('native context is an object with nested property W context is an array', async () => { await DdLogs.info('message', [1, 2, 3]); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), SdkVerbosity.WARN ); @@ -602,12 +602,12 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.info('message', obj); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdLogs.info).toHaveBeenCalledWith( 'message', @@ -635,7 +635,7 @@ describe('DdLogs', () => { it('native context is an object with nested property W context is an array', async () => { await DdLogs.error('message', [1, 2, 3]); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), SdkVerbosity.WARN ); @@ -645,12 +645,12 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.error('message', obj); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdLogs.error).toHaveBeenCalledWith( 'message', @@ -700,17 +700,9 @@ describe('DdLogs', () => { 3 ]); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking debug log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining( - 'The given context is an array, it will be nested' - ), + expect.anything(), SdkVerbosity.WARN ); @@ -729,7 +721,7 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.debug( 'message', 'kind', @@ -738,16 +730,10 @@ describe('DdLogs', () => { obj ); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking debug log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining('Context will be empty.'), - SdkVerbosity.ERROR + expect.anything(), + SdkVerbosity.WARN ); expect( @@ -804,17 +790,9 @@ describe('DdLogs', () => { 3 ]); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking warn log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining( - 'The given context is an array, it will be nested' - ), + expect.anything(), SdkVerbosity.WARN ); @@ -831,7 +809,7 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.warn( 'message', 'kind', @@ -840,16 +818,10 @@ describe('DdLogs', () => { obj ); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking warn log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining('Context will be empty.'), - SdkVerbosity.ERROR + expect.anything(), + SdkVerbosity.WARN ); expect( @@ -906,17 +878,9 @@ describe('DdLogs', () => { 3 ]); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking info log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining( - 'The given context is an array, it will be nested' - ), + expect.anything(), SdkVerbosity.WARN ); @@ -933,7 +897,7 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.info( 'message', 'kind', @@ -942,16 +906,10 @@ describe('DdLogs', () => { obj ); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking info log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining('Context will be empty.'), - SdkVerbosity.ERROR + expect.anything(), + SdkVerbosity.WARN ); expect( @@ -1008,17 +966,9 @@ describe('DdLogs', () => { 3 ]); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking error log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining( - 'The given context is an array, it will be nested' - ), + expect.anything(), SdkVerbosity.WARN ); @@ -1037,7 +987,7 @@ describe('DdLogs', () => { }); it('native context is empty W context is raw type', async () => { - const obj: any = 123; + const obj: any = Symbol('invalid-context'); await DdLogs.error( 'message', 'kind', @@ -1046,16 +996,10 @@ describe('DdLogs', () => { obj ); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Tracking error log'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining('Context will be empty.'), - SdkVerbosity.ERROR + expect.anything(), + SdkVerbosity.WARN ); expect( diff --git a/packages/core/src/rum/__tests__/DdRum.test.ts b/packages/core/src/rum/__tests__/DdRum.test.ts index 8bdc86aac..e1c699fe1 100644 --- a/packages/core/src/rum/__tests__/DdRum.test.ts +++ b/packages/core/src/rum/__tests__/DdRum.test.ts @@ -10,7 +10,7 @@ import { NativeModules } from 'react-native'; import { InternalLog } from '../../InternalLog'; import { SdkVerbosity } from '../../SdkVerbosity'; import { BufferSingleton } from '../../sdk/DatadogProvider/Buffer/BufferSingleton'; -import { DdSdk } from '../../sdk/DdSdk'; +import { NativeDdSdk } from '../../sdk/DdSdkInternal'; import { GlobalState } from '../../sdk/GlobalState/GlobalState'; import { ErrorSource } from '../../types'; import { DdRum } from '../DdRum'; @@ -69,13 +69,13 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.startView('key', 'name', context); expect(InternalLog.log).toHaveBeenNthCalledWith( 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdRum.startView).toHaveBeenCalledWith( @@ -122,7 +122,7 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.startView('key', 'name'); await DdRum.stopView('key', context); @@ -130,7 +130,7 @@ describe('DdRum', () => { expect(InternalLog.log).toHaveBeenNthCalledWith( 3, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdRum.stopView).toHaveBeenCalledWith( @@ -177,13 +177,13 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.startAction(RumActionType.SCROLL, 'name', context); expect(InternalLog.log).toHaveBeenNthCalledWith( 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdRum.startAction).toHaveBeenCalledWith( @@ -236,7 +236,7 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.startAction(RumActionType.SCROLL, 'name'); await DdRum.stopAction( @@ -248,7 +248,7 @@ describe('DdRum', () => { expect(InternalLog.log).toHaveBeenNthCalledWith( 3, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdRum.stopAction).toHaveBeenCalledWith( @@ -353,14 +353,14 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.startResource('key', 'method', 'url', context); expect(InternalLog.log).toHaveBeenNthCalledWith( - 2, + 3, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdRum.startResource).toHaveBeenCalledWith( @@ -414,15 +414,15 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.startResource('key', 'method', 'url', {}); await DdRum.stopResource('key', 200, 'other', -1, context); expect(InternalLog.log).toHaveBeenNthCalledWith( - 2, + 3, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdRum.stopResource).toHaveBeenCalledWith( @@ -442,7 +442,7 @@ describe('DdRum', () => { await DdRum.stopResource('key', 200, 'other', -1, context); expect(InternalLog.log).toHaveBeenNthCalledWith( - 2, + 3, expect.anything(), SdkVerbosity.WARN ); @@ -1200,13 +1200,13 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.addAction(RumActionType.SCROLL, 'name', context); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdRum.addAction).toHaveBeenCalledWith( @@ -1222,7 +1222,7 @@ describe('DdRum', () => { await DdRum.addAction(RumActionType.SCROLL, 'name', context); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), SdkVerbosity.WARN ); @@ -1264,7 +1264,7 @@ describe('DdRum', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdRum.addError( 'error', ErrorSource.CUSTOM, @@ -1272,16 +1272,10 @@ describe('DdRum', () => { context ); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Adding RUM Error'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining('Context will be empty'), - SdkVerbosity.ERROR + expect.anything(), + SdkVerbosity.WARN ); expect(NativeModules.DdRum.addError).toHaveBeenCalledWith( @@ -1305,17 +1299,9 @@ describe('DdRum', () => { context ); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.stringContaining('Adding RUM Error'), - SdkVerbosity.DEBUG - ); - expect(InternalLog.log).toHaveBeenNthCalledWith( 2, - expect.stringContaining( - 'The given context is an array, it will be nested' - ), + expect.anything(), SdkVerbosity.WARN ); @@ -1363,7 +1349,7 @@ describe('DdRum', () => { test('does not call the native SDK when startAction has not been called before and using old API', async () => { await DdRum.stopAction({ user: 'me' }, 789); expect(NativeModules.DdRum.stopAction).not.toHaveBeenCalled(); - expect(DdSdk.telemetryDebug).not.toHaveBeenCalled(); + expect(NativeDdSdk.telemetryDebug).not.toHaveBeenCalled(); }); test('calls the native SDK when called with old API', async () => { @@ -1375,7 +1361,7 @@ describe('DdRum', () => { { user: 'me' }, 789 ); - expect(DdSdk.telemetryDebug).toHaveBeenCalledWith( + expect(NativeDdSdk.telemetryDebug).toHaveBeenCalledWith( 'DDdRum.stopAction called with the old signature' ); }); @@ -1389,7 +1375,7 @@ describe('DdRum', () => { {}, 456 ); - expect(DdSdk.telemetryDebug).toHaveBeenCalledWith( + expect(NativeDdSdk.telemetryDebug).toHaveBeenCalledWith( 'DDdRum.stopAction called with the old signature' ); }); diff --git a/packages/core/src/rum/instrumentation/resourceTracking/__tests__/__utils__/XMLHttpRequestMock.ts b/packages/core/src/rum/instrumentation/resourceTracking/__tests__/__utils__/XMLHttpRequestMock.ts index a5725fbf4..7683b63f9 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/__tests__/__utils__/XMLHttpRequestMock.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/__tests__/__utils__/XMLHttpRequestMock.ts @@ -12,7 +12,7 @@ export class XMLHttpRequestMock implements XMLHttpRequest { static readonly DONE = 4; public response: any; - public responseType: XMLHttpRequestResponseType; + public responseType: XMLHttpRequestResponseType = ''; public status: number = 0; public readyState: number = XMLHttpRequestMock.UNSENT; public requestHeaders: Map = new Map(); @@ -20,29 +20,50 @@ export class XMLHttpRequestMock implements XMLHttpRequest { // eslint-disable-next-line no-empty-function constructor() {} - responseText: string; - responseURL: string; - responseXML: Document; - statusText: string; - timeout: number; - upload: XMLHttpRequestUpload; - withCredentials: boolean; + responseText: string = ''; + responseURL: string = ''; + responseXML: Document = {} as Document; + statusText: string = ''; + timeout: number = -1; + upload: XMLHttpRequestUpload = {} as XMLHttpRequestUpload; + withCredentials: boolean = false; getAllResponseHeaders = jest.fn(); overrideMimeType = jest.fn(); - DONE: number; - HEADERS_RECEIVED: number; - LOADING: number; - OPENED: number; - UNSENT: number; + DONE = 4 as const; + HEADERS_RECEIVED = 2 as const; + LOADING = 3 as const; + OPENED = 1 as const; + UNSENT = 0 as const; addEventListener = jest.fn(); removeEventListener = jest.fn(); - onabort: (this: XMLHttpRequest, ev: ProgressEvent) => any; - onerror: (this: XMLHttpRequest, ev: ProgressEvent) => any; - onload: (this: XMLHttpRequest, ev: ProgressEvent) => any; - onloadend: (this: XMLHttpRequest, ev: ProgressEvent) => any; - onloadstart: (this: XMLHttpRequest, ev: ProgressEvent) => any; - onprogress: (this: XMLHttpRequest, ev: ProgressEvent) => any; - ontimeout: (this: XMLHttpRequest, ev: ProgressEvent) => any; + onabort: ( + this: XMLHttpRequest, + ev: ProgressEvent + ) => any = ev => {}; + onerror: ( + this: XMLHttpRequest, + ev: ProgressEvent + ) => any = ev => {}; + onload: ( + this: XMLHttpRequest, + ev: ProgressEvent + ) => any = ev => {}; + onloadend: ( + this: XMLHttpRequest, + ev: ProgressEvent + ) => any = ev => {}; + onloadstart: ( + this: XMLHttpRequest, + ev: ProgressEvent + ) => any = ev => {}; + onprogress: ( + this: XMLHttpRequest, + ev: ProgressEvent + ) => any = ev => {}; + ontimeout: ( + this: XMLHttpRequest, + ev: ProgressEvent + ) => any = ev => {}; dispatchEvent(event: Event): boolean { throw new Error('Method not implemented.'); } @@ -85,14 +106,14 @@ export class XMLHttpRequestMock implements XMLHttpRequest { } setRequestHeader(header: string, value: string): void { - this.requestHeaders[header] = value; + this.requestHeaders.set(header, value); } setResponseHeader(header: string, value: string): void { - this.responseHeaders[header] = value; + this.responseHeaders.set(header, value); } getResponseHeader(header: string): string | null { - return this.responseHeaders[header]; + return this.responseHeaders.get(header) ?? null; } } diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index a18825771..6599dea08 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -62,7 +62,7 @@ function randomInt(max: number): number { const flushPromises = () => new Promise(jest.requireActual('timers').setImmediate); -let xhrProxy; +let xhrProxy: any; const hexToDecimal = (hex: string): string => { return BigInt(hex, 16).toString(10); @@ -76,6 +76,9 @@ beforeEach(() => { xhrProxy = new XHRProxy({ xhrType: XMLHttpRequestMock, resourceReporter: new ResourceReporter([]) + } as { + xhrType: typeof XMLHttpRequest; + resourceReporter: ResourceReporter; }); // we need this because with ms precision between Date.now() calls we can get 0, so we advance @@ -236,17 +239,19 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - const spanId = xhr.requestHeaders[PARENT_ID_HEADER_KEY]; + const spanId = xhr.requestHeaders.get(PARENT_ID_HEADER_KEY); expect(spanId).toBeDefined(); expect(spanId).toMatch(/[1-9].+/); - const traceId = xhr.requestHeaders[TRACE_ID_HEADER_KEY]; + const traceId = xhr.requestHeaders.get(TRACE_ID_HEADER_KEY); expect(traceId).toBeDefined(); expect(traceId).toMatch(/[1-9].+/); expect(traceId !== spanId).toBeTruthy(); - expect(xhr.requestHeaders[SAMPLING_PRIORITY_HEADER_KEY]).toBe('1'); - expect(xhr.requestHeaders[ORIGIN_HEADER_KEY]).toBe(ORIGIN_RUM); + expect(xhr.requestHeaders.get(SAMPLING_PRIORITY_HEADER_KEY)).toBe( + '1' + ); + expect(xhr.requestHeaders.get(ORIGIN_HEADER_KEY)).toBe(ORIGIN_RUM); }); it('does not generate spanId and traceId in request headers when no first party hosts are provided', async () => { @@ -267,8 +272,10 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[TRACE_ID_HEADER_KEY]).toBeUndefined(); - expect(xhr.requestHeaders[PARENT_ID_HEADER_KEY]).toBeUndefined(); + expect(xhr.requestHeaders.get(TRACE_ID_HEADER_KEY)).toBeUndefined(); + expect( + xhr.requestHeaders.get(PARENT_ID_HEADER_KEY) + ).toBeUndefined(); }); it('does not generate spanId and traceId in request headers when the url does not match first party hosts', async () => { @@ -298,8 +305,10 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[TRACE_ID_HEADER_KEY]).toBeUndefined(); - expect(xhr.requestHeaders[PARENT_ID_HEADER_KEY]).toBeUndefined(); + expect(xhr.requestHeaders.get(TRACE_ID_HEADER_KEY)).toBeUndefined(); + expect( + xhr.requestHeaders.get(PARENT_ID_HEADER_KEY) + ).toBeUndefined(); }); it('does not crash when provided URL is not a valid one', async () => { @@ -325,8 +334,10 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[TRACE_ID_HEADER_KEY]).toBeUndefined(); - expect(xhr.requestHeaders[PARENT_ID_HEADER_KEY]).toBeUndefined(); + expect(xhr.requestHeaders.get(TRACE_ID_HEADER_KEY)).toBeUndefined(); + expect( + xhr.requestHeaders.get(PARENT_ID_HEADER_KEY) + ).toBeUndefined(); }); it('generates spanId and traceId with 0 sampling priority in request headers when trace is not sampled', async () => { @@ -352,12 +363,16 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[TRACE_ID_HEADER_KEY]).not.toBeUndefined(); expect( - xhr.requestHeaders[PARENT_ID_HEADER_KEY] + xhr.requestHeaders.get(TRACE_ID_HEADER_KEY) ).not.toBeUndefined(); - expect(xhr.requestHeaders[SAMPLING_PRIORITY_HEADER_KEY]).toBe('0'); - expect(xhr.requestHeaders[ORIGIN_HEADER_KEY]).toBe(ORIGIN_RUM); + expect( + xhr.requestHeaders.get(PARENT_ID_HEADER_KEY) + ).not.toBeUndefined(); + expect(xhr.requestHeaders.get(SAMPLING_PRIORITY_HEADER_KEY)).toBe( + '0' + ); + expect(xhr.requestHeaders.get(ORIGIN_HEADER_KEY)).toBe(ORIGIN_RUM); }); it('does not origin as RUM in the request headers when startTracking() + XHR.open() + XHR.send()', async () => { @@ -378,7 +393,7 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[ORIGIN_HEADER_KEY]).toBeUndefined(); + expect(xhr.requestHeaders.get(ORIGIN_HEADER_KEY)).toBeUndefined(); }); it('forces the agent to keep the request generated trace when startTracking() + XHR.open() + XHR.send()', async () => { @@ -404,7 +419,9 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[SAMPLING_PRIORITY_HEADER_KEY]).toBe('1'); + expect(xhr.requestHeaders.get(SAMPLING_PRIORITY_HEADER_KEY)).toBe( + '1' + ); }); it('forces the agent to discard the request generated trace when startTracking when the request is not traced', async () => { @@ -430,7 +447,9 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[SAMPLING_PRIORITY_HEADER_KEY]).toBe('0'); + expect(xhr.requestHeaders.get(SAMPLING_PRIORITY_HEADER_KEY)).toBe( + '0' + ); }); it('adds tracecontext request headers when the host is instrumented with tracecontext and request is sampled', async () => { @@ -460,14 +479,16 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - const contextHeader = xhr.requestHeaders[TRACECONTEXT_HEADER_KEY]; + const contextHeader = xhr.requestHeaders.get( + TRACECONTEXT_HEADER_KEY + ); expect(contextHeader).toMatch( /^00-[0-9a-f]{8}[0]{8}[0-9a-f]{16}-[0-9a-f]{16}-01$/ ); // Parent value of the context header is the 3rd part of it - const parentValue = contextHeader.split('-')[2]; - const stateHeader = xhr.requestHeaders[TRACESTATE_HEADER_KEY]; + const parentValue = contextHeader?.split('-')[2]; + const stateHeader = xhr.requestHeaders.get(TRACESTATE_HEADER_KEY); expect(stateHeader).toBe(`dd=s:1;o:rum;p:${parentValue}`); }); @@ -510,15 +531,19 @@ describe('XHRProxy', () => { /* ================================================================================= * Verify that the trace id in the traceparent header is a 128 bit trace ID (hex). * ================================================================================= */ - const traceparentHeader = - xhr.requestHeaders[TRACECONTEXT_HEADER_KEY]; - const traceparentTraceId = traceparentHeader.split('-')[1]; + const traceparentHeader = xhr.requestHeaders.get( + TRACECONTEXT_HEADER_KEY + ); + const traceparentTraceId = traceparentHeader?.split('-')[1]; expect(traceparentTraceId).toMatch( /^[0-9a-f]{8}[0]{8}[0-9a-f]{16}$/ ); expect( - TracingIdentifierUtils.isWithin128Bits(traceparentTraceId, 16) + TracingIdentifierUtils.isWithin128Bits( + traceparentTraceId as string, + 16 + ) ); /* ========================================================================= @@ -526,18 +551,20 @@ describe('XHRProxy', () => { * ========================================================================= */ // x-datadog-trace-id is a decimal representing the low 64 bits of the 128 bits Trace ID - const xDatadogTraceId = xhr.requestHeaders[TRACE_ID_HEADER_KEY]; + const xDatadogTraceId = xhr.requestHeaders.get(TRACE_ID_HEADER_KEY); - expect(TracingIdentifierUtils.isWithin64Bits(xDatadogTraceId)); + expect( + TracingIdentifierUtils.isWithin64Bits(xDatadogTraceId as string) + ); /* =============================================================== * Verify that the trace id in x-datadog-tags headers is HEX 16. * =============================================================== */ // x-datadog-tags is a HEX 16 contains the high 64 bits of the 128 bits Trace ID - const xDatadogTagsTraceId = xhr.requestHeaders[ - TAGS_HEADER_KEY - ].split('=')[1]; + const xDatadogTagsTraceId = xhr.requestHeaders + ?.get(TAGS_HEADER_KEY) + ?.split('=')[1] as string; expect(xDatadogTagsTraceId).toMatch(/^[a-f0-9]{16}$/); expect( @@ -548,8 +575,8 @@ describe('XHRProxy', () => { * Verify that the trace id in the b3 header is a 128 bit trace ID (hex). * ========================================================================= */ - const b3Header = xhr.requestHeaders[B3_HEADER_KEY]; - const b3TraceId = b3Header.split('-')[0]; + const b3Header = xhr.requestHeaders.get(B3_HEADER_KEY); + const b3TraceId = b3Header?.split('-')[0] as string; expect(b3TraceId).toMatch(/^[0-9a-f]{8}[0]{8}[0-9a-f]{16}$/); expect(TracingIdentifierUtils.isWithin128Bits(b3TraceId, 16)); @@ -558,7 +585,9 @@ describe('XHRProxy', () => { * Verify that the trace id in the X-B3-TraceId header is a 128 bit trace ID (hex). * ================================================================================= */ - const xB3TraceId = xhr.requestHeaders[B3_MULTI_TRACE_ID_HEADER_KEY]; + const xB3TraceId = xhr.requestHeaders.get( + B3_MULTI_TRACE_ID_HEADER_KEY + ) as string; expect(xB3TraceId).toMatch(/^[0-9a-f]{8}[0]{8}[0-9a-f]{16}$/); expect(TracingIdentifierUtils.isWithin128Bits(xB3TraceId, 16)); @@ -601,18 +630,21 @@ describe('XHRProxy', () => { // THEN // x-datadog-trace-id is just the low 64 bits (DECIMAL) - const datadogLowTraceValue = - xhr.requestHeaders[TRACE_ID_HEADER_KEY]; + const datadogLowTraceValue = xhr.requestHeaders.get( + TRACE_ID_HEADER_KEY + ); // We convert the low 64 bits to HEX - const datadogLowTraceValueHex = `${BigInt(datadogLowTraceValue) + const datadogLowTraceValueHex = `${BigInt( + datadogLowTraceValue as string + ) .toString(16) .padStart(16, '0')}`; // The high 64 bits are expressed in x-datadog-tags (HEX) - const datadogHighTraceValueHex = xhr.requestHeaders[ - TAGS_HEADER_KEY - ].split('=')[1]; // High HEX 64 bits + const datadogHighTraceValueHex = xhr.requestHeaders + ?.get(TAGS_HEADER_KEY) + ?.split('=')[1] as string; // High HEX 64 bits // We re-compose the full 128 bit trace-id by joining the strings const datadogTraceValue128BitHex = `${datadogHighTraceValueHex}${datadogLowTraceValueHex}`; @@ -622,18 +654,24 @@ describe('XHRProxy', () => { datadogTraceValue128BitHex ); - const datadogParentValue = xhr.requestHeaders[PARENT_ID_HEADER_KEY]; - const contextHeader = xhr.requestHeaders[TRACECONTEXT_HEADER_KEY]; - const traceContextValue = contextHeader.split('-')[1]; - const parentContextValue = contextHeader.split('-')[2]; - const b3MultiTraceHeader = - xhr.requestHeaders[B3_MULTI_TRACE_ID_HEADER_KEY]; - const b3MultiParentHeader = - xhr.requestHeaders[B3_MULTI_SPAN_ID_HEADER_KEY]; - - const b3Header = xhr.requestHeaders[B3_HEADER_KEY]; - const traceB3Value = b3Header.split('-')[0]; - const parentB3Value = b3Header.split('-')[1]; + const datadogParentValue = xhr.requestHeaders.get( + PARENT_ID_HEADER_KEY + ); + const contextHeader = xhr.requestHeaders.get( + TRACECONTEXT_HEADER_KEY + ); + const traceContextValue = contextHeader?.split('-')[1] as string; + const parentContextValue = contextHeader?.split('-')[2] as string; + const b3MultiTraceHeader = xhr.requestHeaders.get( + B3_MULTI_TRACE_ID_HEADER_KEY + ) as string; + const b3MultiParentHeader = xhr.requestHeaders.get( + B3_MULTI_SPAN_ID_HEADER_KEY + ) as string; + + const b3Header = xhr.requestHeaders.get(B3_HEADER_KEY); + const traceB3Value = b3Header?.split('-')[0] as string; + const parentB3Value = b3Header?.split('-')[1] as string; expect(hexToDecimal(traceContextValue)).toBe( datadogTraceValue128BitDec @@ -676,9 +714,11 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - const traceId = xhr.requestHeaders[B3_MULTI_TRACE_ID_HEADER_KEY]; - const spanId = xhr.requestHeaders[B3_MULTI_SPAN_ID_HEADER_KEY]; - const sampled = xhr.requestHeaders[B3_MULTI_SAMPLED_HEADER_KEY]; + const traceId = xhr.requestHeaders.get( + B3_MULTI_TRACE_ID_HEADER_KEY + ); + const spanId = xhr.requestHeaders.get(B3_MULTI_SPAN_ID_HEADER_KEY); + const sampled = xhr.requestHeaders.get(B3_MULTI_SAMPLED_HEADER_KEY); expect(traceId).toMatch(/^[0-9a-f]{8}[0]{8}[0-9a-f]{16}$/); expect(spanId).toMatch(/^[0-9a-f]{16}$/); expect(sampled).toBe('1'); @@ -711,7 +751,7 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - const headerValue = xhr.requestHeaders[B3_HEADER_KEY]; + const headerValue = xhr.requestHeaders.get(B3_HEADER_KEY); expect(headerValue).toMatch( /^[0-9a-f]{8}[0]{8}[0-9a-f]{16}-[0-9a-f]{16}-1$/ ); @@ -750,23 +790,29 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[B3_HEADER_KEY]).not.toBeUndefined(); + expect(xhr.requestHeaders.get(B3_HEADER_KEY)).not.toBeUndefined(); expect( - xhr.requestHeaders[B3_MULTI_TRACE_ID_HEADER_KEY] + xhr.requestHeaders.get(B3_MULTI_TRACE_ID_HEADER_KEY) ).not.toBeUndefined(); expect( - xhr.requestHeaders[B3_MULTI_SPAN_ID_HEADER_KEY] + xhr.requestHeaders.get(B3_MULTI_SPAN_ID_HEADER_KEY) + ).not.toBeUndefined(); + expect(xhr.requestHeaders.get(B3_MULTI_SAMPLED_HEADER_KEY)).toBe( + '1' + ); + expect( + xhr.requestHeaders.get(TRACECONTEXT_HEADER_KEY) ).not.toBeUndefined(); - expect(xhr.requestHeaders[B3_MULTI_SAMPLED_HEADER_KEY]).toBe('1'); expect( - xhr.requestHeaders[TRACECONTEXT_HEADER_KEY] + xhr.requestHeaders.get(TRACE_ID_HEADER_KEY) ).not.toBeUndefined(); - expect(xhr.requestHeaders[TRACE_ID_HEADER_KEY]).not.toBeUndefined(); expect( - xhr.requestHeaders[PARENT_ID_HEADER_KEY] + xhr.requestHeaders.get(PARENT_ID_HEADER_KEY) ).not.toBeUndefined(); - expect(xhr.requestHeaders[SAMPLING_PRIORITY_HEADER_KEY]).toBe('1'); - expect(xhr.requestHeaders[ORIGIN_HEADER_KEY]).toBe(ORIGIN_RUM); + expect(xhr.requestHeaders.get(SAMPLING_PRIORITY_HEADER_KEY)).toBe( + '1' + ); + expect(xhr.requestHeaders.get(ORIGIN_HEADER_KEY)).toBe(ORIGIN_RUM); }); it('adds rum session id to baggage headers when available', async () => { @@ -804,8 +850,10 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).not.toBeUndefined(); - expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).toBe( + expect( + xhr.requestHeaders.get(BAGGAGE_HEADER_KEY) + ).not.toBeUndefined(); + expect(xhr.requestHeaders.get(BAGGAGE_HEADER_KEY)).toBe( 'session.id=TEST-SESSION-ID' ); }); @@ -845,7 +893,7 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).toBeUndefined(); + expect(xhr.requestHeaders.get(BAGGAGE_HEADER_KEY)).toBeUndefined(); }); it('does not add rum session id to baggage headers when propagator type is not datadog or w3c', async () => { @@ -1078,27 +1126,39 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - const timings = - DdNativeRum.stopResource.mock.calls[0][4][ - '_dd.resource_timings' - ]; + const timings = DdNativeRum.stopResource.mock.calls[0][4]; if (Platform.OS === 'ios') { - expect(timings['firstByte']['startTime']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.firstByte.startTime'] + ).toBeGreaterThan(0); } else { - expect(timings['firstByte']['startTime']).toBe(0); + expect( + timings['_dd.resource_timings.firstByte.startTime'] + ).toBe(0); } - expect(timings['firstByte']['duration']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.firstByte.duration'] + ).toBeGreaterThan(0); - expect(timings['download']['startTime']).toBeGreaterThan(0); - expect(timings['download']['duration']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.download.startTime'] + ).toBeGreaterThan(0); + + expect( + timings['_dd.resource_timings.download.duration'] + ).toBeGreaterThan(0); if (Platform.OS === 'ios') { - expect(timings['fetch']['startTime']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.fetch.startTime'] + ).toBeGreaterThan(0); } else { - expect(timings['fetch']['startTime']).toBe(0); + expect(timings['_dd.resource_timings.fetch.startTime']).toBe(0); } - expect(timings['fetch']['duration']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.fetch.duration'] + ).toBeGreaterThan(0); }); it(`M generate resource timings when startTracking() + XHR.open() + XHR.send() + XHR.abort(), platform=${platform}`, async () => { @@ -1126,27 +1186,38 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - const timings = - DdNativeRum.stopResource.mock.calls[0][4][ - '_dd.resource_timings' - ]; + const timings = DdNativeRum.stopResource.mock.calls[0][4]; if (Platform.OS === 'ios') { - expect(timings['firstByte']['startTime']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.firstByte.startTime'] + ).toBeGreaterThan(0); } else { - expect(timings['firstByte']['startTime']).toBe(0); + expect( + timings['_dd.resource_timings.firstByte.startTime'] + ).toBe(0); } - expect(timings['firstByte']['duration']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.firstByte.duration'] + ).toBeGreaterThan(0); - expect(timings['download']['startTime']).toBeGreaterThan(0); - expect(timings['download']['duration']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.download.startTime'] + ).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.download.duration'] + ).toBeGreaterThan(0); if (Platform.OS === 'ios') { - expect(timings['fetch']['startTime']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.fetch.startTime'] + ).toBeGreaterThan(0); } else { - expect(timings['fetch']['startTime']).toBe(0); + expect(timings['_dd.resource_timings.fetch.startTime']).toBe(0); } - expect(timings['fetch']['duration']).toBeGreaterThan(0); + expect( + timings['_dd.resource_timings.fetch.duration'] + ).toBeGreaterThan(0); }); }); @@ -1183,7 +1254,7 @@ describe('XHRProxy', () => { firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder([]) }); DdRum.registerResourceEventMapper(event => { - event.context['body'] = JSON.parse( + (event.context as any)['body'] = JSON.parse( event.resourceContext?.response ); return event; @@ -1200,9 +1271,7 @@ describe('XHRProxy', () => { // THEN const attributes = DdNativeRum.stopResource.mock.calls[0][4]; - expect(attributes['body']).toEqual({ - body: 'content' - }); + expect(attributes['body.body']).toEqual('content'); }); }); @@ -1453,13 +1522,13 @@ describe('XHRProxy', () => { expect(attributes['_dd.graphql.variables']).toEqual('{}'); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER) ).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_OPERATION_NAME_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_OPERATION_NAME_HEADER) ).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_VARIABLES_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_VARIABLES_HEADER) ).not.toBeDefined(); }); @@ -1491,13 +1560,13 @@ describe('XHRProxy', () => { expect(attributes['_dd.graphql.variables']).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER) ).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_OPERATION_NAME_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_OPERATION_NAME_HEADER) ).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_VARIABLES_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_VARIABLES_HEADER) ).not.toBeDefined(); }); @@ -1530,13 +1599,13 @@ describe('XHRProxy', () => { expect(attributes['_dd.graphql.variables']).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_OPERATION_TYPE_HEADER) ).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_OPERATION_NAME_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_OPERATION_NAME_HEADER) ).not.toBeDefined(); expect( - xhr.requestHeaders[DATADOG_GRAPH_QL_VARIABLES_HEADER] + xhr.requestHeaders.get(DATADOG_GRAPH_QL_VARIABLES_HEADER) ).not.toBeDefined(); }); diff --git a/packages/core/src/sdk/DatadogInternalBridge/__tests__/DdSdkNativeBridge.test.tsx b/packages/core/src/sdk/DatadogInternalBridge/__tests__/DdSdkNativeBridge.test.tsx index 9a20162fd..3dff4e7ce 100644 --- a/packages/core/src/sdk/DatadogInternalBridge/__tests__/DdSdkNativeBridge.test.tsx +++ b/packages/core/src/sdk/DatadogInternalBridge/__tests__/DdSdkNativeBridge.test.tsx @@ -72,7 +72,7 @@ describe('DdSdkNativeBridge', () => { afterEach(() => { jest.resetModules(); jest.resetAllMocks(); - delete global.RN$Bridgeless; + delete (global as any).RN$Bridgeless; }); describe('new architecture implementation', () => { diff --git a/packages/core/src/sdk/DatadogProvider/Buffer/__tests__/BoundedBuffer.test.ts b/packages/core/src/sdk/DatadogProvider/Buffer/__tests__/BoundedBuffer.test.ts index 3762da1e5..87fd37798 100644 --- a/packages/core/src/sdk/DatadogProvider/Buffer/__tests__/BoundedBuffer.test.ts +++ b/packages/core/src/sdk/DatadogProvider/Buffer/__tests__/BoundedBuffer.test.ts @@ -5,7 +5,7 @@ */ import { InternalLog } from '../../../../InternalLog'; -import { DdSdk } from '../../../DdSdk'; +import { NativeDdSdk } from '../../../DdSdkInternal'; import { BoundedBuffer } from '../BoundedBuffer'; describe('BoundedBuffer', () => { @@ -126,7 +126,7 @@ describe('BoundedBuffer', () => { await buffer.drain(); expect(callbackWithId).toHaveBeenCalledTimes(1); expect(callbackWithId).toHaveBeenNthCalledWith(1, 'callbackId1'); - expect(DdSdk.telemetryError).toHaveBeenCalledWith( + expect(NativeDdSdk.telemetryError).toHaveBeenCalledWith( 'Could not generate enough random numbers happened 2 times.', '', 'RandomIdGenerationError' @@ -146,7 +146,7 @@ describe('BoundedBuffer', () => { await buffer.drain(); expect(fakeCallback).toHaveBeenCalledTimes(3); - expect(DdSdk.telemetryError).toHaveBeenCalledWith( + expect(NativeDdSdk.telemetryError).toHaveBeenCalledWith( 'Buffer overflow happened 1 times.', '', 'BufferOverflow' @@ -171,7 +171,7 @@ describe('BoundedBuffer', () => { expect(fakeCallback).toHaveBeenCalledTimes(1); expect(callbackReturningId).not.toHaveBeenCalled(); expect(callbackWithId).not.toHaveBeenCalled(); - expect(DdSdk.telemetryError).toHaveBeenCalledWith( + expect(NativeDdSdk.telemetryError).toHaveBeenCalledWith( 'Buffer overflow happened 2 times.', '', 'BufferOverflow' @@ -196,7 +196,7 @@ describe('BoundedBuffer', () => { expect(fakeCallback).toHaveBeenCalledTimes(1); expect(callbackReturningId).toHaveBeenCalledTimes(1); expect(callbackWithId).toHaveBeenCalledTimes(1); - expect(DdSdk.telemetryError).not.toHaveBeenCalled(); + expect(NativeDdSdk.telemetryError).not.toHaveBeenCalled(); }); }); }); diff --git a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx index 8ace4b731..66a3206b2 100644 --- a/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx +++ b/packages/core/src/sdk/DatadogProvider/__tests__/initialization.test.tsx @@ -70,6 +70,7 @@ describe('DatadogProvider', () => { }, "appHangThreshold": undefined, "applicationId": "fakeApplicationId", + "attributeEncoders": [], "batchProcessingLevel": "MEDIUM", "batchSize": "MEDIUM", "bundleLogsWithRum": true, diff --git a/packages/core/src/sdk/EventMappers/__tests__/EventMapper.test.ts b/packages/core/src/sdk/EventMappers/__tests__/EventMapper.test.ts index 780b0a831..93333f7de 100644 --- a/packages/core/src/sdk/EventMappers/__tests__/EventMapper.test.ts +++ b/packages/core/src/sdk/EventMappers/__tests__/EventMapper.test.ts @@ -4,14 +4,14 @@ * Copyright 2016-Present Datadog, Inc. */ -import { DdSdk } from '../../DdSdk'; +import { NativeDdSdk } from '../../DdSdkInternal'; import { EventMapper } from '../EventMapper'; describe('EventMapper', () => { it('returns the original log when the event log mapper crashes', () => { const eventMapper = new EventMapper( (event: object) => { - event['badData'] = 'bad data'; + (event as { badData: string })['badData'] = 'bad data'; throw new Error('crashed'); }, (event: object) => event, @@ -26,7 +26,7 @@ describe('EventMapper', () => { ).toEqual({ someData: 'some data' }); - expect(DdSdk.telemetryDebug).toHaveBeenCalledWith( + expect(NativeDdSdk.telemetryDebug).toHaveBeenCalledWith( 'Error while running the event mapper' ); }); diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts index 1670e5fe2..2d10d390e 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts +++ b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts @@ -118,6 +118,7 @@ describe('FileBasedConfiguration', () => { "actionNameAttribute": "action-name-attr", "additionalConfiguration": {}, "applicationId": "fake-app-id", + "attributeEncoders": [], "batchProcessingLevel": "MEDIUM", "batchSize": "MEDIUM", "bundleLogsWithRum": true, @@ -180,6 +181,7 @@ describe('FileBasedConfiguration', () => { "actionNameAttribute": undefined, "additionalConfiguration": {}, "applicationId": "fake-app-id", + "attributeEncoders": [], "batchProcessingLevel": "MEDIUM", "batchSize": "MEDIUM", "bundleLogsWithRum": true, diff --git a/packages/core/src/trace/__tests__/DdTrace.test.ts b/packages/core/src/trace/__tests__/DdTrace.test.ts index a1567afef..ba7a87d26 100644 --- a/packages/core/src/trace/__tests__/DdTrace.test.ts +++ b/packages/core/src/trace/__tests__/DdTrace.test.ts @@ -52,13 +52,13 @@ describe('DdTrace', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdTrace.startSpan('operation', context); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); expect(NativeModules.DdTrace.startSpan).toHaveBeenCalledWith( @@ -104,18 +104,17 @@ describe('DdTrace', () => { }); test('uses empty context with error when context is invalid or null', async () => { - const context: any = 123; + const context: any = Symbol('invalid-context'); await DdTrace.startSpan('operation', context); const spanId = await DdTrace.startSpan('operation', {}); await DdTrace.finishSpan(spanId, context); expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, + 2, expect.anything(), - SdkVerbosity.ERROR + SdkVerbosity.WARN ); - expect(NativeModules.DdTrace.finishSpan).toHaveBeenCalledWith( spanId, {}, diff --git a/packages/core/src/utils/__tests__/argsUtils.test.ts b/packages/core/src/utils/__tests__/argsUtils.test.ts deleted file mode 100644 index 1a2f4eacb..000000000 --- a/packages/core/src/utils/__tests__/argsUtils.test.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -/* - * Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. - * This product includes software developed at Datadog (https://www.datadoghq.com/). - * Copyright 2016-Present Datadog, Inc. - */ - -import { InternalLog } from '../../InternalLog'; -import { SdkVerbosity } from '../../SdkVerbosity'; -import { validateContext } from '../argsUtils'; - -jest.mock('../../InternalLog', () => { - return { - InternalLog: { - log: jest.fn() - }, - DATADOG_MESSAGE_PREFIX: 'DATADOG:' - }; -}); - -describe('argsUtils', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - describe('validateContext', () => { - it('returns empty object if context is null', () => { - expect(validateContext(null)).toEqual({}); - expect(validateContext(undefined)).toEqual({}); - }); - - it('returns empty object with error if context is raw type', () => { - expect(validateContext('raw-type')).toEqual({}); - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.anything(), - SdkVerbosity.ERROR - ); - }); - - it('nests array inside of new object if context is an array', () => { - const context = [{ a: 1, b: 2 }, 1, true]; - const validatedContext = validateContext(context); - - expect(InternalLog.log).toHaveBeenNthCalledWith( - 1, - expect.anything(), - SdkVerbosity.WARN - ); - - expect(validatedContext).toEqual({ - context - }); - }); - - it('returns unmodified context if it is a valid object', () => { - const context = { - testA: 1, - testB: {} - }; - const validatedContext = validateContext(context); - - expect(validatedContext).toEqual(context); - }); - }); -}); diff --git a/packages/core/src/utils/__tests__/errorUtils.test.ts b/packages/core/src/utils/__tests__/errorUtils.test.ts index b9941d4b4..c89f7a2b7 100644 --- a/packages/core/src/utils/__tests__/errorUtils.test.ts +++ b/packages/core/src/utils/__tests__/errorUtils.test.ts @@ -4,7 +4,7 @@ * Copyright 2016-Present Datadog, Inc. */ -import { getErrorName } from '../errorUtils'; +import { getErrorName } from '../../sdk/AttributesEncoding/errorUtils'; describe('errorUtils', () => { describe('getErrorName', () => { From db66958ef7b79750fab1846aecc49cf290f98c29 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Fri, 10 Oct 2025 11:36:37 +0200 Subject: [PATCH 078/168] Minor warning fixes in tests --- .../rum/instrumentation/DdRumErrorTracking.test.tsx | 6 +++--- .../requestProxy/XHRProxy/__tests__/XHRProxy.test.ts | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx b/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx index cfa6b9014..cfc8bfbb8 100644 --- a/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx +++ b/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx @@ -137,7 +137,7 @@ it('M intercept and send a RUM event W onGlobalError() {Error object}', async () expect.any(Number), '' ); - expect(DdRum.addError.mock.calls[0][2]).toContain( + expect((DdRum.addError as any).mock.calls[0][2]).toContain( '/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx' ); expect(baseErrorHandlerCalled).toStrictEqual(true); @@ -174,7 +174,7 @@ it('M intercept and send a RUM event W onGlobalError() {CustomError object}', as expect.any(Number), '' ); - expect(DdRum.addError.mock.calls[0][2]).toContain( + expect((DdRum.addError as any).mock.calls[0][2]).toContain( '/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx' ); expect(baseErrorHandlerCalled).toStrictEqual(true); @@ -624,7 +624,7 @@ it('M intercept and send a RUM event W on error() {called from RNErrorHandler}', expect.any(Number), '' ); - expect(DdRum.addError.mock.calls[0][2]).toContain( + expect((DdRum.addError as any).mock.calls[0][2]).toContain( '/packages/core/src/__tests__/rum/instrumentation/DdRumErrorTracking.test.tsx' ); expect(baseErrorHandlerCalled).toStrictEqual(true); diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index 6599dea08..6e944b6c3 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -1618,16 +1618,16 @@ describe('XHRProxy', () => { firstPartyHostsRegexMap: firstPartyHostsRegexMapBuilder([]) }); DdRum.registerResourceEventMapper(event => { - if (event.context['_dd.graphql.variables']) { + if ((event.context as any)['_dd.graphql.variables']) { const variables = JSON.parse( - event.context['_dd.graphql.variables'] + (event.context as any)['_dd.graphql.variables'] ); if (variables.password) { variables.password = '***'; } - event.context['_dd.graphql.variables'] = JSON.stringify( - variables - ); + (event.context as any)[ + '_dd.graphql.variables' + ] = JSON.stringify(variables); } return event; From cbe52606c3278603e88c0127ac993c9135779057 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Mon, 13 Oct 2025 17:50:06 +0200 Subject: [PATCH 079/168] Changed .tsx to .ts + removed left-over comment --- .../{attributesEncoding.tsx => attributesEncoding.ts} | 0 .../{defaultEncoders.tsx => defaultEncoders.ts} | 10 ---------- .../{errorUtils.tsx => errorUtils.ts} | 0 .../sdk/AttributesEncoding/{helpers.tsx => helpers.ts} | 0 .../src/sdk/AttributesEncoding/{types.tsx => types.ts} | 0 .../src/sdk/AttributesEncoding/{utils.tsx => utils.ts} | 0 packages/core/src/sdk/{DdSdk.tsx => DdSdk.ts} | 0 .../src/sdk/{DdSdkInternal.tsx => DdSdkInternal.ts} | 0 8 files changed, 10 deletions(-) rename packages/core/src/sdk/AttributesEncoding/{attributesEncoding.tsx => attributesEncoding.ts} (100%) rename packages/core/src/sdk/AttributesEncoding/{defaultEncoders.tsx => defaultEncoders.ts} (95%) rename packages/core/src/sdk/AttributesEncoding/{errorUtils.tsx => errorUtils.ts} (100%) rename packages/core/src/sdk/AttributesEncoding/{helpers.tsx => helpers.ts} (100%) rename packages/core/src/sdk/AttributesEncoding/{types.tsx => types.ts} (100%) rename packages/core/src/sdk/AttributesEncoding/{utils.tsx => utils.ts} (100%) rename packages/core/src/sdk/{DdSdk.tsx => DdSdk.ts} (100%) rename packages/core/src/sdk/{DdSdkInternal.tsx => DdSdkInternal.ts} (100%) diff --git a/packages/core/src/sdk/AttributesEncoding/attributesEncoding.tsx b/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts similarity index 100% rename from packages/core/src/sdk/AttributesEncoding/attributesEncoding.tsx rename to packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts diff --git a/packages/core/src/sdk/AttributesEncoding/defaultEncoders.tsx b/packages/core/src/sdk/AttributesEncoding/defaultEncoders.ts similarity index 95% rename from packages/core/src/sdk/AttributesEncoding/defaultEncoders.tsx rename to packages/core/src/sdk/AttributesEncoding/defaultEncoders.ts index fdee006f1..420b629ec 100644 --- a/packages/core/src/sdk/AttributesEncoding/defaultEncoders.tsx +++ b/packages/core/src/sdk/AttributesEncoding/defaultEncoders.ts @@ -60,17 +60,7 @@ export const dateEncoder: AttributeEncoder = { check: (v: unknown): v is Date => v instanceof Date, encode: (d: Date) => String(d) }; -/* - } else if ('componentStack' in error) { - stack = String(error.componentStack); - } else if ( - 'sourceURL' in error && - 'line' in error && - 'column' in error - ) { - -*/ /** * Extended Error Encoder. * Serializes name, message, stack, and cause (ES2022+) for Error objects. diff --git a/packages/core/src/sdk/AttributesEncoding/errorUtils.tsx b/packages/core/src/sdk/AttributesEncoding/errorUtils.ts similarity index 100% rename from packages/core/src/sdk/AttributesEncoding/errorUtils.tsx rename to packages/core/src/sdk/AttributesEncoding/errorUtils.ts diff --git a/packages/core/src/sdk/AttributesEncoding/helpers.tsx b/packages/core/src/sdk/AttributesEncoding/helpers.ts similarity index 100% rename from packages/core/src/sdk/AttributesEncoding/helpers.tsx rename to packages/core/src/sdk/AttributesEncoding/helpers.ts diff --git a/packages/core/src/sdk/AttributesEncoding/types.tsx b/packages/core/src/sdk/AttributesEncoding/types.ts similarity index 100% rename from packages/core/src/sdk/AttributesEncoding/types.tsx rename to packages/core/src/sdk/AttributesEncoding/types.ts diff --git a/packages/core/src/sdk/AttributesEncoding/utils.tsx b/packages/core/src/sdk/AttributesEncoding/utils.ts similarity index 100% rename from packages/core/src/sdk/AttributesEncoding/utils.tsx rename to packages/core/src/sdk/AttributesEncoding/utils.ts diff --git a/packages/core/src/sdk/DdSdk.tsx b/packages/core/src/sdk/DdSdk.ts similarity index 100% rename from packages/core/src/sdk/DdSdk.tsx rename to packages/core/src/sdk/DdSdk.ts diff --git a/packages/core/src/sdk/DdSdkInternal.tsx b/packages/core/src/sdk/DdSdkInternal.ts similarity index 100% rename from packages/core/src/sdk/DdSdkInternal.tsx rename to packages/core/src/sdk/DdSdkInternal.ts From 277cb224005554b988db35ed48c1e544e6ac2a4e Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 21 Oct 2025 15:56:24 +0200 Subject: [PATCH 080/168] Limit encoded attributes to 128 --- .../__tests__/attributesEncoding.test.ts | 31 ++++++++++ .../AttributesEncoding/attributesEncoding.ts | 8 +-- .../src/sdk/AttributesEncoding/helpers.ts | 62 ++++++++++++++++--- 3 files changed, 87 insertions(+), 14 deletions(-) diff --git a/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts b/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts index 9cd2718ff..759aeefd6 100644 --- a/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts +++ b/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts @@ -263,4 +263,35 @@ describe('encodeAttributes', () => { ]) ); }); + + it('drops attributes after reaching the 128 limit and warns once', () => { + // Prepare 200 simple attributes — max=128 + const input: Record = {}; + for (let i = 0; i < 200; i++) { + input[`key${i}`] = i; + } + + const result = encodeAttributes(input); + + // Check that only 128 attributes remain + expect(Object.keys(result)).toHaveLength(128); + + // Check the first ones are preserved + expect(result).toHaveProperty('key0', 0); + expect(result).toHaveProperty('key127', 127); + + // Check later ones were dropped + expect(result).not.toHaveProperty('key128'); + + // Check that a warning was shown at least once + expect(warn).toHaveBeenCalledWith( + expect.stringContaining('Attribute limit') + ); + + // Check there is only one "limit reached" warning (even if multiple attributes were dropped) + const limitWarnings = (warn as jest.Mock).mock.calls.filter(([msg]) => + msg.includes('Attribute limit') + ); + expect(limitWarnings).toHaveLength(1); + }); }); diff --git a/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts b/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts index 0f05d7e11..cd2103f74 100644 --- a/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts +++ b/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts @@ -7,7 +7,7 @@ import { DdSdk } from '../DdSdk'; import { builtInEncoders } from './defaultEncoders'; -import { encodeAttributesInPlace } from './helpers'; +import { encodeAttributesInPlace, type EncodeContext } from './helpers'; import type { Encodable } from './types'; import { isPlainObject, warn } from './utils'; @@ -23,14 +23,14 @@ import { isPlainObject, warn } from './utils'; export function encodeAttributes(input: unknown): Record { const result: Record = {}; const allEncoders = [...DdSdk.attributeEncoders, ...builtInEncoders]; - + const context: EncodeContext = { numOfAttributes: 0 }; if (isPlainObject(input)) { for (const [k, v] of Object.entries(input)) { - encodeAttributesInPlace(v, result, [k], allEncoders); + encodeAttributesInPlace(v, result, [k], allEncoders, context); } } else { // Fallback for primitive values passed as root - encodeAttributesInPlace(input, result, ['context'], allEncoders); + encodeAttributesInPlace(input, result, ['context'], allEncoders, context); warn( 'Warning: attributes root should be an object.\n' + 'Received a primitive/array instead, which will be wrapped under the "context" key.' diff --git a/packages/core/src/sdk/AttributesEncoding/helpers.ts b/packages/core/src/sdk/AttributesEncoding/helpers.ts index 3636be9e1..1fc80dece 100644 --- a/packages/core/src/sdk/AttributesEncoding/helpers.ts +++ b/packages/core/src/sdk/AttributesEncoding/helpers.ts @@ -7,6 +7,13 @@ import type { AttributeEncoder, Encodable } from './types'; import { formatPathForLog, isPlainObject, warn } from './utils'; +const MAX_ATTRIBUTES = 128; + +export interface EncodeContext { + numOfAttributes: number; + limitReachedWarned?: boolean; +} + /** * Recursive in-place encoder: flattens values into `out` dictionary. * Never applies "context", that's only for the root. @@ -15,7 +22,8 @@ export function encodeAttributesInPlace( input: unknown, out: Record, path: string[], - encoders: AttributeEncoder[] + encoders: AttributeEncoder[], + context: EncodeContext = { numOfAttributes: 0 } ): void { const value = applyEncoders(input, encoders); @@ -27,7 +35,7 @@ export function encodeAttributesInPlace( typeof value === 'number' || typeof value === 'boolean' ) { - out[path.join('.')] = value; + addEncodedAttribute(out, path, value, context); return; } @@ -49,7 +57,7 @@ export function encodeAttributesInPlace( if (isPlainObject(v)) { const nested: Record = {}; - encodeAttributesInPlace(v, nested, [], encoders); + encodeAttributesInPlace(v, nested, [], encoders, context); return nested; } @@ -66,16 +74,20 @@ export function encodeAttributesInPlace( return undefined; }; - out[path.join('.')] = value - .map(normalize) - .filter(item => item !== undefined); // drop unsupported + addEncodedAttribute( + out, + path, + value.map(normalize).filter(item => item !== undefined), + context + ); + return; } // Plain object if (isPlainObject(value)) { for (const [k, v] of Object.entries(value)) { - encodeAttributesInPlace(v, out, [...path, k], encoders); + encodeAttributesInPlace(v, out, [...path, k], encoders, context); } return; } @@ -94,7 +106,8 @@ export function encodeAttributesInPlace( */ export function sanitizeForJson( value: unknown, - encoders: AttributeEncoder[] + encoders: AttributeEncoder[], + context: EncodeContext = { numOfAttributes: 0 } ): Encodable { const v = applyEncoders(value, encoders); @@ -102,14 +115,14 @@ export function sanitizeForJson( if (isPlainObject(v)) { const out: Record = {}; for (const [k, val] of Object.entries(v)) { - encodeAttributesInPlace(val, out, [k], encoders); + encodeAttributesInPlace(val, out, [k], encoders, context); } return out; } // If array, sanitize items if (Array.isArray(v)) { - return v.map(item => sanitizeForJson(item, encoders)); + return v.map(item => sanitizeForJson(item, encoders, context)); } return v; @@ -132,3 +145,32 @@ export function applyEncoders( // Not matched by any encoder; leave as-is for the visitor to decide return value as Encodable; } + +function addEncodedAttribute( + out: Record, + path: string[], + value: Encodable, + context: EncodeContext +): void { + if (context.numOfAttributes >= MAX_ATTRIBUTES) { + // Only warn once to avoid log spam + if (!context.limitReachedWarned) { + warn( + `Attribute limit of ${MAX_ATTRIBUTES} reached; further attributes will be dropped.` + ); + context.limitReachedWarned = true; + } + + // Optional: warn for specific dropped attribute (if desired) + warn( + `Dropped attribute at '${formatPathForLog( + path + )}' because limit of ${MAX_ATTRIBUTES} attributes was reached. All further attributes will be dropped.` + ); + + return; + } + + out[path.join('.')] = value; + context.numOfAttributes++; +} From 1548a136666067ad6f9c01bb6a2120e0c0cf9065 Mon Sep 17 00:00:00 2001 From: Marco Saia Date: Tue, 21 Oct 2025 16:07:32 +0200 Subject: [PATCH 081/168] Add tests to ensure attributes are encoded by copy --- .../__tests__/attributesEncoding.test.ts | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts b/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts index 759aeefd6..b679ee363 100644 --- a/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts +++ b/packages/core/src/sdk/AttributesEncoding/__tests__/attributesEncoding.test.ts @@ -130,6 +130,122 @@ describe('encodeAttributes', () => { expect(result).toEqual({ valid: 'ok' }); }); + it('does not modify original object when dropping values', () => { + const input = { valid: 'ok', bad: () => {} }; + const result = encodeAttributes(input); + expect(result).toEqual({ valid: 'ok' }); + expect(input).toHaveProperty('bad'); + }); + + it('does not modify original object when dropping nested invalid values', () => { + const input = { user: { profile: { bad: () => {}, good: 'ok' } } }; + const userBefore = { ...input.user }; + const profileBefore = { ...input.user.profile }; + + const result = encodeAttributes(input); + + // Encoder should flatten and drop invalid function + expect(result).toEqual({ 'user.profile.good': 'ok' }); + + // Verify that the original objects were not mutated or replaced + expect(input.user).toEqual(userBefore); + expect(input.user.profile).toEqual(profileBefore); + }); + + it('does not modify original array inside object', () => { + const arr = [1, 2, () => {}]; + const input = { data: arr }; + const arrBefore = [...arr]; + + const result = encodeAttributes(input); + expect(result).toEqual({ data: [1, 2] }); // dropped the function + expect(input.data).toEqual(arrBefore); // original array untouched + }); + + it('does not modify original nested arrays of objects', () => { + const objA = { val: 1 }; + const objB = { bad: () => {} }; + const objC = { val: 2 }; + + const input = { + matrix: [[objA, objB], [objC]] + }; + + // capture snapshots + const matrixBefore = input.matrix; + const row0Before = input.matrix[0]; + const row1Before = input.matrix[1]; + const objA_before = { ...objA }; + const objB_before = { ...objB }; + const objC_before = { ...objC }; + + const result = encodeAttributes(input); + + expect(result).toEqual({ + matrix: [ + [{ val: 1 }, {}], // objB sanitized + [{ val: 2 }] + ] + }); + + // check original references untouched + expect(input.matrix).toBe(matrixBefore); // same outer array reference + expect(input.matrix[0]).toBe(row0Before); // same row0 reference + expect(input.matrix[1]).toBe(row1Before); // same row1 reference + expect(objA).toEqual(objA_before); // object A unchanged + expect(objB).toEqual(objB_before); // object B unchanged + expect(objC).toEqual(objC_before); // object C unchanged + }); + + it('does not modify original Map when encoding', () => { + const innerMap = new Map([['x', 1]]); + const outerMap = new Map([['inner', innerMap]]); + const input = { outer: outerMap }; + + const snapshot = new Map(outerMap); + const innerSnapshot = new Map(innerMap); + + const result = encodeAttributes(input); + expect(result.outer).toBeInstanceOf(Array); + expect(input.outer).toBe(outerMap); // same reference + expect(Array.from(input.outer.entries())).toEqual( + Array.from(snapshot.entries()) + ); + expect(Array.from(innerMap.entries())).toEqual( + Array.from(innerSnapshot.entries()) + ); + }); + + it('does not modify original object when attribute limit is reached', () => { + const input: Record = {}; + for (let i = 0; i < 200; i++) { + input[`k${i}`] = `v${i}`; + } + const snapshot = { ...input }; + + const result = encodeAttributes(input); + expect(Object.keys(result)).toHaveLength(128); + expect(input).toEqual(snapshot); // original still has 200 keys + }); + + it('does not modify original when sanitizing arrays of objects', () => { + const obj1 = { ok: true }; + const obj2 = { bad: () => {} }; + const input = [obj1, obj2]; + + // Capture pre-encode snapshots manually + const obj1Before = { ...obj1 }; + const obj2Before = { ...obj2 }; + const arrayBefore = [...input]; + + const result = encodeAttributes(input); + + expect(result).toEqual({ context: [{ ok: true }, {}] }); + expect(input).toEqual(arrayBefore); + expect(input[0]).toEqual(obj1Before); + expect(input[1]).toEqual(obj2Before); + }); + it('handles deeply nested objects', () => { const deep = { level1: { level2: { level3: { value: 42 } } } }; const result = encodeAttributes(deep); From 06b43d991c85dd2f8247c6ee0cfaad49c8ca5c44 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 28 Nov 2025 12:35:19 +0100 Subject: [PATCH 082/168] Missing attributes on FileBasedConfiguration and linting --- .../XHRProxy/__tests__/XHRProxy.test.ts | 15 ++++++----- .../AttributesEncoding/attributesEncoding.ts | 11 ++++++-- packages/core/src/sdk/DdSdkInternal.ts | 26 ++++++++++++++++--- .../__tests__/FileBasedConfiguration.test.ts | 1 + 4 files changed, 41 insertions(+), 12 deletions(-) diff --git a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts index 6e944b6c3..367d48553 100644 --- a/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts +++ b/packages/core/src/rum/instrumentation/resourceTracking/requestProxy/XHRProxy/__tests__/XHRProxy.test.ts @@ -931,7 +931,7 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).toBeUndefined(); + expect(xhr.requestHeaders.get(BAGGAGE_HEADER_KEY)).toBeUndefined(); }); it('rum session id does not overwrite existing baggage headers', async () => { @@ -970,14 +970,17 @@ describe('XHRProxy', () => { await flushPromises(); // THEN - expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).not.toBeUndefined(); - expect(xhr.requestHeaders[BAGGAGE_HEADER_KEY]).toContain( + expect( + xhr.requestHeaders.get(BAGGAGE_HEADER_KEY) + ).not.toBeUndefined(); + expect(xhr.requestHeaders.get(BAGGAGE_HEADER_KEY)).toContain( 'existing.key=existing-value' ); - const values = xhr.requestHeaders[BAGGAGE_HEADER_KEY].split( - ',' - ).sort(); + const values = xhr.requestHeaders + .get(BAGGAGE_HEADER_KEY) + ?.split(',') + .sort(); expect(values[0]).toBe('existing.key=existing-value'); expect(values[1]).toBe('session.id=TEST-SESSION-ID'); diff --git a/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts b/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts index cd2103f74..aa35d5fca 100644 --- a/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts +++ b/packages/core/src/sdk/AttributesEncoding/attributesEncoding.ts @@ -7,7 +7,8 @@ import { DdSdk } from '../DdSdk'; import { builtInEncoders } from './defaultEncoders'; -import { encodeAttributesInPlace, type EncodeContext } from './helpers'; +import type { EncodeContext } from './helpers'; +import { encodeAttributesInPlace } from './helpers'; import type { Encodable } from './types'; import { isPlainObject, warn } from './utils'; @@ -30,7 +31,13 @@ export function encodeAttributes(input: unknown): Record { } } else { // Fallback for primitive values passed as root - encodeAttributesInPlace(input, result, ['context'], allEncoders, context); + encodeAttributesInPlace( + input, + result, + ['context'], + allEncoders, + context + ); warn( 'Warning: attributes root should be an object.\n' + 'Received a primitive/array instead, which will be wrapped under the "context" key.' diff --git a/packages/core/src/sdk/DdSdkInternal.ts b/packages/core/src/sdk/DdSdkInternal.ts index 7af33d2e3..78df1217a 100644 --- a/packages/core/src/sdk/DdSdkInternal.ts +++ b/packages/core/src/sdk/DdSdkInternal.ts @@ -39,10 +39,6 @@ export class DdSdkWrapper implements DdNativeSdkType { return NativeDdSdk.getConstants(); } - setAttributes(attributes: object): Promise { - return NativeDdSdk.setAttributes(attributes); - } - setUserInfo(user: object): Promise { return NativeDdSdk.setUserInfo(user); } @@ -55,6 +51,28 @@ export class DdSdkWrapper implements DdNativeSdkType { return NativeDdSdk.addUserExtraInfo(extraInfo); } + addAttribute(key: string, value: object): Promise { + return NativeDdSdk.addAttribute(key, value); + } + removeAttribute(key: string): Promise { + return NativeDdSdk.removeAttribute(key); + } + addAttributes(attributes: object): Promise { + return NativeDdSdk.addAttributes(attributes); + } + removeAttributes(keys: string[]): Promise { + return NativeDdSdk.removeAttributes(keys); + } + setAccountInfo(account: object): Promise { + return NativeDdSdk.setAccountInfo(account); + } + clearAccountInfo(): Promise { + return NativeDdSdk.clearAccountInfo(); + } + addAccountExtraInfo(extraInfo: object): Promise { + return NativeDdSdk.addAccountExtraInfo(extraInfo); + } + setTrackingConsent(trackingConsent: string): Promise { return NativeDdSdk.setTrackingConsent(trackingConsent); } diff --git a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts index 2d10d390e..e1fe5e511 100644 --- a/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts +++ b/packages/core/src/sdk/FileBasedConfiguration/__tests__/FileBasedConfiguration.test.ts @@ -27,6 +27,7 @@ describe('FileBasedConfiguration', () => { "actionNameAttribute": "action-name-attr", "additionalConfiguration": {}, "applicationId": "fake-app-id", + "attributeEncoders": [], "batchProcessingLevel": "MEDIUM", "batchSize": "MEDIUM", "bundleLogsWithRum": true, From c355340d27ada62717d79fc11b2b689512abc0b7 Mon Sep 17 00:00:00 2001 From: Sergio Barrio Date: Fri, 28 Nov 2025 16:25:22 +0100 Subject: [PATCH 083/168] Add NavigationTrackingOptions to React Navigation tracking --- example/app.json | 2 +- example/src/App.tsx | 34 ++++- .../NestedNavigator/ScreenWithLinks.tsx | 4 +- .../DdRumReactNavigationTracking.test.tsx | 134 +++++++++++++++++- packages/react-navigation/src/index.tsx | 14 +- .../DdRumReactNavigationTracking.tsx | 65 +++++++-- 6 files changed, 234 insertions(+), 19 deletions(-) diff --git a/example/app.json b/example/app.json index f0f0c83d7..70b08eb89 100644 --- a/example/app.json +++ b/example/app.json @@ -1,5 +1,5 @@ { "name": "DdSdkReactNativeExample", "displayName": "DD RN Sample", - "navigation": "react-native-navigation" + "navigation": "react-navigation" } diff --git a/example/src/App.tsx b/example/src/App.tsx index c29982a97..831d68fd6 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -12,13 +12,41 @@ import { Route } from "@react-navigation/native"; import { NestedNavigator } from './screens/NestedNavigator/NestedNavigator'; import { getDatadogConfig, onDatadogInitialization } from './ddUtils'; import { TrackingConsent } from '@datadog/mobile-react-native'; +import { NavigationTrackingOptions, ParamsTrackingPredicate, ViewTrackingPredicate } from '@datadog/mobile-react-navigation/src/rum/instrumentation/DdRumReactNavigationTracking'; const Tab = createBottomTabNavigator(); -const viewPredicate: ViewNamePredicate = function customViewNamePredicate(route: Route, trackedName: string) { +// === Navigation Tracking custom predicates +const viewNamePredicate: ViewNamePredicate = function customViewNamePredicate(route: Route, trackedName: string) { return "Custom RN " + trackedName; } +const viewTrackingPredicate: ViewTrackingPredicate = function customViewTrackingPredicate(route: Route) { + if (route.name === "AlertModal") { + return false; + } + + return true; +} + +const paramsTrackingPredicate: ParamsTrackingPredicate = function customParamsTrackingPredicate(route: Route) { + const filteredParams: any = {}; + if (route.params?.creditCardNumber) { + filteredParams["creditCardNumber"] = "XXXX XXXX XXXX XXXX"; + } + + if (route.params?.username) { + filteredParams["username"] = route.params.username; + } + + return filteredParams; +} + +const navigationTrackingOptions: NavigationTrackingOptions = { + viewNamePredicate, + viewTrackingPredicate, + paramsTrackingPredicate, +} // === Datadog Provider Configuration schemes === // 1.- Direct configuration @@ -39,7 +67,9 @@ export default function App() { return ( { - DdRumReactNavigationTracking.startTrackingViews(navigationRef.current, viewPredicate) + DdRumReactNavigationTracking.startTrackingViews( + navigationRef.current, + navigationTrackingOptions) }}> { const {navigate} = useNavigation() return (<> - {props.links.map(link =>